You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/docs/playback-background-workers.md

91 lines
6.3 KiB
Markdown

# Playback Background Workers
Playback background workers use the current process's active room set as their scheduling boundary.
An active room for these workers means a room with at least one realtime connection registered in the local `ConnectionRuntime`. It is intentionally local-node state. In cluster deployments, the same room may be active on more than one node, and each node may attempt the same background work for that room.
This is a deliberate ownership boundary:
- a room with local realtime connections already has lifecycle state on the current node;
- background playback work only needs rooms this node can observe locally;
- global hot-room stats are presence/analytics data for lists, admin views, and metrics.
These workers run on every node so rooms connected to any replica receive duration probing, auto-advance, and playback resource lifecycle work.
Correctness for duplicate attempts belongs at the storage and state-transition layer:
- duration probing claims rows with database locks and `SKIP LOCKED`, so one worker owns a probe attempt;
- playback auto-advance uses the playback state transaction and optimistic version checks, so concurrent attempts converge to one committed state transition;
- workers use local realtime ownership, which keeps lifecycle work tied to rooms the node can observe.
Use shared presence or global hot-room queries for user-facing room lists and cross-node statistics. Use `ConnectionRuntime::active_room_ids()` for per-node playback lifecycle work. Use leader election for true singleton tasks such as partition management and cleanup.
This rule also applies when optimizing for fewer background scans. An active room already exists on one or more processes because clients are connected there. Each process scans its own active rooms, and the write paths provide the lock or version guard. Keeping analytics data out of lifecycle scheduling keeps live resource ownership easy to reason about.
## SQLx Query Cache
Repository queries for playback background work use SQLx checked macros. When any of these queries change, update `.sqlx` with `cargo sqlx prepare --workspace -- --all-targets`, then verify with `SQLX_OFFLINE=true cargo check --workspace --all-targets`.
Keep checked SQLx macros in repository code and treat `.sqlx` updates as part of the SQL change.
The 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 local active-room scheduler boundary. The target hash binds media and dynamic playlist playback to the currently selected item.
Auto-advance must move state through the playback-state transaction and version write path. When several nodes scan the same active room, exactly one state transition should commit.
Finite sequential playlists persist a stable ended or paused state when there is no next item. The persisted end state lets later scan intervals skip the already-finished source.
## End-to-End Coverage
Playback background changes require manual end-to-end checks in addition to unit tests:
- start a built `synctv` binary so startup, config, migrations, and routes are covered;
- keep a room active through a real WebSocket connection;
- use `synctv` CLI for setup/control and `curl` for HTTP/media assertions;
- verify duration probing initializes metadata only for local active rooms;
- verify auto-advance advances once when multiple workers can see the same room;
- verify provider playback URLs by actually requesting direct/proxy/manifest/segment URLs;
- cover dynamic playlist media resolution, path return, switching, cover/thumbnail paths, and auto-advance target changes.
Provider E2E coverage should include Direct URL, Alist, Emby, Jellyfin, Bilibili anonymous playback, RTMP, live proxy, HLS, FLV, cache hit/miss, URL expiry, and cleanup behavior.
The scheduling source for these checks is `ConnectionRuntime::active_room_ids()`.
Presence hot-room queries are analytics inputs for lists and metrics. Playback
workers use local realtime ownership, and storage/state transitions provide
cross-node convergence.
Provider checks must request every mode and auxiliary URL returned in
`PlaybackResult`: upstream URLs, `proxy_*` URLs, manifests, indexed segments,
subtitles, danmaku, thumbnails, Range responses, cache hit/miss paths, expiry
handling, and live resource cleanup. Add `synctv` CLI coverage for workflows
that require manual verification, then exercise them through CLI plus `curl`.
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 prove the full lifecycle: create the publish
key or live proxy media through CLI, publish with a real upstream such as
`ffmpeg`, request stream info, fetch HLS playlists and segments, fetch FLV, stop
the viewer or publisher, and observe idle cleanup releasing the stream.
## Provider Playback Contract
Every Provider signs and finalizes playback inside `generate_playback`, then
serves every generated proxy action through its resolver.
`VersionedPlayback` is the cache payload plus proxy lookup index. The `version`
maps every generated `/stream`, `/m3u8`, `/mpd`, subtitle, danmaku, thumbnail,
FLV, and HLS segment URL back to the provider-owned playback result. Response
finalization applies the provider's signer/rewrite callback for the current
request. Policy still lives in that provider: mode names, default mode, header
exposure, direct/proxy shape, manifest metadata, and live lifecycle data.
| Provider | Coverage |
| --- | --- |
| Direct URL | upstream mode, `proxy_*` sibling, proxy default for header-bound sources, HLS segment rewriting, Range |
| Alist | direct/transcode modes, proxy siblings, thumbnail, subtitle, HLS segment, stream resolver |
| Emby/Jellyfin | upstream/transcode modes, proxy siblings, allowed token headers, stream/HLS/subtitle proxy |
| Bilibili | anonymous playback, DASH/MPD proxy default, proxied manifest segments, subtitle, danmaku, thumbnail, cache metadata |
| RTMP | publish key, stream info, HLS playlist/segment, FLV, provider-proxy URL, idle cleanup |
| live proxy | external RTMP/HTTP-FLV pull, HLS/FLV provider-proxy URL, publisher registration and unregister cleanup |