feat(playlists): system playlists default to shuffle on tile play
Closes Fable #412 (For You force-refresh on play) and #413 (shuffle-on-play default for system playlists). Web (PlaylistCard.svelte + player/store.svelte.ts): - Drop the refreshForYou() call from the play handler. The daily 03:00 user-local snapshot is what plays now. Stops burning server compute on every press and stops swapping the playlist out from under the user. - Generalize the kebab affordance to render for any system playlist (was Discover-only). Adds "Refresh For You" as an explicit replacement so users can still force a regen when they want one. - Extend playQueue(tracks, startIndex, { shuffle? }) to Fisher-Yates the queue when shuffle:true. PlaylistCard passes shuffle:true for any non-null system_variant. Flutter (player_provider.dart + playlists/widgets/playlist_card.dart): - playTracks now accepts shuffle:bool. When true, picks a random starting index and enables AudioServiceShuffleMode.all after setQueueFromTracks. PlaylistCard passes shuffle:playlist.isSystem. User playlists keep linear order. Detail-screen play buttons are unchanged for now (follow-up if user requests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,15 +62,34 @@ export function registerAudioEl(el: HTMLAudioElement | null): void {
|
||||
_audioEl = el;
|
||||
}
|
||||
|
||||
export function playQueue(tracks: TrackRef[], startIndex = 0): void {
|
||||
export function playQueue(
|
||||
tracks: TrackRef[],
|
||||
startIndex = 0,
|
||||
opts: { shuffle?: boolean } = {},
|
||||
): void {
|
||||
_radioSeedId = null; // M4c: non-radio enqueue clears the radio refresh state
|
||||
_queue = tracks;
|
||||
if (tracks.length === 0) {
|
||||
if (opts.shuffle && tracks.length > 1) {
|
||||
// Fisher-Yates over the whole list. startIndex is ignored — the
|
||||
// caller is asking for "random play from this pool," so the first
|
||||
// track should also be random, not the one at startIndex.
|
||||
const arr = tracks.slice();
|
||||
for (let j = arr.length - 1; j > 0; j--) {
|
||||
const r = Math.floor(_rng() * (j + 1));
|
||||
[arr[j], arr[r]] = [arr[r], arr[j]];
|
||||
}
|
||||
_queue = arr;
|
||||
_index = 0;
|
||||
_state = 'idle';
|
||||
} else {
|
||||
_index = Math.max(0, Math.min(startIndex, tracks.length - 1));
|
||||
_shuffle = true;
|
||||
_state = 'loading';
|
||||
} else {
|
||||
_queue = tracks;
|
||||
if (tracks.length === 0) {
|
||||
_index = 0;
|
||||
_state = 'idle';
|
||||
} else {
|
||||
_index = Math.max(0, Math.min(startIndex, tracks.length - 1));
|
||||
_state = 'loading';
|
||||
}
|
||||
}
|
||||
_position = 0;
|
||||
_duration = 0;
|
||||
|
||||
Reference in New Issue
Block a user