Claude Multicam (a)
This commit is contained in:
59
server.js
59
server.js
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
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 { createSnapshotRouter, createStreamRouter, createCamerasRouter } = require('./src/snapshotService');
|
||||
|
||||
const PORT = parseInt(process.env.PORT ?? '8444', 10);
|
||||
const LIVE_SIZE = process.env.LIVE_SIZE ?? '640x480';
|
||||
@@ -16,31 +16,60 @@ const ENCODE_MODE = process.env.ENCODE_MODE ?? 'copybsf'; // 'copybsf' (niedrige
|
||||
const ON_DEMAND = (process.env.ON_DEMAND ?? 'true') !== 'false'; // Live nur bei Verbrauchern (spart idle-CPU)
|
||||
const IDLE_GRACE_MS = parseInt(process.env.IDLE_GRACE_MS ?? '15000', 10);
|
||||
|
||||
// ── Kameras: cam0 = erstes Gerät, cam1 = zweites … (DEV0/DEV1-Env überschreibt) ─
|
||||
const devices = detectDevices();
|
||||
// ── cameras.json → CameraSwitch-Instanzen ─────────────────────────────────────
|
||||
let camerasJson;
|
||||
try {
|
||||
camerasJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'cameras.json'), 'utf8'));
|
||||
} catch (e) {
|
||||
console.error('cameras.json fehlt oder ungültig:', e.message); process.exit(1);
|
||||
}
|
||||
const camsConfig = camerasJson.cameras;
|
||||
if (!Array.isArray(camsConfig) || camsConfig.length === 0) {
|
||||
console.error('cameras.json: "cameras" muss ein nicht-leeres Array sein'); process.exit(1);
|
||||
}
|
||||
|
||||
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, encode: ENCODE_MODE, onDemand: ON_DEMAND, idleGraceMs: IDLE_GRACE_MS });
|
||||
});
|
||||
const camsMeta = []; // { id, device, name, position, stream, hires, note }
|
||||
for (const cam of camsConfig) {
|
||||
if (!cam.id || !cam.device) {
|
||||
console.error(`cameras.json: Eintrag ohne id/device: ${JSON.stringify(cam)}`); process.exit(1);
|
||||
}
|
||||
switches[cam.id] = new CameraSwitch({
|
||||
id: cam.id, device: cam.device,
|
||||
liveSize: LIVE_SIZE, liveFps: LIVE_FPS,
|
||||
hiresSize: HIRES_SIZE, hiresFps: HIRES_FPS,
|
||||
encode: ENCODE_MODE, onDemand: ON_DEMAND, idleGraceMs: IDLE_GRACE_MS,
|
||||
});
|
||||
camsMeta.push({
|
||||
id: cam.id,
|
||||
device: cam.device,
|
||||
name: cam.name ?? cam.id,
|
||||
position: cam.position ?? '',
|
||||
stream: cam.stream !== false,
|
||||
hires: cam.hires !== false,
|
||||
note: cam.note ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
const app = express();
|
||||
|
||||
// ── 1. Eigene Endpunkte ───────────────────────────────────────────────────────
|
||||
app.use('/api/snapshot', createSnapshotRouter(switches));
|
||||
app.use('/api/snapshot', createSnapshotRouter(switches, camsMeta));
|
||||
app.use('/api/stream', createStreamRouter(switches));
|
||||
app.use('/api/cameras', createCamerasRouter(camsMeta));
|
||||
|
||||
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,
|
||||
})),
|
||||
cameras: camsMeta.map((c) => {
|
||||
const sw = switches[c.id];
|
||||
return { id: c.id, name: c.name, device: c.device, state: sw?.state, hasFrame: !!sw?.latest };
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/config.json', (_req, res) => {
|
||||
res.json({ cameras: Object.keys(switches) });
|
||||
res.json({ cameras: camsMeta.map((c) => ({ id: c.id, name: c.name, stream: c.stream })) });
|
||||
});
|
||||
|
||||
// ── 2. Statische Dateien ──────────────────────────────────────────────────────
|
||||
@@ -54,11 +83,9 @@ 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(` Kameras: ${camsMeta.map((c) => `${c.id}=${c.device} "${c.name}" stream=${c.stream}`).join(', ')}`);
|
||||
console.log(` Live: ${LIVE_SIZE}@${LIVE_FPS} · HD-Grab: ${HIRES_SIZE}@${HIRES_FPS} · Encode: ${ENCODE_MODE} · On-Demand: ${ON_DEMAND}`);
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user