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:
@@ -1,3 +1,5 @@
|
||||
import 'dart:math' show Random;
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@@ -65,7 +67,18 @@ class PlayerActions {
|
||||
}
|
||||
final Ref _ref;
|
||||
|
||||
Future<void> playTracks(List<TrackRef> tracks, {int initialIndex = 0}) async {
|
||||
Future<void> playTracks(
|
||||
List<TrackRef> tracks, {
|
||||
int initialIndex = 0,
|
||||
bool shuffle = false,
|
||||
}) async {
|
||||
// shuffle=true means "play this pool randomly, starting at a
|
||||
// random track." Used for system playlists so the first track of
|
||||
// a re-play within the day is different from the prior one.
|
||||
var startAt = initialIndex;
|
||||
if (shuffle && tracks.length > 1) {
|
||||
startAt = Random().nextInt(tracks.length);
|
||||
}
|
||||
final url = await _ref.read(serverUrlProvider.future);
|
||||
final token = await _ref.read(secureStorageProvider).read(key: 'session_token');
|
||||
final cache = _ref.read(albumCoverCacheProvider);
|
||||
@@ -78,7 +91,10 @@ class PlayerActions {
|
||||
audioCacheManager: audioCache,
|
||||
likeBridge: _buildLikeBridge(),
|
||||
);
|
||||
await h.setQueueFromTracks(tracks, initialIndex: initialIndex);
|
||||
await h.setQueueFromTracks(tracks, initialIndex: startAt);
|
||||
if (shuffle) {
|
||||
await h.setShuffleMode(AudioServiceShuffleMode.all);
|
||||
}
|
||||
await h.play();
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,13 @@ class PlaylistCard extends ConsumerWidget {
|
||||
));
|
||||
}
|
||||
if (refs.isEmpty) return;
|
||||
await ref.read(playerActionsProvider).playTracks(refs, initialIndex: 0);
|
||||
// System playlists default to shuffle so replaying within a day
|
||||
// doesn't deliver the identical experience. User playlists keep
|
||||
// stored order.
|
||||
await ref.read(playerActionsProvider).playTracks(
|
||||
refs,
|
||||
initialIndex: 0,
|
||||
shuffle: playlist.isSystem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user