Files
jellytau/src/lib/components/player/backgroundAudioHandoff.ts
T
dtourolle 3fbf6afdbc Background-audio handoff for video + repository/player refactor
Hand video playback off to a native audio-only stream when the app is
backgrounded or locked, with no on-device video decode (UR-040). Adds
player_enter/exit_background_audio commands, an audio-only stream URL
for video items across the repository layer, and the frontend handoff
state machine wired into VideoPlayer. Includes accompanying
repository/offline/player refactoring and regenerates the traceability
matrix.
2026-07-22 21:52:07 +02:00

58 lines
1.8 KiB
TypeScript

/**
* Pure helpers for the video → background-audio handoff (UR-040).
*
* TRACES: UR-040 | DR-052 | UT-060
*
* Kept free of Svelte/DOM so the handoff arithmetic and state transitions are
* unit-testable without mounting the player. The component
* (VideoPlayer.svelte) owns the actual `<video>` teardown and IPC calls.
*/
/**
* Absolute playback position to resume the audio stream at.
*
* Transcoded HLS playback tracks time as `videoElement.currentTime + seekOffset`
* (the element resets to 0 after each transcode reload; `seekOffset` carries the
* cumulative offset). Background audio must resume at that ABSOLUTE position, so
* both terms are summed here — mirroring the `effectiveTime` used elsewhere in
* the player.
*/
export function computeHandoffPosition(elementCurrentTime: number, seekOffset: number): number {
const pos = elementCurrentTime + seekOffset;
return pos > 0 ? pos : 0;
}
/**
* The handoff state. `wasPlaying` is captured on the way out so play/pause is
* restored when the app returns to the foreground.
*/
export interface BackgroundAudioState {
active: boolean;
wasPlaying: boolean;
}
export const initialHandoffState: BackgroundAudioState = {
active: false,
wasPlaying: false,
};
/**
* Whether a background signal should trigger the audio handoff right now.
* Only when the toggle is on and we're not already handed off.
*/
export function shouldEnterBackgroundAudio(
toggleOn: boolean,
state: BackgroundAudioState
): boolean {
return toggleOn && !state.active;
}
/**
* Whether a foreground signal should trigger the return to WebView video.
* Only when we actually handed off (regardless of the current toggle value, so
* turning the toggle off while backgrounded still returns cleanly).
*/
export function shouldExitBackgroundAudio(state: BackgroundAudioState): boolean {
return state.active;
}