Files
Psychotoxical-psysonic/src/features/playback/store/pausedRestorePrepare.ts
T
Psychotoxical 9058abd340 refactor(lib): extract Track/QueueItemRef domain model to lib/media/trackTypes
Track is the app's normalized song model and QueueItemRef its thin queue
identity -- consumed app-wide (queue, cover, context menus, sharing, lucky-mix,
library browse), not a playback-internal detail. They lived in
features/playback/store/playerStoreTypes, forcing ~115 core/feature files to
reach into the playback feature for the central media type.

Move the two interfaces to lib/media/trackTypes; playerStoreTypes keeps
PlayerState and now imports them from lib. 115 importers repointed (mixed
'PlayerState, Track' lines split so PlayerState stays). Pure type move (erased at
runtime). tsc 0, lint 0.
2026-06-30 17:16:56 +02:00

76 lines
2.7 KiB
TypeScript

import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
import { getQueueTracksView } from '@/features/playback/store/queueTrackView';
import { scheduleHotCachePrefetchForTrack } from '@/hotCachePrefetch';
import { getPlaybackCacheServerKey } from '@/features/playback/utils/playback/playbackServer';
import { useAuthStore } from '@/store/authStore';
import { bumpPlayGeneration, getPlayGeneration } from '@/features/playback/store/engineState';
import { engineLoadTrackAtPosition } from '@/features/playback/store/engineLoadTrackAtPosition';
import { emitPlaybackProgress } from '@/features/playback/store/playbackProgress';
import { promoteCompletedStreamToHotCache } from '@/features/playback/store/promoteStreamCache';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { setSeekFallbackVisualTarget } from '@/features/playback/store/seekFallbackState';
/** Push restored position into the store + progress channel so the seekbar paints immediately. */
export function applyRestoredPlaybackVisual(track: Track, atSeconds: number): void {
const dur = track.duration > 0 ? track.duration : 0;
const seconds = Math.max(0, atSeconds);
const progress = dur > 0 ? Math.min(1, seconds / dur) : 0;
usePlayerStore.setState({ currentTime: seconds, progress, buffered: 0 });
emitPlaybackProgress({
currentTime: seconds,
progress,
buffered: 0,
buffering: false,
});
if (seconds > 0.05) {
setSeekFallbackVisualTarget({
trackId: track.id,
seconds,
setAtMs: Date.now(),
});
}
}
/**
* After `getPlayQueue` restores a paused session: show the saved seek position,
* prefetch bytes for the current track, and load the engine paused at that spot
* so the next Play is a warm `audio_resume`.
*/
export function preparePausedRestoreOnStartup(
track: Track,
queueItems: QueueItemRef[],
queueIndex: number,
atSeconds: number,
): void {
const player = usePlayerStore.getState();
if (player.isPlaying || player.currentRadio) return;
applyRestoredPlaybackVisual(track, atSeconds);
scheduleHotCachePrefetchForTrack(track, getPlaybackCacheServerKey());
const generation = bumpPlayGeneration();
void (async () => {
const auth = useAuthStore.getState();
const promoteSid = getPlaybackCacheServerKey();
if (auth.hotCacheEnabled && promoteSid) {
await promoteCompletedStreamToHotCache(
track,
promoteSid,
auth.hotCacheDownloadDir || null,
);
}
if (getPlayGeneration() !== generation) return;
if (usePlayerStore.getState().isPlaying) return;
const queue = getQueueTracksView(queueItems, [track]);
engineLoadTrackAtPosition({
generation,
track,
queue,
queueIndex,
atSeconds,
wantPlaying: false,
});
})();
}