Introduce PlayerAdapter contract; decision logic shared in Rust backend
Establish a decoupled player boundary so UI and backend interact with video through one contract, with the HTML5 (Linux/interim-Android) and native (ExoPlayer) providers as interchangeable primitive-executor adapters. - PlayerAdapter interface + AdapterHost callback bag (adapters/types.ts): the adapter owns only decision-free element PRIMITIVES (seekElement, reloadSource, play/pause, setVolume, selectSubtitle); it never branches on strategy. - Seek/audio-track DECISIONS stay in Rust (player_seek_video / _switch_audio_track return a strategy); the facade dispatches the chosen primitive to the active adapter. Both providers share the one decision path — logic lives once, in Rust. - Facade holds the active adapter; a new ControlCommand PlayerStatusEvent lets backend control (lockscreen/remote/sleep) drive the webview <video> element. - Html5PlayerAdapter resolves the LIVE element via the bridge (fixes play/pause silently no-opping when the element was re-bound). - Do not emit a "stopped" player state on natural end-of-video: it flipped the player/mode to idle mid-handoff and suppressed next-episode auto-advance under a sleep timer. Jellyfin progress reporting is preserved; the backend's on_video_playback_ended owns the transition. - VideoPlayer net -300 lines (strategy/HLS-reload logic relocated to the adapter). - Adds 20 adapter unit tests; existing suites stay green (vitest 457, cargo 416). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1996,7 +1996,15 @@ export type PlayerStatusEvent =
|
||||
* frontend owns the two-step remote->local transfer (it must reload the
|
||||
* media item locally), so the native side only signals intent here.
|
||||
*/
|
||||
{ type: "remote_disconnect_requested" }
|
||||
{ type: "remote_disconnect_requested" } |
|
||||
/**
|
||||
* Backend-originated control command targeting the active frontend player
|
||||
* adapter (the HTML5 <video> that lives in the webview, which Rust cannot
|
||||
* drive directly). Emitted by control paths like the sleep timer, lockscreen,
|
||||
* or remote so they can pause/play/seek/stop the webview element.
|
||||
* `playerEvents.ts` routes this to the active PlayerAdapter via the facade.
|
||||
*/
|
||||
{ type: "control_command"; action: string; position: number | null }
|
||||
/**
|
||||
* Result of creating a playlist
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user