diff --git a/index.php b/index.php index a962074..d98b71b 100644 --- a/index.php +++ b/index.php @@ -652,6 +652,67 @@ $m3u_url = htmlspecialchars($first_source['m3u_url'] ?? ''); #navBtns { display: none !important; } } + + + /* ── MODAL PERSONAL EPG ─────────────────────────────────────────────────── */ + .modal-overlay { + position: fixed; inset: 0; background: rgba(0,0,0,0.7); + z-index: 3000; display: none; align-items: center; justify-content: center; + } + .modal-overlay.visible { display: flex; } + .modal-card { + background: var(--surface); border: 1px solid var(--border-bright); + padding: 28px; width: 100%; max-width: 420px; margin: 16px; + } + .modal-title { + font-family: var(--font-mono); font-size: 13px; color: var(--accent); + letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 6px; + } + .modal-subtitle { font-size: 12px; color: var(--text-dim); margin-bottom: 20px; line-height: 1.5; } + .modal-field { margin-bottom: 14px; } + .modal-field label { + display: block; font-family: var(--font-mono); font-size: 10px; + letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; + } + .modal-field input { + width: 100%; background: var(--surface2); border: 1px solid var(--border-bright); + color: var(--text-bright); font-family: var(--font-mono); font-size: 12px; + padding: 9px 10px; outline: none; transition: border-color 0.15s; + } + .modal-field input:focus { border-color: var(--accent); } + .modal-field input::placeholder { color: var(--text-dim); opacity: 0.4; } + .modal-actions { display: flex; gap: 10px; margin-top: 20px; justify-content: flex-end; } + .modal-btn-cancel { + background: none; border: 1px solid var(--border-bright); color: var(--text-dim); + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; + text-transform: uppercase; padding: 8px 16px; cursor: pointer; transition: border-color 0.15s; + } + .modal-btn-cancel:hover { border-color: var(--text-dim); color: var(--text); } + .modal-btn-apply { + background: var(--accent); border: none; color: #000; + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; + text-transform: uppercase; padding: 8px 20px; cursor: pointer; transition: opacity 0.15s; + } + .modal-btn-apply:hover { opacity: 0.85; } + + /* ── SOURCE SELECTOR ─────────────────────────────────────────────────────── */ + .source-select { + background: var(--surface2); + border: 1px solid var(--border-bright); + color: var(--text); + font-family: var(--font-mono); + font-size: 11px; + padding: 5px 8px; + cursor: pointer; + letter-spacing: 0.06em; + height: 28px; + outline: none; + transition: border-color 0.15s, color 0.15s; + max-width: 130px; + } + .source-select:hover { border-color: var(--accent2); color: var(--accent2); } + .source-select option { background: #111; color: #eee; font-family: sans-serif; } + /* ═══════════════════════════════════════════ PLAYER PiP ═══════════════════════════════════════════ */ @@ -772,6 +833,16 @@ $m3u_url = htmlspecialchars($first_source['m3u_url'] ?? ''); M3U + 1 || $allow_personal_epg): ?> + + $src): ?> + = htmlspecialchars($src['name']) ?> + + + ✏ Personal EPG + + + { const btns = document.querySelectorAll('.copy-btn'); @@ -1192,6 +1263,61 @@ function hideTooltip() { document.getElementById('tooltip').classList.remove('vi + +// ── SOURCE SWITCHER ─────────────────────────────────────────────────────────── +let currentEpgUrl = EPG_SOURCES[0]?.epg_url ?? ''; +let currentM3uUrl = EPG_SOURCES[0]?.m3u_url ?? ''; + +function switchSource(value) { + if (value === 'personal') { + openPersonalEpgModal(); + return; + } + const idx = parseInt(value); + const src = EPG_SOURCES[idx]; + if (!src) return; + activeSourceIndex = idx; + currentEpgUrl = src.epg_url; + currentM3uUrl = src.m3u_url ?? ''; + loadEPG(currentEpgUrl); + if (currentM3uUrl) loadM3U(currentM3uUrl); + // Mettre à jour les boutons copy + updateCopyButtons(); +} + +function updateCopyButtons() { + // Rien à faire visuellement, copyUrl() lit currentEpgUrl/currentM3uUrl +} + +function openPersonalEpgModal() { + const saved_epg = localStorage.getItem('gridtv-personal-epg') ?? ''; + const saved_m3u = localStorage.getItem('gridtv-personal-m3u') ?? ''; + const modal = document.getElementById('personalEpgModal'); + document.getElementById('personalEpgInput').value = saved_epg; + document.getElementById('personalM3uInput').value = saved_m3u; + modal.classList.add('visible'); +} + +function closePersonalEpgModal() { + document.getElementById('personalEpgModal').classList.remove('visible'); + // Si annulé, remettre le select sur la source précédente + const sel = document.getElementById('sourceSelect'); + if (sel) sel.value = activeSourceIndex; +} + +function applyPersonalEpg() { + const epg = document.getElementById('personalEpgInput').value.trim(); + const m3u = document.getElementById('personalM3uInput').value.trim(); + if (!epg) { alert('URL EPG obligatoire'); return; } + localStorage.setItem('gridtv-personal-epg', epg); + localStorage.setItem('gridtv-personal-m3u', m3u); + currentEpgUrl = epg; + currentM3uUrl = m3u; + closePersonalEpgModal(); + loadEPG(epg); + if (m3u) loadM3U(m3u); +} + // ── M3U PARSER ──────────────────────────────────────────────────────────────── function slugify(str) { return str.toLowerCase() @@ -1336,5 +1462,26 @@ window.addEventListener('resize', () => { window.addEventListener('load', () => { loadEPG(DEFAULT_EPG_URL); loadM3U(DEFAULT_M3U_URL); }); + + + + + ✏ Personal EPG + Utilisez votre propre source EPG/M3U sur cette instance. + + URL EPG (XMLTV) * + + + + URL M3U (optionnel) + + + + Annuler + Appliquer + + + +