/** * Sleep Timer Store (Backend-First Architecture) * * This store reflects sleep timer state from the backend. * All logic is in the Rust backend (PlayerController). * * The backend emits SleepTimerChanged events to update this store. * Components call store methods which invoke the backend API. * * TRACES: UR-026 | DR-029 */ import { writable, derived } from "svelte/store"; import { commands } from "$lib/api/bindings"; export type SleepTimerMode = | { kind: "off" } | { kind: "time"; endTime: number } | { kind: "endOfTrack" } | { kind: "episodes"; remaining: number }; interface SleepTimerState { mode: SleepTimerMode; remainingSeconds: number; } function createSleepTimerStore() { const initialState: SleepTimerState = { mode: { kind: "off" }, remainingSeconds: 0, }; const { subscribe, set } = writable(initialState); return { subscribe, set, // Updated by playerEvents.ts when backend emits SleepTimerChanged event // Methods that invoke the backend API async setTimeTimer(minutes: number): Promise { const endTime = Date.now() + minutes * 60 * 1000; await commands.playerSetSleepTimer({ kind: "time", endTime }); }, async setEndOfTrackTimer(): Promise { await commands.playerSetSleepTimer({ kind: "endOfTrack" }); }, async setEpisodesTimer(count: number): Promise { await commands.playerSetSleepTimer({ kind: "episodes", remaining: count }); }, async cancel(): Promise { await commands.playerCancelSleepTimer(); }, }; } export const sleepTimer = createSleepTimerStore(); /** * Incremented each time the backend reports the time-based sleep timer * expired. The backend stops its own (MPV/ExoPlayer) playback itself, but * HTML5 video on Linux plays in the webview outside the backend's control — * VideoPlayer watches this signal and pauses the