Files
nginx-stream-server/web/static/js/stream.js
jbergner b2e7515327
All checks were successful
release-tag / release-image (push) Successful in 2m1s
bugfix-9
2025-09-21 21:28:10 +02:00

146 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(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); });
function pickViewers(it){
// Häufige Feldnamen aus diversen Backends
const candidates = [
it.viewers,
it.viewerCount,
it.viewCount,
it.watchers,
it.clients,
it.hls_viewers,
it.stats && it.stats.viewers,
];
for (const v of candidates) {
const n = typeof v === 'string' ? parseInt(v, 10) : v;
if (Number.isFinite(n) && n >= 0) return n;
}
return 0;
}
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 = pickViewers(it);
// Nur wenn API wirklich 0/fehlend meldet, aber der Player sicher läuft,
// zeigen wir "≥1" als sinnvollen Fallback:
if (playerLive && viewers === 0) {
viewersEl.textContent = 'Zuschauer: ≥1';
} else {
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 = pickViewers(it);
// Nur wenn API wirklich 0/fehlend meldet, aber der Player sicher läuft,
// zeigen wir "≥1" als sinnvollen Fallback:
if (playerLive && viewers === 0) {
viewersEl.textContent = 'Zuschauer: ≥1';
} else {
viewersEl.textContent = 'Zuschauer: ' + viewers;
}
} catch(e){ /* ignore */ }
});
})();