/** * Where a printer's camera can be watched, from `GET /printers/:id/stream`. */ export interface PrinterStream { streamUrl: string; cameraId: string; } /** * Asks the backend where a printer's camera can be watched, or `null` if it * cannot. * * This replaces the build-time `go2rtcStreamBase` that used to live in * `nuxt.config.ts`. A single compiled-in URL can only ever be correct for a * one-farm deployment: the stream lives on a gateway box inside a farm's own * network, each farm has its own, and the gateway reports its address to the * backend when it connects. So the address has to be asked for at runtime, * per printer, rather than baked in. * * A plain function and not a `useAsyncData` wrapper despite the file it lives * in: the caller needs one of these per printer, discovered from a list that * itself arrives asynchronously, and Nuxt's data composables may only be * called while a component is setting up. A loop over a resolved list is not * that. It takes the fetcher as an argument for the same reason - the caller * builds it in setup, where `useApiFetch` is valid. * * **Absence is the normal case, not an error.** Only Bambu's X1/H2 class * publishes a camera URL a gateway can restream; a P1 or A1 reports that it * has a camera and gives nothing to connect to. The backend answers 404 for * those, so this answers `null` and the caller shows no player at all - * rendering one that can never load would be worse than rendering nothing. * * It is also a LAN address with no relay in front of it, so the stream plays * only for someone on the same network as the printers. That is the right * trade-off for an operator standing in the workshop, and a real limitation * for anyone else. */ export async function fetchPrinterStream( apiFetch: ReturnType, printerId: string, ): Promise { try { return await apiFetch(`/printers/${printerId}/stream`); } catch (err: unknown) { // A 404 means "this printer has no watchable camera", which is expected // often enough that treating it as a failure would put an error state on // most cards in a mixed fleet. Anything else is a real problem: logged // and then treated as "no stream", because a broken camera lookup should // not take the fleet overview down with it. const status = (err as { statusCode?: number }).statusCode; if (status !== 404) { console.warn(`could not look up the camera stream for printer ${printerId}`, err); } return null; } }