From 3fd8408605b14d83ff79cc531bd49f865864020f Mon Sep 17 00:00:00 2001 From: jbergner Date: Sun, 21 Sep 2025 19:56:14 +0200 Subject: [PATCH] bugfix-7 --- web/static/js/stream.js | 61 +++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/web/static/js/stream.js b/web/static/js/stream.js index 390310e..8cc1fab 100644 --- a/web/static/js/stream.js +++ b/web/static/js/stream.js @@ -5,6 +5,8 @@ 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'; @@ -14,71 +16,84 @@ const enc = encodeURIComponent(name); const candidates = [ `/hls/${enc}/index.m3u8`, - `/hls/${enc}/main_stream.m3u8`, // Fallback, wenn es keinen Master gibt + `/hls/${enc}/main_stream.m3u8`, ]; for (const url of candidates) { - try { - const r = await fetch(url, { cache: 'no-store' }); - if (r.ok) return url; // WICHTIG: GET statt HEAD - } catch (_) {} + 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) { setLive(false); return false; } + if (!url) { playerLive = false; setLive(false); return false; } srcEl.textContent = url; - setLive(true); - try { v.muted = true; v.playsInline = true; } catch(_) {} + 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, (_e, data) => { - console.log('HLS manifest parsed. levels=', data.levels?.length); + 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 + v.src = url; // Safari/iOS } else { console.warn('HLS nicht unterstützt'); + playerLive = false; setLive(false); return false; } - try { await v.play(); } catch(e){ console.log('autoplay blockiert (ok):', e?.name||e); } + 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' }); + 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) { - setLive(!!it.live); - viewersEl.textContent = 'Zuschauer: ' + it.viewers; - } - } catch (_) {} - } + if (!it) return; - v.addEventListener('error', e => console.warn('video error', e)); - v.addEventListener('loadedmetadata', () => console.log('loadedmetadata')); - v.addEventListener('playing', () => console.log('playing')); + 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, 1500)); + await new Promise(r => setTimeout(r, 1200)); } refreshMeta(); setInterval(refreshMeta, 2500);