Button Foto
This commit is contained in:
100
server/webcamClient.js
Normal file
100
server/webcamClient.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* webcamClient.js
|
||||
* Kapselt alle Zugriffe auf den WebCam-Service.
|
||||
* Basis-URL kommt von aussen (WEBCAM_URL) — kein Hartkodieren hier.
|
||||
*
|
||||
* Verwendung:
|
||||
* import { WebcamClient } from './webcamClient.js';
|
||||
* const wc = new WebcamClient(process.env.WEBCAM_URL);
|
||||
* const cameras = await wc.getCameras();
|
||||
* const response = await wc.getSnapshot('cam0'); // Response-Objekt, JPEG-Body
|
||||
*/
|
||||
|
||||
const TIMEOUT_MS = 15_000;
|
||||
|
||||
export class WebcamClient {
|
||||
/** @param {string} baseUrl z.B. "http://appRobotWebcam:8444" */
|
||||
constructor(baseUrl) {
|
||||
if (!baseUrl) throw new Error('WebcamClient: baseUrl ist erforderlich');
|
||||
this.baseUrl = baseUrl.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste aller Kameras mit Metadaten.
|
||||
* `calibrationUrl` ist enthalten, wenn eine .npz unter data/calibration/{id}/ liegt.
|
||||
* @returns {Promise<{cameras: CameraMeta[]}>}
|
||||
*/
|
||||
async getCameras() {
|
||||
const res = await this.#get('/api/cameras');
|
||||
if (!res.ok) throw new Error(`getCameras: HTTP ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* HD-JPEG einer Kamera als fetch-Response.
|
||||
* Caller kann res.body direkt pipen (kein Buffering).
|
||||
* @param {string} id Kamera-ID, z.B. "cam0"
|
||||
* @param {boolean} hires true = /hires (Default), false = Live-Auflösung
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async getSnapshot(id, hires = true) {
|
||||
const path = hires ? `/api/snapshot/${id}/hires` : `/api/snapshot/${id}`;
|
||||
const res = await this.#get(path);
|
||||
if (!res.ok) throw new Error(`getSnapshot(${id}): HTTP ${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kalibrierungsdatei (.npz) als ArrayBuffer.
|
||||
* Kann direkt an den BodyTracker weitergereicht werden.
|
||||
* Wirft bei 404 (noch keine Kalibrierung vorhanden).
|
||||
* @param {string} id Kamera-ID
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
*/
|
||||
async getCalibration(id) {
|
||||
const res = await this.#get(`/api/cameras/${id}/calibration`);
|
||||
if (res.status === 404) throw new Error(`Keine Kalibrierung für Kamera "${id}"`);
|
||||
if (!res.ok) throw new Error(`getCalibration(${id}): HTTP ${res.status}`);
|
||||
return res.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gesundheitsstatus des WebCam-Service inkl. Kamera-Zustände.
|
||||
* @returns {Promise<{status: string, cameras: CameraState[]}>}
|
||||
*/
|
||||
async health() {
|
||||
const res = await this.#get('/health');
|
||||
if (!res.ok) throw new Error(`health: HTTP ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// ── intern ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#get(path, options = {}) {
|
||||
const url = `${this.baseUrl}${path}`;
|
||||
return fetch(url, {
|
||||
...options,
|
||||
signal: AbortSignal.timeout(TIMEOUT_MS),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CameraMeta
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} position "front" | "left" | "right"
|
||||
* @property {boolean} stream
|
||||
* @property {boolean} hires
|
||||
* @property {string} encode
|
||||
* @property {string|null} mseCodec
|
||||
* @property {string} note Hardware-Serial (stable key)
|
||||
* @property {string|null} [calibrationUrl] vorhanden wenn .npz existiert
|
||||
*
|
||||
* @typedef {Object} CameraState
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} device
|
||||
* @property {string} state "running" | "idle" | "stopping"
|
||||
* @property {boolean} hasFrame
|
||||
*/
|
||||
Reference in New Issue
Block a user