feat(web/m7-364): queue persistence write/restore on player store
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
import type { TrackRef, RadioResponse } from '$lib/api/types';
|
import type { TrackRef, RadioResponse } from '$lib/api/types';
|
||||||
import { api } from '$lib/api/client';
|
import { api } from '$lib/api/client';
|
||||||
|
import { user } from '$lib/auth/store.svelte';
|
||||||
|
import { readPersistedQueue, writePersistedQueue } from './persisted';
|
||||||
|
|
||||||
export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
|
export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
|
||||||
export type RepeatMode = 'off' | 'all' | 'one';
|
export type RepeatMode = 'off' | 'all' | 'one';
|
||||||
@@ -319,6 +321,20 @@ export function closeQueueDrawer(): void {
|
|||||||
_queueDrawerOpen = false;
|
_queueDrawerOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function restoreQueue(userId: string): void {
|
||||||
|
if (!userId) return;
|
||||||
|
const persisted = readPersistedQueue(userId);
|
||||||
|
if (!persisted) return;
|
||||||
|
if (persisted.queue.length === 0) return;
|
||||||
|
_queue = persisted.queue;
|
||||||
|
_index = Math.max(0, Math.min(persisted.index, persisted.queue.length - 1));
|
||||||
|
_position = persisted.position;
|
||||||
|
_duration = 0;
|
||||||
|
_state = 'paused';
|
||||||
|
_error = null;
|
||||||
|
_radioSeedId = null;
|
||||||
|
}
|
||||||
|
|
||||||
// M4c: auto-refresh radio queue when 80% consumed.
|
// M4c: auto-refresh radio queue when 80% consumed.
|
||||||
// Fires whenever the consumption ratio crosses 80% AND the queue was
|
// Fires whenever the consumption ratio crosses 80% AND the queue was
|
||||||
// seeded by playRadio() AND no refresh is currently in-flight.
|
// seeded by playRadio() AND no refresh is currently in-flight.
|
||||||
@@ -355,3 +371,45 @@ $effect.root(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Persistence (M7 #364): split-effect shape. Queue/index changes write
|
||||||
|
// immediately (rare, user-driven); position changes throttle to once
|
||||||
|
// per 5s during steady playback. See spec section "Persistence write
|
||||||
|
// effect" for the alternative debounce shape.
|
||||||
|
$effect.root(() => {
|
||||||
|
let lastPositionWriteAt = 0;
|
||||||
|
|
||||||
|
// Immediate write on queue or index changes.
|
||||||
|
$effect(() => {
|
||||||
|
const userId = user.value?.id;
|
||||||
|
if (!userId) return;
|
||||||
|
// Read these to register the dependency. Position is also read but
|
||||||
|
// its standalone changes are handled by the second effect below.
|
||||||
|
const _q = _queue;
|
||||||
|
const _i = _index;
|
||||||
|
void _q;
|
||||||
|
void _i;
|
||||||
|
writePersistedQueue(userId, {
|
||||||
|
queue: _queue,
|
||||||
|
index: _index,
|
||||||
|
position: _position
|
||||||
|
});
|
||||||
|
lastPositionWriteAt = Date.now();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Throttled write on position changes.
|
||||||
|
$effect(() => {
|
||||||
|
const userId = user.value?.id;
|
||||||
|
if (!userId) return;
|
||||||
|
const _p = _position;
|
||||||
|
void _p;
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastPositionWriteAt < 5000) return;
|
||||||
|
writePersistedQueue(userId, {
|
||||||
|
queue: _queue,
|
||||||
|
index: _index,
|
||||||
|
position: _position
|
||||||
|
});
|
||||||
|
lastPositionWriteAt = now;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user