diff --git a/web/static/js/stream.js b/web/static/js/stream.js index a7e169e..390310e 100644 --- a/web/static/js/stream.js +++ b/web/static/js/stream.js @@ -5,27 +5,34 @@ const viewersEl = document.getElementById('viewers'); const srcEl = document.getElementById('hlssrc'); - const manifest = '/hls/' + encodeURIComponent(name) + '/index.m3u8'; - - function setLive(on){ - liveEl.className = 'pill ' + (on ? 'live' : 'off'); + function setLive(on){ + liveEl.className = 'pill ' + (on ? 'live' : 'off'); liveEl.textContent = on ? 'LIVE' : 'Offline'; } - async function head(u){ - try { const r = await fetch(u, {method:'HEAD', cache:'no-store'}); return r.ok; } - catch { return false; } + async function chooseManifest() { + const enc = encodeURIComponent(name); + const candidates = [ + `/hls/${enc}/index.m3u8`, + `/hls/${enc}/main_stream.m3u8`, // Fallback, wenn es keinen Master gibt + ]; + for (const url of candidates) { + try { + const r = await fetch(url, { cache: 'no-store' }); + if (r.ok) return url; // WICHTIG: GET statt HEAD + } catch (_) {} + } + return null; } async function initPlayer() { - srcEl.textContent = manifest; - const ok = await head(manifest); - setLive(ok); - if (!ok) return false; + const url = await chooseManifest(); + if (!url) { setLive(false); return false; } - // Autoplay-Chance verbessern - try { v.muted = true; } catch(_) {} - try { v.playsInline = true; } catch(_) {} + srcEl.textContent = url; + setLive(true); + + try { v.muted = true; v.playsInline = true; } catch(_) {} if (window.Hls && Hls.isSupported()) { if (!window._hls) { @@ -38,23 +45,21 @@ }); window._hls.attachMedia(v); } - window._hls.loadSource(manifest); + window._hls.loadSource(url); } else if (v.canPlayType('application/vnd.apple.mpegurl')) { - v.src = manifest; // Safari / iOS + v.src = url; // Safari / iOS } else { console.warn('HLS nicht unterstützt'); return false; } - // Autoplay versuchen (wird bei Desktop-Browsern oft blockiert; Controls bleiben) - try { await v.play(); } catch(e) { console.log('autoplay blockiert (ok):', e?.name||e); } - + try { await v.play(); } catch(e){ console.log('autoplay blockiert (ok):', e?.name||e); } return true; } async function refreshMeta(){ 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); @@ -62,7 +67,7 @@ setLive(!!it.live); viewersEl.textContent = 'Zuschauer: ' + it.viewers; } - } catch {} + } catch (_) {} } v.addEventListener('error', e => console.warn('video error', e)); @@ -70,7 +75,7 @@ v.addEventListener('playing', () => console.log('playing')); (async function boot(){ - for (let i=0; i<10; i++) { + for (let i=0; i<10; i++){ const ok = await initPlayer(); if (ok) break; await new Promise(r => setTimeout(r, 1500));