(function () { const name = document.documentElement.dataset.stream; const v = document.getElementById('v'); const liveEl = document.getElementById('live'); const viewersEl = document.getElementById('viewers'); const srcEl = document.getElementById('hlssrc'); let playerLive = false; // <- neue Quelle der Wahrheit function setLive(on){ liveEl.className = 'pill ' + (on ? 'live' : 'off'); liveEl.textContent = on ? 'LIVE' : 'Offline'; } async function chooseManifest() { const enc = encodeURIComponent(name); const candidates = [ `/hls/${enc}/index.m3u8`, `/hls/${enc}/main_stream.m3u8`, ]; for (const url of candidates) { try { const r = await fetch(url, { cache: 'no-store' }); if (r.ok) return url; } catch {} } return null; } async function initPlayer() { const url = await chooseManifest(); if (!url) { playerLive = false; setLive(false); return false; } srcEl.textContent = url; try { v.muted = true; v.playsInline = true; } catch {} if (window.Hls && Hls.isSupported()) { if (!window._hls) { window._hls = new Hls({ liveDurationInfinity: true }); window._hls.on(Hls.Events.MANIFEST_PARSED, () => { // Manifest geladen → sehr gutes Live-Signal playerLive = true; setLive(true); }); window._hls.on(Hls.Events.ERROR, (_e, data) => { console.warn('hls.js error', data); // fatale Fehler → als offline markieren if (data?.fatal) { playerLive = false; setLive(false); } }); window._hls.attachMedia(v); } window._hls.loadSource(url); } else if (v.canPlayType('application/vnd.apple.mpegurl')) { v.src = url; // Safari/iOS } else { console.warn('HLS nicht unterstützt'); playerLive = false; setLive(false); return false; } try { await v.play(); } catch(e){ /* Autoplay blockiert ist ok */ } return true; } // Zusätzliche Player-Signale v.addEventListener('loadedmetadata', () => { playerLive = true; setLive(true); }); v.addEventListener('playing', () => { playerLive = true; setLive(true); }); v.addEventListener('error', () => { playerLive = false; setLive(false); }); async function refreshMeta(){ // API nur als Zusatz – überschreibt NIE ein „echtes“ playerLive=true try { const r = await fetch('/api/streams', { cache:'no-store' }); if (!r.ok) return; const d = await r.json(); const it = d.items.find(x => x.name === name); if (!it) return; const apiLive = !!it.live; const combinedLive = playerLive || apiLive; setLive(combinedLive); let viewers = it.viewers ?? 0; if (combinedLive && viewers === 0) { // Wir sehen selbst Video → mindestens 1 Betrachter viewers = '≥1'; } viewersEl.textContent = 'Zuschauer: ' + viewers; } catch (_) { // Bei API-Fehler nichts überschreiben } } (async function boot(){ for (let i=0; i<10; i++){ const ok = await initPlayer(); if (ok) break; await new Promise(r => setTimeout(r, 1200)); } refreshMeta(); //setInterval(refreshMeta, 2500); })(); const es = new EventSource('/api/streams/events'); es.addEventListener('update', (ev)=>{ try { const data = JSON.parse(ev.data); const it = data.items.find(x=>x.name===name); if (!it) return; const apiLive = !!it.live; const combinedLive = playerLive || apiLive; setLive(combinedLive); let viewers = it.viewers ?? 0; if (combinedLive && viewers === 0) viewers = '≥1'; viewersEl.textContent = 'Zuschauer: ' + viewers; } catch(e){ /* ignore */ } }); })();