Playback Background Workers
Playback background workers use the current process’s active room set as their scheduling boundary. An active room here means a room with at least one Realtime connection in the local ConnectionRuntime.
Scheduling Boundary
Section titled “Scheduling Boundary”Playback background workers run on every node. Each node scans only the rooms it is actively serving because those connections are the lifecycle state owned by that process.
This boundary applies to:
- duration probing;
- playback auto-advance;
- playback resource lifecycle work.
Global hot-room statistics are presence and analytics data for room lists, admin views, and metrics. Playback background workers read ConnectionRuntime::active_room_ids().
This rule also applies when reducing background workload. An active room already exists on one or more processes because clients are connected there. Each process scans its own active rooms, and the durable write paths provide the lock or version guard.
Concurrency Control
Section titled “Concurrency Control”The same room can be active on several nodes in a cluster. Duplicate attempts converge through the database and playback state write paths:
- duration probing claims rows with database locks and
SKIP LOCKED; - auto-advance uses playback state transactions and optimistic versions;
- worker input comes from node-local connections, while write correctness comes from persistence.
Leader election fits partition maintenance, cleanup, and other global singleton tasks. Playback lifecycle worker ownership comes from the node-local realtime connection set.
Maintenance Rules
Section titled “Maintenance Rules”Repository queries use SQLx checked macros. After changing these queries, update .sqlx:
cargo sqlx prepare --workspace -- --all-targetsSQLX_OFFLINE=true cargo check --workspace --all-targetsKeep checked macros so CI’s offline SQLx validation continues to cover query shape.
The scheduling input is ConnectionRuntime::active_room_ids(). Presence hot-room queries serve lists, admin views, and metrics. Playback workers use the node-local realtime connection set, while database claims, SKIP LOCKED, and playback-state optimistic versions converge duplicate cross-node attempts.
Room-scoped queries must keep the room_id = ANY(...) filter and the join to the current room_playback_progress.target_hash. The room filter preserves the node-local active-room scheduler boundary. The target hash binds normal media and dynamic playlist playback to the currently selected item.
Finite sequential playlists must persist a stable ended or paused state when there is no next item. Leaving the old playing state in storage makes every scan interval rediscover the same expired source.
End-to-End Verification
Section titled “End-to-End Verification”Playback background changes require real-service verification:
- start the built
synctvbinary so startup, config, migrations, and routes are covered; - keep a room active through a real WebSocket connection;
- use the
synctvCLI to set up rooms, media, and playback state; - use
curlto request direct, proxy, manifest, segment, and Range URLs; - verify rooms without active connections keep duration metadata unchanged;
- verify an active room probes duration and auto-advances once after expiry;
- verify duplicate worker attempts converge to one state advance.
Provider and playback E2E should cover the complete PlaybackResult: Direct URL, Alist, Emby, Jellyfin, Bilibili anonymous playback, RTMP, live proxy, HLS, FLV, every mode, every direct/proxy URL, manifests, indexed segments, subtitles, danmaku, thumbnails, Range, cache miss/hit paths, URL expiry, and resource cleanup. Dynamic playlist coverage also includes returned paths, media resolution, switching, cover/thumbnail routes, and target changes after auto-advance.
Use cached playback results as part of the provider checklist. Cache hits and fresh provider responses must expose the same usable mode names and resolver actions, including MPD/HLS manifests and indexed segment routes.
RTMP and live proxy checks should cover the full lifecycle: create the publish key or live proxy media through the CLI, publish with a real upstream such as ffmpeg, read stream info, fetch HLS playlists and segments, fetch FLV, disconnect the viewer or publisher, and verify idle cleanup releases the stream.
Add synctv CLI support for workflows that require manual verification, then exercise them through CLI plus curl.