Claude: Lag Arbeiten
This commit is contained in:
90
server.js
90
server.js
@@ -6,15 +6,14 @@ const path = require('path');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const { createSnapshotRouter } = require('./src/snapshotService');
|
||||
|
||||
const PORT = parseInt(process.env.PORT ?? '8444', 10);
|
||||
const GO2RTC_URL = process.env.GO2RTC_URL ?? 'http://localhost:1984';
|
||||
const GO2RTC_PORT = parseInt(process.env.GO2RTC_PORT ?? '1984', 10);
|
||||
const PORT = parseInt(process.env.PORT ?? '8444', 10);
|
||||
const GO2RTC_URL = process.env.GO2RTC_URL ?? 'http://localhost:1984';
|
||||
const GO2RTC_PORT = parseInt(process.env.GO2RTC_PORT ?? '1984', 10);
|
||||
|
||||
const app = express();
|
||||
|
||||
// ── 1. Eigene Endpunkte (vor dem Proxy registrieren) ─────────────────────────
|
||||
|
||||
// Stabile Snapshot-API für das Homing-Projekt
|
||||
app.use('/api/snapshot', createSnapshotRouter(GO2RTC_URL));
|
||||
|
||||
app.get('/health', async (_req, res) => {
|
||||
@@ -27,21 +26,18 @@ app.get('/health', async (_req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Gibt dem Viewer die go2rtc-Port-Nummer mit – Browser baut WS direkt zu go2rtc.
|
||||
// Trennung HTTP (Node) / WebSocket (go2rtc) ist sauberer als ein fragiler WS-Proxy.
|
||||
app.get('/config.json', (_req, res) => {
|
||||
res.json({ go2rtcPort: GO2RTC_PORT });
|
||||
});
|
||||
|
||||
// ── 2. HTTP-Proxy zu go2rtc (nur für Script-Dateien und API ohne WS) ─────────
|
||||
// /api/ws NICHT hier proxy-en – das macht der Browser direkt (s. viewer.js).
|
||||
// ── 2. HTTP-Proxy zu go2rtc ───────────────────────────────────────────────────
|
||||
const go2rtcProxy = createProxyMiddleware({
|
||||
target: GO2RTC_URL,
|
||||
target: GO2RTC_URL,
|
||||
changeOrigin: true,
|
||||
pathFilter: ['/api', '/video-rtc.js', '/video-stream.js'],
|
||||
logger: console,
|
||||
pathFilter: ['/api', '/video-rtc.js', '/video-stream.js'],
|
||||
logger: console,
|
||||
on: {
|
||||
error: (err, req, res) => {
|
||||
error: (err, _req, res) => {
|
||||
console.error('[HPM] proxy error:', err.message);
|
||||
if (!res.headersSent) res.status(502).json({ error: 'go2rtc nicht erreichbar' });
|
||||
},
|
||||
@@ -49,9 +45,70 @@ const go2rtcProxy = createProxyMiddleware({
|
||||
});
|
||||
app.use(go2rtcProxy);
|
||||
|
||||
// ── 3. Statische Dateien (eigener Viewer) ────────────────────────────────────
|
||||
// ── 3. Statische Dateien ──────────────────────────────────────────────────────
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// ── 4. go2rtc Stream-Monitor (server-seitiges Logging) ───────────────────────
|
||||
// Pollt alle 5 s go2rtc /api/streams und loggt Änderungen.
|
||||
// Sichtbar im Portainer-Log von AppRobotWebcam.
|
||||
// Logt: Producer-Starts/-Stops, Consumer-Anzahl, Timeouts/Restarts.
|
||||
//
|
||||
// go2rtc /api/streams liefert z.B.:
|
||||
// { "cam0": { "producers": [{"url":"...","state":"running"}], "consumers": [...] } }
|
||||
//
|
||||
const STREAM_POLL_MS = 5000;
|
||||
let prevStreamState = {};
|
||||
|
||||
async function pollGo2rtcStreams() {
|
||||
try {
|
||||
const r = await fetch(`${GO2RTC_URL}/api/streams`);
|
||||
if (!r.ok) { console.warn(`[monitor] /api/streams → HTTP ${r.status}`); return; }
|
||||
const streams = await r.json();
|
||||
|
||||
for (const [name, data] of Object.entries(streams)) {
|
||||
const producers = data.producers ?? [];
|
||||
const consumers = data.consumers ?? [];
|
||||
const nConsumers = consumers.length;
|
||||
const prev = prevStreamState[name] ?? {};
|
||||
|
||||
// Producer-Status
|
||||
for (let i = 0; i < producers.length; i++) {
|
||||
const p = producers[i];
|
||||
const state = p.state ?? 'unknown';
|
||||
const key = `${name}.p${i}`;
|
||||
const pPrev = prevStreamState[key];
|
||||
|
||||
if (pPrev !== state) {
|
||||
if (state === 'running') console.log(`[monitor][${name}] producer #${i} LÄUFT (${p.url ?? ''})`);
|
||||
if (state === 'error') console.error(`[monitor][${name}] producer #${i} FEHLER (${p.url ?? ''})`);
|
||||
if (state === 'stop') console.warn(`[monitor][${name}] producer #${i} GESTOPPT`);
|
||||
if (!['running','error','stop'].includes(state))
|
||||
console.log(`[monitor][${name}] producer #${i} state="${state}"`);
|
||||
prevStreamState[key] = state;
|
||||
}
|
||||
}
|
||||
|
||||
// Consumer-Anzahl — nur loggen wenn sie sich ändert
|
||||
if (prev.nConsumers !== nConsumers) {
|
||||
console.log(`[monitor][${name}] consumers: ${prev.nConsumers ?? '?'} → ${nConsumers}`);
|
||||
prevStreamState[name] = { ...prev, nConsumers };
|
||||
}
|
||||
}
|
||||
|
||||
// Streams die verschwunden sind (Timeout/Restart)
|
||||
for (const name of Object.keys(prevStreamState)) {
|
||||
if (name.includes('.')) continue; // skip producer-state keys
|
||||
if (!streams[name]) {
|
||||
console.warn(`[monitor][${name}] Stream verschwunden aus go2rtc`);
|
||||
delete prevStreamState[name];
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('[monitor] go2rtc nicht erreichbar:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Start ─────────────────────────────────────────────────────────────────────
|
||||
const server = http.createServer(app);
|
||||
|
||||
@@ -61,6 +118,13 @@ server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(` go2rtc WS: ws://[host]:${GO2RTC_PORT} (Browser verbindet direkt)`);
|
||||
console.log(` Viewer: http://0.0.0.0:${PORT}/`);
|
||||
console.log(` Snapshot API: http://0.0.0.0:${PORT}/api/snapshot/cam0`);
|
||||
console.log(` Stream-Monitor: alle ${STREAM_POLL_MS / 1000}s → Portainer-Log`);
|
||||
|
||||
// Ersten Poll nach 3 s (go2rtc braucht einen Moment zum Starten)
|
||||
setTimeout(() => {
|
||||
pollGo2rtcStreams();
|
||||
setInterval(pollGo2rtcStreams, STREAM_POLL_MS);
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
const shutdown = (sig) => {
|
||||
|
||||
Reference in New Issue
Block a user