From ed6892a8ae2a6444b72874e3786d248a88d479ef Mon Sep 17 00:00:00 2001 From: Johnnybegood90 Date: Wed, 11 Mar 2026 18:19:24 +0100 Subject: [PATCH] Correction du player. --- index.php | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- proxy.php | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 proxy.php diff --git a/index.php b/index.php index fb97ee1..176a5cb 100644 --- a/index.php +++ b/index.php @@ -631,6 +631,86 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? ""); #navBtns { display: none !important; } } + + /* ═══════════════════════════════════════════ + PLAYER PiP + ═══════════════════════════════════════════ */ + .pip { + position: fixed; + bottom: 24px; + right: 24px; + width: 380px; + background: #000; + border: 1px solid var(--border-bright); + box-shadow: 0 8px 32px rgba(0,0,0,0.8); + z-index: 2000; + display: none; + flex-direction: column; + } + .pip.visible { display: flex; } + .pip-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 10px; + background: var(--surface); + border-bottom: 1px solid var(--border); + gap: 8px; + flex-shrink: 0; + } + .pip-channel { + font-family: var(--font-mono); + font-size: 11px; + color: var(--accent); + letter-spacing: 0.08em; + text-transform: uppercase; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + } + .pip-program { + font-size: 11px; + color: var(--text-dim); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 2; + text-align: center; + } + .pip-close { + background: none; + border: none; + color: var(--text-dim); + font-size: 18px; + cursor: pointer; + padding: 0 4px; + line-height: 1; + flex-shrink: 0; + transition: color 0.15s; + } + .pip-close:hover { color: var(--accent-live); } + .pip video { + width: 100%; + aspect-ratio: 16/9; + display: block; + background: #000; + } + .pip-error { + padding: 20px; + text-align: center; + font-family: var(--font-mono); + font-size: 11px; + color: var(--accent-live); + letter-spacing: 0.06em; + } + .channel-cell { cursor: pointer; } + .channel-cell:hover { background: var(--surface2); } + .program-block.is-live { cursor: pointer; } + @media (max-width: 600px) { + .pip { width: calc(100vw - 16px); bottom: 8px; right: 8px; left: 8px; } + } + @@ -1125,12 +1205,21 @@ function parseM3U(text) { function getStreamUrl(channelName) { const slug = slugify(channelName); - if (m3uStreams[slug]) return m3uStreams[slug]; - // Fallback : chercher une clé qui contient le slug ou l'inverse - for (const [key, url] of Object.entries(m3uStreams)) { - if (key.includes(slug) || slug.includes(key)) return url; + let url = null; + if (m3uStreams[slug]) { + url = m3uStreams[slug]; + } else { + // Fallback : chercher une clé qui contient le slug ou l'inverse + for (const [key, u] of Object.entries(m3uStreams)) { + if (key.includes(slug) || slug.includes(key)) { url = u; break; } + } } - return null; + if (!url) return null; + // Passer par proxy.php pour éviter le mixed content HTTP/HTTPS + if (url.startsWith('http://')) { + return 'proxy.php?url=' + encodeURIComponent(url); + } + return url; } // ── PLAYER PiP ──────────────────────────────────────────────────────────────── diff --git a/proxy.php b/proxy.php new file mode 100644 index 0000000..c36eed0 --- /dev/null +++ b/proxy.php @@ -0,0 +1,84 @@ + [ + 'method' => 'GET', + 'timeout' => 15, + 'header' => "User-Agent: GridTV-Proxy/1.0\r\nAccept: */*", + 'follow_location' => 1, +]]; +$body = @file_get_contents($url, false, stream_context_create($opts)); + +if ($body === false) { + http_response_code(502); die('Cannot reach upstream'); +} + +// Détecter si c'est une playlist M3U8 +$is_m3u8 = stripos($url, '.m3u8') !== false + || strpos($body, '#EXTM3U') === 0 + || strpos($body, '#EXT-X-') !== false; + +header('Access-Control-Allow-Origin: *'); +header('Cache-Control: no-cache'); + +if ($is_m3u8) { + header('Content-Type: application/vnd.apple.mpegurl'); + // Réécrire chaque URL de segment/playlist pour passer par ce proxy + $proxy_base = (isset($_SERVER['HTTPS']) ? 'https' : 'http') + . '://' . $_SERVER['HTTP_HOST'] + . strtok($_SERVER['REQUEST_URI'], '?') + . '?url='; + + $lines = explode("\n", $body); + $out = []; + foreach ($lines as $line) { + $line = rtrim($line); + if ($line === '' || $line[0] === '#') { + // Réécrire les URIs dans les tags comme #EXT-X-MAP:URI="..." + $line = preg_replace_callback('/URI="([^"]+)"/', function($m) use ($base, $proxy_base) { + $seg_url = strpos($m[1], 'http') === 0 ? $m[1] : $base . $m[1]; + return 'URI="' . $proxy_base . urlencode($seg_url) . '"'; + }, $line); + $out[] = $line; + } else { + // Ligne d'URL : segment .ts ou sous-playlist .m3u8 + $seg_url = strpos($line, 'http') === 0 ? $line : $base . $line; + $out[] = $proxy_base . urlencode($seg_url); + } + } + echo implode("\n", $out); +} else { + // Segment binaire (.ts, .aac, etc.) — streamer direct + // Récupérer le Content-Type depuis les headers de réponse + $ct = 'video/MP2T'; + foreach ($http_response_header ?? [] as $h) { + if (preg_match('/^Content-Type:\s*(.+)/i', $h, $m)) { + $ct = trim($m[1]); break; + } + } + header('Content-Type: ' . $ct); + echo $body; +}