'use strict'; const express = require('express'); const http = require('http'); const path = require('path'); const { CameraSwitch } = require('./src/cameraSwitch'); const { detectDevices } = require('./src/deviceDetect'); const { createSnapshotRouter, createStreamRouter } = require('./src/snapshotService'); const PORT = parseInt(process.env.PORT ?? '8444', 10); const LIVE_SIZE = process.env.LIVE_SIZE ?? '640x480'; const LIVE_FPS = parseInt(process.env.LIVE_FPS ?? '30', 10); const HIRES_SIZE = process.env.HIRES_SIZE ?? '1280x960'; const HIRES_FPS = parseInt(process.env.HIRES_FPS ?? '15', 10); // ── Kameras: cam0 = erstes Gerät, cam1 = zweites … (DEV0/DEV1-Env überschreibt) ─ const devices = detectDevices(); const switches = {}; devices.forEach((device, i) => { const id = `cam${i}`; switches[id] = new CameraSwitch({ id, device, liveSize: LIVE_SIZE, liveFps: LIVE_FPS, hiresSize: HIRES_SIZE, hiresFps: HIRES_FPS }); }); const app = express(); // ── 1. Eigene Endpunkte ─────────────────────────────────────────────────────── app.use('/api/snapshot', createSnapshotRouter(switches)); app.use('/api/stream', createStreamRouter(switches)); app.get('/health', (_req, res) => { res.json({ status: 'ok', cameras: Object.values(switches).map((sw) => ({ id: sw.id, device: sw.device, state: sw.state, hasFrame: !!sw.latest, })), }); }); app.get('/config.json', (_req, res) => { res.json({ cameras: Object.keys(switches) }); }); // ── 2. Statische Dateien ────────────────────────────────────────────────────── // no-cache: Browser MUSS index.html/viewer.js vor Nutzung revalidieren. app.use(express.static(path.join(__dirname, 'public'), { setHeaders: (res) => res.setHeader('Cache-Control', 'no-cache'), })); // ── 3. Start ────────────────────────────────────────────────────────────────── const server = http.createServer(app); server.listen(PORT, '0.0.0.0', () => { console.log(`AppRobotWebcam http://0.0.0.0:${PORT}`); console.log(` Kameras: ${Object.entries(switches).map(([id, sw]) => `${id}=${sw.device}`).join(', ')}`); console.log(` Live: ${LIVE_SIZE}@${LIVE_FPS} · HD-Grab: ${HIRES_SIZE}@${HIRES_FPS}`); console.log(` Viewer: http://0.0.0.0:${PORT}/`); console.log(` Live-Stream: http://0.0.0.0:${PORT}/api/stream/cam0`); console.log(` Snapshot API: http://0.0.0.0:${PORT}/api/snapshot/cam0 (+ /hires)`); // Live-Producer starten (Dauerbetrieb) Object.values(switches).forEach((sw) => sw.start()); }); const shutdown = (sig) => { console.log(`\n${sig} – shutting down`); Object.values(switches).forEach((sw) => { sw.stopping = true; if (sw.proc) { try { sw.proc.kill('SIGKILL'); } catch (_e) {} } }); server.close(() => process.exit(0)); setTimeout(() => process.exit(0), 3000); // Sicherheitsnetz }; process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM'));