/** * Playback mode store - Thin wrapper over Rust PlaybackModeManager * * Manages transitions between Local (device playback) and Remote (controlling * another Jellyfin session) playback modes. * * Most business logic moved to Rust (src-tauri/src/playback_mode/mod.rs) * * TRACES: UR-010 | IR-012 | DR-037 */ import { writable, get, derived } from "svelte/store"; import { commands } from "$lib/api/bindings"; import { sessions, selectedSession } from "./sessions"; import { auth } from "./auth"; import { ticksToSeconds } from "$lib/utils/playbackUnits"; export type PlaybackMode = "local" | "remote" | "idle"; interface PlaybackModeState { mode: PlaybackMode; remoteSessionId: string | null; isTransferring: boolean; transferError: string | null; } interface RustPlaybackMode { type: "local" | "remote" | "idle"; session_id?: string; } function createPlaybackModeStore() { const initialState: PlaybackModeState = { mode: "idle", remoteSessionId: null, isTransferring: false, transferError: null, }; const { subscribe, update } = writable(initialState); // Track ongoing transfer promise to allow cancellation let currentTransferAbort: (() => void) | null = null; /** * Refresh mode from Rust backend */ async function refreshMode(): Promise { try { const rustMode = (await commands.playbackModeGetCurrent()) as RustPlaybackMode; update((s) => ({ ...s, mode: rustMode.type, remoteSessionId: rustMode.type === "remote" ? rustMode.session_id || null : null, })); } catch (error) { console.error("Failed to get playback mode:", error); } } /** * Set playback mode directly (for internal use) */ function setMode(mode: PlaybackMode, remoteSessionId: string | null = null): void { update((s) => ({ ...s, mode, remoteSessionId })); } /** * Transfer playback from local to remote session * Rust backend handles all the heavy lifting: * - Sends play command with StartPositionTicks * - Polls remote session until track loads * - Stops local playback */ async function transferToRemote( sessionId: string | null | undefined, currentPosition?: number, ): Promise { console.log("[PlaybackMode] Transferring to remote session:", sessionId); update((s) => ({ ...s, isTransferring: true, transferError: null })); let aborted = false; currentTransferAbort = () => { aborted = true; update((s) => ({ ...s, isTransferring: false, transferError: "Transfer cancelled", })); }; try { // Pass the caller's current local position so the remote resumes where we // are. The backend can't reliably read this itself: on Linux video plays in // the HTML5