From a968f708b33be0fd48b5a078ce7a8749ca367288 Mon Sep 17 00:00:00 2001 From: KoalaDev <6156589+Shik3i@users.noreply.github.com> Date: Thu, 25 Jun 2026 23:27:19 +0200 Subject: [PATCH] feat(protocol): add host control mode events & constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 1 of host control mode. Adds the protocol surface only — no behavior yet: - EVENTS.SET_CONTROL_MODE (client->server: host sets mode) - EVENTS.CONTROL_MODE (server->client: mode changed) - CONTROL_MODES { EVERYONE, HOST_ONLY } Purely additive; old clients ignore the new events. Propagated to extension/shared via build-extension. Also records the audited design decisions in the edge-case log. Co-Authored-By: Claude Opus 4.8 --- docs/host-control-mode-EDGECASES.md | 28 ++++++++++++++++++++++------ shared/constants.js | 12 ++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/docs/host-control-mode-EDGECASES.md b/docs/host-control-mode-EDGECASES.md index 884f0dd..b1007a4 100644 --- a/docs/host-control-mode-EDGECASES.md +++ b/docs/host-control-mode-EDGECASES.md @@ -204,9 +204,25 @@ Host hammering the toggle → many `SET_CONTROL_MODE`. Covered by existing | Disney+ / DRM | | | | | | | Jellyfin / Emby | | | | | | -## 8. Open decisions to lock before/while coding -- [ ] Intent-classifier signal set (EC-9) — design first. -- [ ] Host grace period on disconnect, yes/no + duration (EC-10). -- [ ] Desync semantics: full solo vs receive-only (EC-12). -- [ ] Snap-back cooldown duration + retry cap (EC-4). -- [ ] Does host-only apply to live streams or degrade (EC-15)? +## 8. Decisions (audited) +- [x] **Intent-classifier (EC-9):** A `pause`/`seek` is **involuntary** if ANY of: + `readyState < 3`, `video.seeking`, a `waiting` fired < ~1500ms ago (`isBuffering` + flag), inside `visibilityGraceUntil`, OR no own-tracked user gesture + (`Date.now() - lastUserGestureAt < 1000`, via capturing keydown/pointerdown — do + NOT use sticky `navigator.userActivation.hasBeenActive`). Bias: only *clearly* + involuntary is ignored; everything else = deliberate. **Note:** in host-only the + guest never broadcasts anyway, so this only decides dialog-vs-silent — a UX call, + not a room-integrity call. Start simple, tune later. +- [x] **Host grace on disconnect (EC-10): NOT in v1.** Immediate fallback to + `everyone` (EC-3). A grace window risks a multi-second hard-lock if the host never + returns. Revisit as polish once the core flow works. +- [x] **Desync semantics (EC-12): full solo.** Ignore host play/pause/seek while + desynced; Resync snaps to host position + adopts host play/pause state. Desync + auto-clears on new media/episode. Requires a persistent, obvious "You are desynced" + UI. +- [x] **Snap-back cooldown (EC-4): until-settled, not fixed.** Suppress re-trigger + until `readyState>=3 && playing && |Δt| Client: current room state ERROR: "error", + // Host Control Mode + SET_CONTROL_MODE: "set_control_mode", // Client -> Server: host sets room control mode ('everyone' | 'host-only') + CONTROL_MODE: "control_mode", // Server -> Client: room control mode changed { controlMode, hostPeerId } + // Media Control PLAY: "play", PAUSE: "pause", @@ -57,6 +61,14 @@ export const EVENTS = { PONG: "pong" // server responds with same { t } for client RTT calculation }; +// Room control modes (Host Control Mode feature). +// NOTE: content.js does not import this module — it uses the string literals +// 'everyone' / 'host-only' directly. Keep these values in sync there. +export const CONTROL_MODES = { + EVERYONE: 'everyone', // default: anyone can play/pause/seek for the room + HOST_ONLY: 'host-only' // only the host drives the room +}; + export const HEARTBEAT_INTERVAL = 15000; // 15s export const FORCE_SYNC_TIMEOUT = 8500; // 8.5s timeout for force sync ACKs (must be > content.js poll timeout of 8s) export const EPISODE_LOBBY_TIMEOUT = 60000; // 60s timeout for episode lobby