openvibers/openvibe.bot Commits

Bot docs: the design for moving Live's stream controls into Bot (T15 R9). (#37)

OP OpenVibers committed 7 Oct 2026 · 0262d8e016 · 1 file, +65 −0

Design for plan T15 row R9: moving OpenVibe.Live's stream controls into Bot (docs/live-controls-migration.md). What Live has (buttons, hold keys, video click, ONVIF, whitelist; production counts), where each piece goes in Bot, the six things to build (contracts button/point kinds, robot-local profiles, gate and panel, the Node relay plugin, a one-time conversion, Live embed then deletion) and the cutover order. Docs only.

Changes

ddiff --git a/docs/live-controls-migration.md b/docs/live-controls-migration.md
nnew file mode 100644
iindex 0000000..1e12b19
---- /dev/null
++++ b/docs/live-controls-migration.md
@@@ -0,0 +1,65 @@
++# Moving OpenVibe.Live's stream controls into Bot (plan T15, row R9)
++
++Status: design, 2026-10-07. Live still serves its controls; nothing here has been built yet.
++
++## What Live has today
++
++Live's "camp controls" (`server/controls/`, `public/js/controls.js`, `public/js/onvif-dashboard.js`, about 3,750
++lines) let viewers drive a streamer's hardware from the channel page:
++
++- **Buttons**: a viewer presses a labelled button (`{type:'command', command}`), optionally bound to a key, with a
++ per-button cooldown; the server relays the command string to the streamer's **hardware client** (usually a
++ Raspberry Pi script) over a WebSocket authenticated with the stream key.
++- **Hold keys**: `key_down`/`key_up` become `key_held`/`key_released` on the hardware client.
++- **Video click**: a click on the video sends normalised coordinates (`video_click`), with its own cooldown.
++- **ONVIF cameras**: pan/tilt/zoom moves sent by the server itself to a configured camera.
++- **Access**: controls on/off per stream, login required, an optional whitelist, a global per-viewer rate limit.
++- **Data** (production, 2026-10-07): 4 `control_configs` (users 1, 80, 275), their `control_config_buttons`,
++ 11,725 `stream_controls` rows (each stream copies its owner's buttons; 1,390 streams), `control_whitelist`, and
++ `cameras`/`camera_profiles`/`camera_presets` (0 profiles, 0 presets).
++
++## Where each piece goes in Bot
++
++| Live | Bot |
++|---|---|
++| A control config (an owner's set of buttons) | A **robot** owned by the same person, with a **robot-local profile** built from the buttons |
++| A button (`command`, label, key, cooldown) | A `button` command (new kind, below): `commands.button.names.<name> = { label, key, cooldown_ms }`, rendered by a `buttons` widget |
++| Hold keys | The same `button` command with `hold: true` (pressed → `{kind:'button', name, state:'down'}`, released → `up`), under the device-side deadman like every hold control |
++| Video click | A `point` command (new kind): `{ kind: 'point', x, y }` with `x, y ∈ [0, 1]`, rendered as a click layer on the camera tile, with its own cooldown |
++| ONVIF movement | Bot's server-side ONVIF camera (`camera.onvif`, `BOT_ONVIF_CAMERAS`), already built |
++| Controls on/off, login required | The robot's access policy (`private` / `invite` / `queue`); controls are never anonymous |
++| Whitelist | Operators added by @username (built) |
++| Per-viewer rate limit, cooldowns | The command gate's per-robot limits and cooldowns (built), plus the per-button `cooldown_ms` |
++| The hardware client and its stream-key socket | The OpenVibe **Node** paired to the robot, running a **relay** plugin (below); the stream key stops being a robot credential |
++| The controls panel under the video | Bot's panel embedded on the Live channel page (`/panel/:id/embed`, `LIVE_BOT_EMBED`), already built |
++
++## What has to be built
++
++1. **Contracts** (minor release): `bot.command@1` gains the kinds `button` (`name`, optional `state: down|up`) and
++ `point` (`x`, `y` in `[0, 1]`); `bot.robot-profile@1` gains `commands.button.names` and `commands.point`, and the
++ widget types `buttons` and `video_click`. Additive; existing profiles stay valid.
++2. **Bot — robot-local profiles**: a robot may carry its own profile (`robot_profiles` row owned by the robot,
++ `profile_id = 'local.<rob_…>'`) instead of a catalogue one; the owner edits its buttons on the robot page (a
++ plain form: name, label, key, cooldown, hold). Validated by the same profile schema and registries.
++3. **Bot — the gate and the panel**: the gate accepts `button`/`point` only for names and shapes the profile
++ declares; the panel renders `buttons` (keyboard bindings, hold behaviour) and `video_click` (a click layer on the
++ camera tile, normalised coordinates).
++4. **Node — the relay plugin**: forwards `button`/`point` commands to the owner's local script in the shape Live's
++ hardware clients already understand — one JSON line per command on stdout of a supervised child process, or a
++ loopback WebSocket that speaks Live's `{type:'command'|'key_held'|'key_released'|'video_click'}` messages — so an
++ existing Pi script keeps working with a one-line change of where it connects. Local policy and the kill switch
++ apply as for every plugin.
++5. **The conversion** (a script in Bot, run once by the operator with a JSON export from Live's database): for each
++ `control_configs` row, create the robot for the owner's Network subject (Live `users.subject_id`), a robot-local
++ profile from its buttons, operators from the whitelist, and the `stream_controls` overrides of that owner's most
++ recent stream where they differ. Idempotent on the Live config id. The owner then pairs a Node (the five-minute
++ path) and moves the script onto the relay plugin.
++6. **Live**: `LIVE_BOT_EMBED=1`; the channel page shows the robot's embedded panel for a stream whose owner has a
++ robot; then delete `server/controls/*`, the hardware WebSocket path and its stream-key auth, the controls tables
++ and `public/js/controls.js` / `onvif-dashboard.js` (the embed stays).
++
++## Order and cutover
++
++Contracts → robot-local profiles → gate/panel → relay plugin → conversion run (owners notified) → Live embed on →
++owners re-pair → Live deletion. The deletion waits for the three converted owners' robots to be paired, because the
++hardware clients are physical devices only their owners can move; it does not wait on a date.