MJPEG Stream Viewer
Public Made by Adomby adom
Give it a camera URL and the raw MJPEG stream turns into a pane that fits: aspect kept, frame rate capped, one-click snapshot.
| Name | Last updated |
|---|---|
| bin | 26d ago |
| public | 26d ago |
| screenshots | 26d ago |
| dockbar.json | 26d ago |
| install.sh | 26d ago |
| package.json | 26d ago |
| page.json | 26d ago |
| README.md | 26d ago |
| server.mjs | 26d ago |
| SKILL.md | 26d ago |
| uninstall.sh | 26d ago |
MJPEG Stream Viewer
Give it a camera URL and the raw stream turns into a pane that fits.
A bench cam, a printer cam or a camera box usually serves a raw MJPEG stream (multipart/x-mixed-replace). Opened straight in a Hydrogen webview that stream paints at its native size, so a 4K camera shows one corner of the frame behind two scrollbars, and an http camera on the LAN cannot show inside https Hydrogen at all. This viewer fixes both: it proxies the stream through the container (same origin, so the browser is happy), fits the picture to the pane with the aspect kept, caps the frame rate so a 30 fps 4K source does not flood the browser, shows fps, bandwidth and frame size, and saves a snapshot to the project with one click.

Use it
adom-wiki pkg install adom/mjpeg-stream-viewer
mjpeg-stream-viewer --ai-thread "<your thread>" show --url http://192.168.0.34:3000/stream
show starts the server if needed and opens the viewer in a Hydrogen webview tab in a work pane (never the VS Code pane). Or start it yourself and open the URL it prints:
mjpeg-stream-viewer --ai-thread "<your thread>" serve --print-url --port auto --url http://<camera>/stream
The page takes ?url=<stream>, ?fps=<n> and ?bare=1. Paste a URL in the bar and press Show; Save source keeps it in the dropdown for next time; Fit keeps the whole frame, Fill crops to the pane; Snapshot writes ~/project/screenshots/mjpeg-<timestamp>.jpg. Bare mode (--bare on show, ?bare=1, the corner button on hover, or the b key) hides the header and the bar so the pane is nothing but the picture; it is remembered per browser.


What it does to the stream
- Proxies it:
GET /stream?url=<camera>&fps=<n>fetches the camera from the container and re-serves it same-origin. Only http and https sources; the viewer refuses to proxy itself. - Caps the rate: frames above the cap are dropped in the proxy, so the browser sees at most
fpsframes per second. Default 10; a 4K source at 30 fps is about 11 MB/s raw, and 5 fps of it is about 2 MB/s. - Reads the frame size from the JPEG itself and shows it in the status pills, with a hint when the source is larger than the pane can use.
GET /snapshot.jpg?url=<camera>returns one frame;POST /api/snapshot { aiThread, url }saves one.- A camera that serves a still image instead of a stream works too: the image is fetched once per show.
Status, settings, lifecycle
GET /api/status is the dock's LED source: source, frames, size, each with a state and a hint. Settings persist on the server (GET/POST /settings: maxFps, fit, ttlHours, sources). State-changing calls need the ai-thread name (--ai-thread on the CLI, aiThread in a raw body); anonymous ones are refused with caller_identity_required. The server stops itself after 24 hours idle (--ttl <hours>, --keep-alive), registers itself under ~/.adom/instances/mjpeg-stream-viewer/, and ls lists live instances; stop shuts one down politely.
In a shared workspace
The page ships dockbar.json, so it launches from the dock and a shared Hydrogen workspace can open a camera in a pane:
{ "panel": "app", "app": "adom/mjpeg-stream-viewer", "displayName": "Bench cam", "args": { "url": "http://192.168.0.34:3000/stream", "fps": "5" } }
Limits
A viewer, not a recorder. One source per pane. MJPEG and stills only (no HLS, no WebRTC). The camera must be reachable from the container: true on the desktop and in the office, not true for a cloud container on another network, and the status pill says so.