'Main', 'epg_url' => $existing['epg_url'], 'm3u_url' => $existing['m3u_url'] ?? '', ]]; $existing['allow_personal_epg'] = false; unset($existing['epg_url'], $existing['m3u_url']); file_put_contents($config_path, json_encode($existing, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } // Bootstrap an admin key for older configs that do not have one yet. if (empty($existing['admin_key'])) { $existing['admin_key'] = bin2hex(random_bytes(16)); file_put_contents($config_path, json_encode($existing, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } } $is_first_setup = !file_exists($config_path); $existing = []; $corrupt_config = false; if (!$is_first_setup) { $existing = json_decode(file_get_contents($config_path), true) ?? []; if (!is_array($existing)) { $existing = []; $corrupt_config = true; $is_first_setup = true; } } $admin_key = $existing['admin_key'] ?? null; $mode = 'setup'; $error = ''; $new_key = ''; if (!$is_first_setup) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['admin_key_input'])) { if (trim($_POST['admin_key_input']) === $admin_key) { $mode = 'setup'; } else { $mode = 'lock'; $error = 'Invalid admin key.'; } } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['group_name'])) { if (trim($_POST['admin_key_hidden'] ?? '') !== $admin_key) { $mode = 'lock'; $error = 'Invalid admin key.'; } else { $mode = 'setup'; } } else { $mode = 'lock'; } } if ($mode === 'setup' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['group_name'])) { $group_name = trim($_POST['group_name'] ?? ''); $allow_personal_epg = isset($_POST['allow_personal_epg']); $names = $_POST['source_name'] ?? []; $epg_urls = $_POST['source_epg_url'] ?? []; $m3u_urls = $_POST['source_m3u_url'] ?? []; $epg_sources = []; foreach ($names as $i => $name) { $epg_url = trim($epg_urls[$i] ?? ''); $m3u_url = trim($m3u_urls[$i] ?? ''); if (empty($epg_url)) continue; if (!filter_var($epg_url, FILTER_VALIDATE_URL)) { $error = "Invalid EPG URL for source \"$name\"."; break; } if (!empty($m3u_url) && !filter_var($m3u_url, FILTER_VALIDATE_URL)) { $error = "Invalid M3U URL for source \"$name\"."; break; } $epg_sources[] = [ 'name' => trim($name) ?: 'Source ' . ($i + 1), 'epg_url' => $epg_url, 'm3u_url' => $m3u_url, ]; } if (empty($error)) { if (empty($group_name)) { $error = 'Group name is required.'; } elseif (empty($epg_sources)) { $error = 'At least one EPG source is required.'; } else { $new_key = $is_first_setup ? strtoupper(bin2hex(random_bytes(8))) : $admin_key; $config = [ 'group_name' => $group_name, 'epg_sources' => $epg_sources, 'allow_personal_epg' => $allow_personal_epg, 'admin_key' => $new_key, ]; $written = file_put_contents($config_path, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); if ($written === false) { $error = 'Cannot write config.json. Check permissions (chmod 775 .).'; } else { $mode = 'success'; } } } } $prefill_group = $_POST['group_name'] ?? $existing['group_name'] ?? ''; $prefill_sources = $existing['epg_sources'] ?? []; $prefill_epg = !empty($existing['allow_personal_epg']); $submitted_key = trim($_POST['admin_key_input'] ?? $_POST['admin_key_hidden'] ?? ''); ?> GridTV — Setup
Enter your admin key to access the configuration.
🔒
Configuration locked
GridTV is already configured.
Enter your admin key to edit the settings.
Configuration saved successfully.
✓ Settings saved to config.json.
Your admin key
Save this key now. It will not be shown again.
You will need it to access this setup page in the future.
Your admin key is unchanged.
Open GridTV →
Displayed top-left in the topbar.
EPG Sources
'Main','epg_url'=>'','m3u_url'=>'']]; foreach ($sources_to_show as $i => $src): ?>
SOURCE
Used by the built-in player to match channels to streams.
Options
Allow personal EPG Visitors can use your instance with their own EPG/M3U URLs.
Note: Configuration is stored in config.json on the server. This file is excluded from the Git repository (.gitignore).