'use strict'; // go2rtc Player-Modi – Fallback-Reihenfolge: WebRTC → MSE → MJPEG const MODE = 'webrtc,mse,mjpeg'; // ── Logging (Browser DevTools → Console → F12) ─────────────────────────────── const P = '[WebcamViewer]'; const log = (c, m) => console.log(`${P}[${c}] ${m}`); const warn = (c, m) => console.warn(`${P}[${c}] ⚠ ${m}`); const logErr = (c, m, e) => console.error(`${P}[${c}] ✗ ${m}`, e ?? ''); // ── Snapshot aller Kameras gleichzeitig ────────────────────────────────────── // Auflösung = was go2rtc im MJPEG-Stream hält (aktuell 640×480 gemäss Config). // Für höhere Auflösung: in go2rtc.yaml einen separaten Hi-Res-Stream definieren // und hier auf dessen /api/frame.jpeg?src=cam0_hires zeigen. function snapshotAll(camIds) { const ts = Date.now(); log('snap', `Snapshot alle Kameras: ${camIds.join(', ')}`); camIds.forEach(id => { const a = document.createElement('a'); a.href = `/api/snapshot/${id}`; a.download = `${id}_${ts}.jpg`; document.body.appendChild(a); a.click(); document.body.removeChild(a); }); } // ── Kamera-View aufbauen ───────────────────────────────────────────────────── function buildCamera(camId, go2rtcPort, container) { const wsUrl = `ws://${location.hostname}:${go2rtcPort}/api/ws?src=${encodeURIComponent(camId)}`; log(camId, `View erstellt mode="${MODE}" ws=${wsUrl}`); const box = document.createElement('div'); box.className = 'cam-box'; const stream = document.createElement('video-stream'); stream.mode = MODE; stream.addEventListener('play', () => log(camId, '▶ spielt'), true); stream.addEventListener('playing', () => log(camId, '▶ Bild läuft'), true); stream.addEventListener('pause', () => warn(camId, 'pausiert'), true); stream.addEventListener('stalled', () => warn(camId, 'stalled (keine Daten)'), true); stream.addEventListener('waiting', () => warn(camId, 'waiting (Buffer leer)'), true); stream.addEventListener('error', (e) => logErr(camId, 'Video-Fehler', e), true); log(camId, `Verbinde WebSocket → ${wsUrl}`); stream.src = wsUrl; box.appendChild(stream); const label = document.createElement('div'); label.className = 'cam-label'; label.textContent = camId; box.appendChild(label); container.appendChild(box); } // ── Init ───────────────────────────────────────────────────────────────────── async function init() { log('init', 'Starte...'); let go2rtcPort = 1984; try { const r = await fetch('/config.json'); const d = await r.json(); go2rtcPort = d.go2rtcPort ?? 1984; log('init', `go2rtc WS-Port: ${go2rtcPort}`); } catch (e) { warn('init', `Konnte /config.json nicht laden, nehme Port ${go2rtcPort}`); } try { await customElements.whenDefined('video-stream'); log('init', ' definiert'); } catch (e) { logErr('init', ' nicht geladen – /video-stream.js erreichbar?', e); return; } const container = document.getElementById('cameras'); const statusText = document.getElementById('statusText'); let cams = []; try { const r = await fetch('/api/snapshot'); log('init', `/api/snapshot → HTTP ${r.status}`); if (r.ok) { const d = await r.json(); cams = (d.cameras ?? []).map(c => c.id); log('init', `Kameras: ${cams.join(', ') || '(keine)'}`); } } catch (e) { logErr('init', '/api/snapshot Fehler – Fallback', e); } if (cams.length === 0) { warn('init', 'Fallback auf cam0, cam1'); cams = ['cam0', 'cam1']; } // Globaler Snapshot-Button in der Header-Bar verdrahten const snapAllBtn = document.getElementById('snapAllBtn'); if (snapAllBtn) { snapAllBtn.onclick = () => snapshotAll(cams); snapAllBtn.disabled = false; } cams.forEach(id => buildCamera(id, go2rtcPort, container)); statusText.textContent = `${cams.length} Kamera${cams.length !== 1 ? 's' : ''} · WebRTC`; log('init', 'Fertig'); } init();