Files
continuum-app/packages/ui
octoturge e928fcf658 Show a printer's camera, from the backend's address not a build-time one
`WebRTCStreamView` has existed since before this commit with no consumer:
nothing rendered it, and the URL it would have used came from a
`go2rtcStreamBase` in `nuxt.config.ts` defaulting to
`http://localhost:1984`.

That default was the real problem. A camera stream lives on a gateway box
inside one farm's network, each farm has its own, and the gateway now
reports its address to the backend when it connects. A compiled-in
constant could only ever be correct for a single-farm install, and its
localhost default pointed every player at the operator's own laptop. It
is gone, replaced by `fetchPrinterStream`, which asks
`GET /printers/:id/stream`.

Absence is the normal case here, not an error: only Bambu's X1/H2 class
publishes a camera URL a gateway can restream, so a P1 or A1 has a camera
and nothing to connect to. The backend 404s for those, this resolves to
null, and the card renders no player - offering one that can never load
would be worse than offering nothing. The lookup also runs only for
printers whose `cameraStream` the fleet list already reports, so a
twenty-printer farm does not make twenty requests to learn that eighteen
of them 404.

`PrinterCard` takes the stream as a prop rather than fetching it, like
`live` above it: `packages/ui` is plain Vue that knows nothing about the
API or about Nuxt, and that is what lets these components render from a
test or the desktop shell with no server behind them.

Three things in `WebRTCStreamView` that would have bitten whoever first
pointed it at a real printer:

- The fetch status was never checked. go2rtc answers a request for a
  stream it does not have with an error page, and handing that to
  `setRemoteDescription` throws about SDP parsing - which sends you
  looking at the wrong layer entirely.
- `connect()` is async inside a watcher with nothing catching it, so a
  failure left the badge on "connecting" forever and showed up only as an
  unhandled rejection in the console.
- Two prop changes in quick succession raced: the slower request could
  finish last and apply its answer to a connection already replaced and
  closed, leaving a black video with a "connected" badge.

No `iceServers`, deliberately and now commented: both ends are on the
same LAN, so host candidates are enough. The same fact is the MVP's
limitation - there is no relay, so this plays for someone on the
printers' network and for nobody else.

`nuxt typecheck` and `vue-tsc --noEmit` both clean.
2026-10-15 23:09:54 +00:00
..