claude: webcam

This commit is contained in:
chk
2026-06-03 19:50:16 +02:00
parent 1a712ed877
commit 77c20bc3f1
9 changed files with 346 additions and 365 deletions

View File

@@ -3,65 +3,57 @@
const express = require('express');
const http = require('http');
const path = require('path');
const { createSnapshotRouter } = require('./src/snapshotService');
const { createProxyMiddleware } = require('http-proxy-middleware');
const { createSnapshotRouter } = require('./src/snapshotService');
const PORT = parseInt(process.env.PORT ?? '8444', 10);
const PORT = parseInt(process.env.PORT ?? '8444', 10);
const GO2RTC_URL = process.env.GO2RTC_URL ?? 'http://localhost:1984';
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
// ── Stabile Snapshot-API (vor dem Proxy registrieren!) ────────────────────────
// Für das Homing-Projekt: GET /api/snapshot/cam0 → JPEG
app.use('/api/snapshot', createSnapshotRouter(GO2RTC_URL));
// ── WebRTC signaling proxy ────────────────────────────────────────────────────
// Browser postet SDP-Offer hierher; wir leiten es an go2rtc weiter und
// geben die SDP-Answer zurück. Nur ein HTTP-Port nach aussen nötig.
app.post(
'/api/webrtc',
express.text({ type: 'application/sdp', limit: '64kb' }),
async (req, res) => {
const src = req.query.src ?? '';
try {
const upstream = await fetch(
`${GO2RTC_URL}/api/webrtc?src=${encodeURIComponent(src)}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/sdp' },
body: req.body,
}
);
if (!upstream.ok) {
const msg = await upstream.text();
return res.status(upstream.status).send(msg);
}
const answer = await upstream.text();
res.set('Content-Type', 'application/sdp');
res.send(answer);
} catch (err) {
res.status(503).json({ error: `go2rtc nicht erreichbar: ${err.message}` });
}
}
);
// ── Health ────────────────────────────────────────────────────────────────────
app.get('/health', async (_req, res) => {
let go2rtcOk = false;
try {
const r = await fetch(`${GO2RTC_URL}/api/streams`);
go2rtcOk = r.ok;
} catch { /* not reachable */ }
res.json({ status: go2rtcOk ? 'ok' : 'degraded', go2rtc: go2rtcOk });
const streams = r.ok ? await r.json() : {};
res.json({ status: r.ok ? 'ok' : 'degraded', cameras: Object.keys(streams) });
} catch (err) {
res.status(503).json({ status: 'down', error: err.message });
}
});
// ── Start ─────────────────────────────────────────────────────────────────────
// ── Reverse-Proxy zu go2rtc ───────────────────────────────────────────────────
// Reicht nur die nötigen Pfade durch (go2rtc-Admin bleibt damit unerreichbar):
// /api/ws WebRTC/MSE-Signaling (WebSocket)
// /api/frame.jpeg Snapshots
// /api/stream.* MJPEG/MP4-Fallback
// /api/streams Stream-Liste
// /video-rtc.js, /video-stream.js go2rtc's offizieller Player
const go2rtcProxy = createProxyMiddleware({
target: GO2RTC_URL,
changeOrigin: true,
ws: true,
pathFilter: ['/api/**', '/video-rtc.js', '/video-stream.js'],
logger: console,
});
app.use(go2rtcProxy);
// ── Eigener Viewer ─────────────────────────────────────────────────────────────
app.use(express.static(path.join(__dirname, 'public')));
// ── Start ───────────────────────────────────────────────────────────────────────
const server = http.createServer(app);
server.on('upgrade', go2rtcProxy.upgrade); // WebSocket-Signaling durchreichen
server.listen(PORT, '0.0.0.0', () => {
console.log(`AppRobotWebcam on http://0.0.0.0:${PORT}`);
console.log(` go2rtc backend: ${GO2RTC_URL}`);
console.log(` WebRTC signaling proxy: POST /api/webrtc?src=cam0`);
console.log(` Snapshot API: GET /api/snapshot/cam0`);
console.log(` go2rtc backend: ${GO2RTC_URL}`);
console.log(` Viewer: http://0.0.0.0:${PORT}/`);
console.log(` Snapshot (API): http://0.0.0.0:${PORT}/api/snapshot/cam0`);
});
const shutdown = (sig) => {