From 334455c8ab793e2303e5e3669fa6deba179412be Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Sun, 14 Jun 2026 10:45:30 -0500 Subject: M9-precursor: v1 Scrypted Script (snapshot-rate end-to-end) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scrypted/scrypted-viewport.ts — single-file TypeScript script for Scrypted's Scripts plugin. Binds N viewports to N Scrypted cameras and implements the full Scrypted side of the protocol: Outbound (Scrypted -> device): - POST /config on script load + every 5 min (re-syncs after device reboot or DHCP renumber). - POST /state {wake} when a bound camera fires an event or when the device's own POST /state {wake} arrives (operator tap). - POST /frame at FRAME_INTERVAL_MS (1 fps default) via camera.takePicture({picture:{width,height}}). - POST /state {sleep} when the Scrypted-side per-stream timer expires. Inbound (device -> Scrypted POST /state): - {state:wake} -> startStream() (cancels pending sleep timer + any prior interval, then begins pushing frames). - {state:sleep} -> stopStream(sendSleep=false) (no echo back). - Unknown viewport -> 404. Bad body -> 400. Race rules from the spec are honored: - Every callback cancels prior pending state for that viewport before applying the new state (cancelPendingSleep semantics). - startStream / stopStream are idempotent — re-entering is safe. - On /frame returning 409, stream stops immediately without echoing /state {sleep} back (the device already slept). Configuration is a single BINDINGS array at the top of the file — edit name/host/cameraId/orientation per viewport and save in the Scrypted UI. No package install. Tuning constants (IDLE_TIMEOUT_MS, FRAME_INTERVAL_MS, REREGISTER_INTERVAL_MS, etc.) are right above the class. scrypted/README.md walks through Scripts-plugin install, the BINDINGS schema, what each constant controls, an event-by-event description of the script's behavior, the v1 limitations (snapshot-rate, manual IP, camera must respect picture dims), and an end-to-end smoke test. Top-level README's Scrypted Integration section now links the script file and the install README directly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'README.md') diff --git a/README.md b/README.md index 4fe081d..c4f4406 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ Both use a small embedded bitmap font. No LVGL, no general text engine. The Scrypted side is **code, not configuration** — Scrypted has no built-in concept of a network framebuffer. The code is small and lives inside Scrypted: -- **v1**: a Scrypted Script (in the Scripts plugin) — listens for camera events, calls `takePicture()`, POSTs the JPEG to `/frame`. Exposes a `POST /state` handler at the plugin's endpoint root (e.g. `http://scrypted.local:11080/endpoint/scrypted-viewport/state`) via the EndpointManager. ~50 lines of TypeScript, no package install. +- **v1** (in this repo at [`scrypted/scrypted-viewport.ts`](scrypted/scrypted-viewport.ts), install instructions in [`scrypted/README.md`](scrypted/README.md)): a Scrypted Script (in the Scripts plugin) — listens for camera events, calls `takePicture()`, POSTs the JPEG to `/frame`. Exposes a `POST /state` handler at the plugin's endpoint root (e.g. `http://scrypted.local:11080/endpoint/scrypted-viewport/state`) via the EndpointManager. ~250 lines of TypeScript, no package install. - **Next milestone after v1 (`/stream`)**: add `POST /stream` with `multipart/x-mixed-replace` chunked body for live frame rates. Scrypted side becomes a small custom plugin using FFmpeg via `MediaManager` to pipe MJPEG. `/frame` stays for snapshots and debug. Either way, no Scrypted core changes and no external service. -- cgit v1.2.3