Claude: Fix

This commit is contained in:
chk
2026-06-03 21:07:46 +02:00
parent 98d77e6697
commit f618fdac42
3 changed files with 90 additions and 82 deletions

View File

@@ -1,38 +1,37 @@
'use strict';
// go2rtc Player-Modi Fallback-Reihenfolge: WebRTC → MSE → MJPEG
// Schlägt WebRTC fehl, springt der go2rtc-Player automatisch weiter → kein schwarzes Bild.
const MODE = 'webrtc,mse,mjpeg';
// ── Logging-Hilfe (sichtbar in Browser DevTools → Console) ──────────────────
const LOG_PREFIX = '[AppRobotWebcam]';
function log(cam, msg) { console.log(`${LOG_PREFIX}[${cam}] ${msg}`); }
function warn(cam, msg) { console.warn(`${LOG_PREFIX}[${cam}] ⚠ ${msg}`); }
function logErr(cam, msg, e) { console.error(`${LOG_PREFIX}[${cam}] ✗ ${msg}`, e ?? ''); }
// ── Logging (sichtbar in 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 err = (c, m, e) => console.error(`${P}[${c}] ✗ ${m}`, e ?? '');
// ── Kamera-View aufbauen ─────────────────────────────────────────────────────
function buildCamera(camId, container) {
const wsUrl = `/api/ws?src=${encodeURIComponent(camId)}`;
log(camId, `View erstellt mode=${MODE} ws=${wsUrl}`);
function buildCamera(camId, go2rtcPort, container) {
// WebSocket direkt zu go2rtc kein Proxy-Zwischenschritt, garantiert stabil.
// Protokoll: ws:// auf LAN (http). Für Internet mit TLS wird aus ws: wss: (Caddy).
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';
// go2rtc Web-Component: verbindet sich via WebSocket zu /api/ws (→ Node-Proxy → go2rtc)
const stream = document.createElement('video-stream');
stream.mode = MODE;
// Events vom inneren <video>-Element abfangen (capture-Phase = vor dem Element selbst)
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 (kein Daten)'), true);
stream.addEventListener('waiting', () => warn(camId, 'waiting (Buffer leer)'), true);
stream.addEventListener('error', (e) => logErr(camId, 'Video-Fehler', e), true);
// Events vom inneren <video>-Element (capture-Phase)
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) => err(camId, 'Video-Fehler', e), true);
// src setzen startet die Verbindung
log(camId, `Verbinde → ${wsUrl}`);
stream.src = wsUrl;
log(camId, `Verbinde WebSocket → ${wsUrl}`);
stream.src = wsUrl; // setzt den VideoRTC-src und startet die Verbindung
box.appendChild(stream);
@@ -46,9 +45,9 @@ function buildCamera(camId, container) {
const snapBtn = document.createElement('button');
snapBtn.textContent = 'Snapshot';
snapBtn.onclick = () => {
log(camId, 'Snapshot download /api/snapshot/' + camId);
log(camId, `Snapshot → /api/snapshot/${camId}`);
const a = document.createElement('a');
a.href = `/api/snapshot/${camId}`;
a.href = `/api/snapshot/${camId}`;
a.download = `${camId}_${Date.now()}.jpg`;
a.click();
};
@@ -56,61 +55,55 @@ function buildCamera(camId, container) {
box.appendChild(actions);
container.appendChild(box);
// Connectivity-Check nach 5 s: ist die WebSocket-Verbindung zu /api/ws nutzbar?
setTimeout(async () => {
try {
const r = await fetch('/api/streams');
if (r.ok) {
const d = await r.json();
log(camId, `go2rtc streams: ${Object.keys(d).join(', ')}`);
} else {
warn(camId, `/api/streams → HTTP ${r.status}`);
}
} catch (err) {
logErr(camId, '/api/streams nicht erreichbar (Proxy defekt?)', err);
}
}, 5000);
}
// ── Init ─────────────────────────────────────────────────────────────────────
async function init() {
log('init', 'Starte...');
// go2rtc-Port vom Server holen (damit er nicht im Client hart kodiert ist)
let go2rtcPort = 1984;
try {
const r = await fetch('/config.json');
const d = await r.json();
go2rtcPort = d.go2rtcPort ?? 1984;
log('init', `go2rtc WebSocket-Port: ${go2rtcPort}`);
} catch (e) {
warn('init', `Konnte /config.json nicht laden, nehme Port ${go2rtcPort}`);
}
// go2rtc Web-Component muss geladen sein bevor .src gesetzt wird
try {
await customElements.whenDefined('video-stream');
log('init', '<video-stream> definiert');
} catch (err) {
logErr('init', '<video-stream> nicht geladen /video-stream.js erreichbar?', err);
} catch (e) {
err('init', '<video-stream> nicht geladen /video-stream.js erreichbar?', e);
return;
}
const container = document.getElementById('cameras');
const statusText = document.getElementById('statusText');
// Kamera-Liste von go2rtc (via Node-Proxy /api/snapshot → /api/streams)
// Kamera-Liste von go2rtc via Node-Proxy
let cams = [];
try {
const r = await fetch('/api/snapshot');
log('init', `/api/snapshot → HTTP ${r.status}`);
const d = await r.json();
if (Array.isArray(d.cameras) && d.cameras.length) {
cams = d.cameras.map(c => c.id);
log('init', `Kameras: ${cams.join(', ')}`);
} else {
warn('init', 'Keine Kameras in go2rtc Config OK? go2rtc läuft?');
if (r.ok) {
const d = await r.json();
cams = (d.cameras ?? []).map(c => c.id);
log('init', `Kameras: ${cams.join(', ') || '(keine)'}`);
}
} catch (err) {
logErr('init', '/api/snapshot nicht erreichbar Fallback cam0/cam1', err);
} catch (e) {
err('init', '/api/snapshot Fehler Fallback', e);
}
if (cams.length === 0) {
warn('init', 'Fallback: nehme cam0 und cam1 an');
warn('init', 'Fallback auf cam0, cam1');
cams = ['cam0', 'cam1'];
}
cams.forEach(id => buildCamera(id, container));
cams.forEach(id => buildCamera(id, go2rtcPort, container));
statusText.textContent = `${cams.length} Kamera${cams.length !== 1 ? 's' : ''} · WebRTC`;
log('init', 'Fertig');
}