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:
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Player adapter factory + public exports.
|
||||
*
|
||||
* `createAdapter` selects the concrete PlayerAdapter for the current platform.
|
||||
* It is the single place that encodes the INTERIM Android override: the Rust
|
||||
* backend may report a native ExoPlayer backend, but native Android video
|
||||
* rendering is blocked upstream (tauri#10152 — transparent webview / SurfaceView
|
||||
* compositing), so we render Android video through the HTML5 adapter for now.
|
||||
* When that upstream limitation is resolved, flip this to honor `backendKind`.
|
||||
*
|
||||
* TRACES: UR-003 | DR-004
|
||||
*/
|
||||
|
||||
import { Html5PlayerAdapter, type Html5ElementBridge } from "./html5Adapter";
|
||||
import { NativePlayerAdapter } from "./nativeAdapter";
|
||||
import type { AdapterHost, PlayerAdapter } from "./types";
|
||||
|
||||
export type { PlayerAdapter, AdapterHost, PlayerLoadOptions, SubtitleTrackInput } from "./types";
|
||||
export type { Html5ElementBridge } from "./html5Adapter";
|
||||
export { Html5PlayerAdapter } from "./html5Adapter";
|
||||
export { NativePlayerAdapter } from "./nativeAdapter";
|
||||
|
||||
/** What the Rust `player_play_item` response says it chose. */
|
||||
export type BackendKind = "html5" | "native";
|
||||
|
||||
export interface CreateAdapterArgs {
|
||||
/** Backend kind reported by `player_play_item` (`useHtml5Element`). */
|
||||
backendKind: BackendKind;
|
||||
host: AdapterHost;
|
||||
/** Required for the HTML5 adapter; ignored by the native adapter. */
|
||||
bridge?: Html5ElementBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the adapter for this platform/stream.
|
||||
*
|
||||
* INTERIM: always returns the HTML5 adapter, because the native surface is not
|
||||
* visible through the webview on current Tauri (see module docs). The bridge is
|
||||
* therefore required.
|
||||
*/
|
||||
export function createAdapter({ backendKind, host, bridge }: CreateAdapterArgs): PlayerAdapter {
|
||||
// INTERIM OVERRIDE: force HTML5 rendering even when the backend reports native.
|
||||
const effectiveKind: BackendKind = "html5";
|
||||
|
||||
if (effectiveKind === "html5") {
|
||||
if (!bridge) {
|
||||
throw new Error("createAdapter: Html5ElementBridge is required for the HTML5 adapter");
|
||||
}
|
||||
return new Html5PlayerAdapter(host, bridge);
|
||||
}
|
||||
|
||||
// Reached only once the interim override is lifted (native Android unblocked).
|
||||
void backendKind;
|
||||
return new NativePlayerAdapter(host);
|
||||
}
|
||||
Reference in New Issue
Block a user