From 5f70207076b4fc7daaecc440830ce2b44bcf5b47 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 13 May 2023 12:46:56 +0600 Subject: [PATCH] refactor(playback): migration to ProxyPlaylist based playback --- lib/collections/intents.dart | 26 +- lib/components/album/album_card.dart | 35 +- lib/components/library/user_local_tracks.dart | 37 +- lib/components/player/player_actions.dart | 28 +- lib/components/player/player_controls.dart | 106 ++-- lib/components/player/player_overlay.dart | 10 +- lib/components/player/player_queue.dart | 24 +- .../player/player_track_details.dart | 14 +- .../player/sibling_tracks_sheet.dart | 26 +- lib/components/playlist/playlist_card.dart | 24 +- lib/components/root/bottom_player.dart | 26 +- .../shared/track_table/track_tile.dart | 21 +- .../shared/track_table/tracks_table_view.dart | 12 +- lib/hooks/use_init_sys_tray.dart | 17 +- lib/hooks/use_progress.dart | 9 +- lib/pages/album/album.dart | 39 +- lib/pages/artist/artist.dart | 27 +- lib/pages/lyrics/lyrics.dart | 10 +- lib/pages/lyrics/mini_lyrics.dart | 10 +- lib/pages/lyrics/plain_lyrics.dart | 10 +- lib/pages/lyrics/synced_lyrics.dart | 8 +- lib/pages/player/player.dart | 6 +- lib/pages/playlist/playlist.dart | 34 +- lib/pages/search/search.dart | 24 +- lib/provider/blacklist_provider.dart | 4 + lib/provider/playlist_queue_provider.dart | 555 ------------------ .../proxy_playlist/next_fetcher_mixin.dart | 79 +++ .../proxy_playlist/proxy_playlist.dart | 74 +++ .../proxy_playlist_provider.dart | 297 ++++++++++ lib/provider/user_preferences_provider.dart | 4 +- lib/services/audio_player/audio_player.dart | 123 +++- .../audio_player/mk_state_player.dart | 39 +- .../audio_services/audio_services.dart | 16 +- .../audio_services/linux_audio_service.dart | 22 +- .../audio_services/mobile_audio_service.dart | 31 +- .../audio_services/windows_audio_service.dart | 8 +- pubspec.yaml | 2 +- 37 files changed, 915 insertions(+), 922 deletions(-) delete mode 100644 lib/provider/playlist_queue_provider.dart create mode 100644 lib/provider/proxy_playlist/next_fetcher_mixin.dart create mode 100644 lib/provider/proxy_playlist/proxy_playlist.dart create mode 100644 lib/provider/proxy_playlist/proxy_playlist_provider.dart diff --git a/lib/collections/intents.dart b/lib/collections/intents.dart index 2ad5bdd7..732b96de 100644 --- a/lib/collections/intents.dart +++ b/lib/collections/intents.dart @@ -5,7 +5,8 @@ import 'package:go_router/go_router.dart'; import 'package:spotube/components/player/player_controls.dart'; import 'package:spotube/collections/routes.dart'; import 'package:spotube/models/logger.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/utils/platform.dart'; import 'package:window_manager/window_manager.dart'; @@ -23,19 +24,11 @@ class PlayPauseAction extends Action { if (PlayerControls.focusNode.canRequestFocus) { PlayerControls.focusNode.requestFocus(); } - final playlist = intent.ref.read(PlaylistQueueNotifier.provider); - final playlistNotifier = intent.ref.read(PlaylistQueueNotifier.notifier); - if (playlist == null) { - return null; - } else if (!audioPlayer.isPlaying) { - if (audioPlayer.hasSource && !await audioPlayer.isCompleted) { - await playlistNotifier.resume(); - } else { - // TODO: Implement play on start - // await playlistNotifier.play(); - } + + if (!audioPlayer.isPlaying) { + await audioPlayer.resume(); } else { - await playlistNotifier.pause(); + await audioPlayer.pause(); } return null; } @@ -98,9 +91,8 @@ class SeekIntent extends Intent { class SeekAction extends Action { @override invoke(intent) async { - final playlist = intent.ref.read(PlaylistQueueNotifier.provider); - final playlistNotifier = intent.ref.read(PlaylistQueueNotifier.notifier); - if (playlist == null || playlist.isLoading) { + final playlist = intent.ref.read(ProxyPlaylistNotifier.provider); + if (playlist.isFetching) { DirectionalFocusAction().invoke( DirectionalFocusIntent( intent.forward ? TraversalDirection.right : TraversalDirection.left, @@ -109,7 +101,7 @@ class SeekAction extends Action { return null; } final position = (await audioPlayer.position ?? Duration.zero).inSeconds; - await playlistNotifier.seek( + await audioPlayer.seek( Duration( seconds: intent.forward ? position + 5 : position - 5, ), diff --git a/lib/components/album/album_card.dart b/lib/components/album/album_card.dart index 128133d6..821194aa 100644 --- a/lib/components/album/album_card.dart +++ b/lib/components/album/album_card.dart @@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/shared/playbutton_card.dart'; import 'package:spotube/hooks/use_breakpoint_value.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -41,16 +41,15 @@ class AlbumCard extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final playlist = ref.watch(PlaylistQueueNotifier.provider); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); final playing = useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); final queryClient = useQueryClient(); final query = queryClient .getQuery, dynamic>("album-tracks/${album.id}"); bool isPlaylistPlaying = useMemoized( - () => - playlistNotifier.isPlayingPlaylist(query?.data ?? album.tracks ?? []), + () => playlist.containsTracks(query?.data ?? album.tracks ?? []), [playlistNotifier, query?.data, album.tracks], ); final int marginH = @@ -66,7 +65,7 @@ class AlbumCard extends HookConsumerWidget { ), margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()), isPlaying: isPlaylistPlaying, - isLoading: isPlaylistPlaying && playlist?.isLoading == true, + isLoading: isPlaylistPlaying && playlist.isFetching == true, title: album.name!, description: "${AlbumType.from(album.albumType!).formatted} • ${TypeConversionUtils.artists_X_String(album.artists ?? [])}", @@ -77,16 +76,19 @@ class AlbumCard extends HookConsumerWidget { updating.value = true; try { if (isPlaylistPlaying && playing) { - return playlistNotifier.pause(); + return audioPlayer.pause(); } else if (isPlaylistPlaying && !playing) { - return playlistNotifier.resume(); + return audioPlayer.resume(); } - await playlistNotifier.loadAndPlay(album.tracks - ?.map((e) => - TypeConversionUtils.simpleTrack_X_Track(e, album)) - .toList() ?? - []); + await playlistNotifier.load( + album.tracks + ?.map((e) => + TypeConversionUtils.simpleTrack_X_Track(e, album)) + .toList() ?? + [], + autoPlay: true, + ); } finally { updating.value = false; } @@ -115,16 +117,15 @@ class AlbumCard extends HookConsumerWidget { ); if (fetchedTracks == null || fetchedTracks.isEmpty) return; - playlistNotifier.add( - fetchedTracks, - ); + playlistNotifier.addTracks(fetchedTracks); if (context.mounted) { final snackbar = SnackBar( content: Text("Added ${album.tracks?.length} tracks to queue"), action: SnackBarAction( label: "Undo", onPressed: () { - playlistNotifier.remove(fetchedTracks); + playlistNotifier + .removeTracks(fetchedTracks.map((e) => e.id!)); }, ), ); diff --git a/lib/components/library/user_local_tracks.dart b/lib/components/library/user_local_tracks.dart index 02076435..b7d06ff0 100644 --- a/lib/components/library/user_local_tracks.dart +++ b/lib/components/library/user_local_tracks.dart @@ -22,7 +22,7 @@ import 'package:spotube/components/shared/track_table/track_tile.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/use_async_effect.dart'; import 'package:spotube/models/local_track.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -132,33 +132,35 @@ class UserLocalTracks extends HookConsumerWidget { const UserLocalTracks({Key? key}) : super(key: key); void playLocalTracks( - PlaylistQueueNotifier playback, + WidgetRef ref, List tracks, { LocalTrack? currentTrack, }) async { + final playlist = ref.read(ProxyPlaylistNotifier.provider); + final playback = ref.read(ProxyPlaylistNotifier.notifier); currentTrack ??= tracks.first; - final isPlaylistPlaying = playback.isPlayingPlaylist(tracks); + final isPlaylistPlaying = playlist.containsTracks(tracks); if (!isPlaylistPlaying) { - await playback.loadAndPlay( + await playback.load( tracks, - active: tracks.indexWhere((s) => s.id == currentTrack?.id), + initialIndex: tracks.indexWhere((s) => s.id == currentTrack?.id), + autoPlay: true, ); } else if (isPlaylistPlaying && currentTrack.id != null && - currentTrack.id != playback.state?.activeTrack.id) { - await playback.playTrack(currentTrack); + currentTrack.id != playlist.activeTrack?.id) { + await playback.jumpToTrack(currentTrack); } } @override Widget build(BuildContext context, ref) { final sortBy = useState(SortBy.none); - final playlist = ref.watch(PlaylistQueueNotifier.provider); - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); final trackSnapshot = ref.watch(localTracksProvider); - final isPlaylistPlaying = playlistNotifier.isPlayingPlaylist( - trackSnapshot.value ?? [], - ); + final isPlaylistPlaying = + playlist.containsTracks(trackSnapshot.value ?? []); final isMounted = useIsMounted(); final searchText = useState(""); @@ -194,9 +196,12 @@ class UserLocalTracks extends HookConsumerWidget { if (trackSnapshot.value?.isNotEmpty == true) { if (!isPlaylistPlaying) { playLocalTracks( - playlistNotifier, trackSnapshot.value!); + ref, + trackSnapshot.value!, + ); } else { - playlistNotifier.stop(); + // TODO: Remove stop capability + // playlistNotifier.stop(); } } } @@ -272,13 +277,13 @@ class UserLocalTracks extends HookConsumerWidget { duration: "${track.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.duration?.inSeconds.remainder(60) ?? 0)}", track: MapEntry(index, track), - isActive: playlist?.activeTrack.id == track.id, + isActive: playlist.activeTrack?.id == track.id, isChecked: false, showCheck: false, isLocal: true, onTrackPlayButtonPressed: (currentTrack) { return playLocalTracks( - playlistNotifier, + ref, sortedTracks, currentTrack: track, ); diff --git a/lib/components/player/player_actions.dart b/lib/components/player/player_actions.dart index 6c51825a..deded47a 100644 --- a/lib/components/player/player_actions.dart +++ b/lib/components/player/player_actions.dart @@ -13,7 +13,7 @@ import 'package:spotube/models/local_track.dart'; import 'package:spotube/models/logger.dart'; import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/downloader_provider.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class PlayerActions extends HookConsumerWidget { @@ -30,26 +30,26 @@ class PlayerActions extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final playlist = ref.watch(PlaylistQueueNotifier.provider); - final isLocalTrack = playlist?.activeTrack is LocalTrack; + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final isLocalTrack = playlist.activeTrack is LocalTrack; final downloader = ref.watch(downloaderProvider); final isInQueue = downloader.inQueue - .any((element) => element.id == playlist?.activeTrack.id); + .any((element) => element.id == playlist.activeTrack?.id); final localTracks = [] /* ref.watch(localTracksProvider).value */; final auth = ref.watch(AuthenticationNotifier.provider); final isDownloaded = useMemoized(() { return localTracks.any( (element) => - element.name == playlist?.activeTrack.name && - element.album?.name == playlist?.activeTrack.album?.name && + element.name == playlist.activeTrack?.name && + element.album?.name == playlist.activeTrack?.album?.name && TypeConversionUtils.artists_X_String( element.artists ?? []) == TypeConversionUtils.artists_X_String( - playlist?.activeTrack.artists ?? []), + playlist.activeTrack?.artists ?? []), ) == true; - }, [localTracks, playlist?.activeTrack]); + }, [localTracks, playlist.activeTrack]); return Row( mainAxisAlignment: mainAxisAlignment, @@ -57,7 +57,7 @@ class PlayerActions extends HookConsumerWidget { IconButton( icon: const Icon(SpotubeIcons.queue), tooltip: context.l10n.queue, - onPressed: playlist != null + onPressed: playlist.activeTrack != null ? () { showModalBottomSheet( context: context, @@ -83,7 +83,7 @@ class PlayerActions extends HookConsumerWidget { IconButton( icon: const Icon(SpotubeIcons.alternativeRoute), tooltip: context.l10n.alternative_track_sources, - onPressed: playlist?.activeTrack != null + onPressed: playlist.activeTrack != null ? () { showModalBottomSheet( context: context, @@ -120,12 +120,12 @@ class PlayerActions extends HookConsumerWidget { icon: Icon( isDownloaded ? SpotubeIcons.done : SpotubeIcons.download, ), - onPressed: playlist?.activeTrack != null - ? () => downloader.addToQueue(playlist!.activeTrack) + onPressed: playlist.activeTrack != null + ? () => downloader.addToQueue(playlist.activeTrack!) : null, ), - if (playlist?.activeTrack != null && !isLocalTrack && auth != null) - TrackHeartButton(track: playlist!.activeTrack), + if (playlist.activeTrack != null && !isLocalTrack && auth != null) + TrackHeartButton(track: playlist.activeTrack!), ...(extraActions ?? []) ], ); diff --git a/lib/components/player/player_controls.dart b/lib/components/player/player_controls.dart index fe13aaa4..b9d2f381 100644 --- a/lib/components/player/player_controls.dart +++ b/lib/components/player/player_controls.dart @@ -9,7 +9,7 @@ import 'package:spotube/collections/intents.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/use_progress.dart'; import 'package:spotube/models/logger.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/loop_mode.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -43,8 +43,8 @@ class PlayerControls extends HookConsumerWidget { SeekIntent: SeekAction(), }, []); - final playlist = ref.watch(PlaylistQueueNotifier.provider); - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); final playing = useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; @@ -145,13 +145,13 @@ class PlayerControls extends HookConsumerWidget { // than total duration. Keeping it resolved value: progress.value.toDouble(), secondaryTrackValue: progressObj.item4, - onChanged: playlist?.isLoading == true || buffering + onChanged: playlist.isFetching == true || buffering ? null : (v) { progress.value = v; }, onChangeEnd: (value) async { - await playlistNotifier.seek( + await audioPlayer.seek( Duration( seconds: (value * duration.inSeconds).toInt(), ), @@ -186,24 +186,27 @@ class PlayerControls extends HookConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - IconButton( - tooltip: playlist?.shuffled == true - ? context.l10n.unshuffle_playlist - : context.l10n.shuffle_playlist, - icon: const Icon(SpotubeIcons.shuffle), - style: playlist?.shuffled == true - ? activeButtonStyle - : buttonStyle, - onPressed: playlist == null || playlist.isLoading - ? null - : () { - if (playlist.shuffled == true) { - playlistNotifier.setShuffle(false); - } else { - playlistNotifier.setShuffle(true); - } - }, - ), + StreamBuilder( + stream: audioPlayer.shuffledStream, + builder: (context, snapshot) { + final shuffled = snapshot.data ?? false; + return IconButton( + tooltip: shuffled + ? context.l10n.unshuffle_playlist + : context.l10n.shuffle_playlist, + icon: const Icon(SpotubeIcons.shuffle), + style: shuffled ? activeButtonStyle : buttonStyle, + onPressed: playlist.isFetching + ? null + : () { + if (shuffled) { + audioPlayer.setShuffle(false); + } else { + audioPlayer.setShuffle(true); + } + }, + ); + }), IconButton( tooltip: context.l10n.previous_track, icon: const Icon(SpotubeIcons.skipBack), @@ -214,7 +217,7 @@ class PlayerControls extends HookConsumerWidget { tooltip: playing ? context.l10n.pause_playback : context.l10n.resume_playback, - icon: playlist?.isLoading == true + icon: playlist.isFetching == true ? SizedBox( height: 20, width: 20, @@ -227,7 +230,7 @@ class PlayerControls extends HookConsumerWidget { playing ? SpotubeIcons.pause : SpotubeIcons.play, ), style: resumePauseStyle, - onPressed: playlist?.isLoading == true + onPressed: playlist.isFetching == true ? null : Actions.handler( context, @@ -240,30 +243,35 @@ class PlayerControls extends HookConsumerWidget { style: buttonStyle, onPressed: playlistNotifier.next, ), - IconButton( - tooltip: playlist?.loopMode == PlaybackLoopMode.one - ? context.l10n.loop_track - : context.l10n.repeat_playlist, - icon: Icon( - playlist?.loopMode == PlaybackLoopMode.one - ? SpotubeIcons.repeatOne - : SpotubeIcons.repeat, - ), - style: playlist?.loopMode == PlaybackLoopMode.one - ? activeButtonStyle - : buttonStyle, - onPressed: playlist == null || playlist.isLoading - ? null - : () { - if (playlist.loopMode == PlaybackLoopMode.one) { - playlistNotifier - .setLoopMode(PlaybackLoopMode.all); - } else { - playlistNotifier - .setLoopMode(PlaybackLoopMode.one); - } - }, - ), + StreamBuilder( + stream: audioPlayer.loopModeStream, + builder: (context, snapshot) { + final loopMode = snapshot.data ?? PlaybackLoopMode.none; + return IconButton( + tooltip: loopMode == PlaybackLoopMode.one + ? context.l10n.loop_track + : context.l10n.repeat_playlist, + icon: Icon( + loopMode == PlaybackLoopMode.one + ? SpotubeIcons.repeatOne + : SpotubeIcons.repeat, + ), + style: loopMode == PlaybackLoopMode.one + ? activeButtonStyle + : buttonStyle, + onPressed: playlist.isFetching + ? null + : () { + if (loopMode == PlaybackLoopMode.one) { + audioPlayer + .setLoopMode(PlaybackLoopMode.all); + } else { + audioPlayer + .setLoopMode(PlaybackLoopMode.one); + } + }, + ); + }), ], ), const SizedBox(height: 5) diff --git a/lib/components/player/player_overlay.dart b/lib/components/player/player_overlay.dart index d5ddf013..f0339deb 100644 --- a/lib/components/player/player_overlay.dart +++ b/lib/components/player/player_overlay.dart @@ -9,7 +9,7 @@ import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/player/player_track_details.dart'; import 'package:spotube/collections/intents.dart'; import 'package:spotube/hooks/use_progress.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -24,10 +24,10 @@ class PlayerOverlay extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { final canShow = ref.watch( - PlaylistQueueNotifier.provider.select((s) => s != null), + ProxyPlaylistNotifier.provider.select((s) => s != null), ); - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); - final playlist = ref.watch(PlaylistQueueNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); final playing = useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; @@ -116,7 +116,7 @@ class PlayerOverlay extends HookConsumerWidget { Consumer( builder: (context, ref, _) { return IconButton( - icon: playlist?.isLoading == true + icon: playlist.isFetching ? const SizedBox( height: 20, width: 20, diff --git a/lib/components/player/player_queue.dart b/lib/components/player/player_queue.dart index 7a2dd39e..4c679e57 100644 --- a/lib/components/player/player_queue.dart +++ b/lib/components/player/player_queue.dart @@ -10,7 +10,7 @@ import 'package:spotube/components/shared/fallbacks/not_found.dart'; import 'package:spotube/components/shared/track_table/track_tile.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/use_auto_scroll_controller.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/utils/primitive_utils.dart'; class PlayerQueue extends HookConsumerWidget { @@ -22,10 +22,10 @@ class PlayerQueue extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final playlist = ref.watch(PlaylistQueueNotifier.provider); - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); final controller = useAutoScrollController(); - final tracks = playlist?.tracks ?? {}; + final tracks = playlist.tracks; if (tracks.isEmpty) { return const NotFound(vertical: true); @@ -41,11 +41,11 @@ class PlayerQueue extends HookConsumerWidget { final headlineColor = theme.textTheme.headlineSmall?.color; useEffect(() { - if (playlist == null) return null; - final index = playlist.active; - if (index < 0) return; + if (playlist.active == null) return null; + + if (playlist.active! < 0) return; controller.scrollToIndex( - index, + playlist.active!, preferPosition: AutoScrollPosition.middle, ); return null; @@ -113,7 +113,7 @@ class PlayerQueue extends HookConsumerWidget { Flexible( child: ReorderableListView.builder( onReorder: (oldIndex, newIndex) { - playlistNotifier.reorder(oldIndex, newIndex); + playlistNotifier.moveTrack(oldIndex, newIndex); }, scrollController: controller, itemCount: tracks.length, @@ -133,12 +133,12 @@ class PlayerQueue extends HookConsumerWidget { playlist, track: track, duration: duration, - isActive: playlist?.activeTrack.id == track.value.id, + isActive: playlist.activeTrack?.id == track.value.id, onTrackPlayButtonPressed: (currentTrack) async { - if (playlist?.activeTrack.id == track.value.id) { + if (playlist.activeTrack?.id == track.value.id) { return; } - await playlistNotifier.playTrack(currentTrack); + await playlistNotifier.jumpToTrack(currentTrack); }, leadingActions: [ ReorderableDragStartListener( diff --git a/lib/components/player/player_track_details.dart b/lib/components/player/player_track_details.dart index 8e2a57ee..2c400e98 100644 --- a/lib/components/player/player_track_details.dart +++ b/lib/components/player/player_track_details.dart @@ -5,7 +5,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/hooks/use_breakpoints.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class PlayerTrackDetails extends HookConsumerWidget { @@ -18,11 +18,11 @@ class PlayerTrackDetails extends HookConsumerWidget { Widget build(BuildContext context, ref) { final theme = Theme.of(context); final breakpoint = useBreakpoints(); - final playback = ref.watch(PlaylistQueueNotifier.provider); + final playback = ref.watch(ProxyPlaylistNotifier.provider); return Row( children: [ - if (playback != null) + if (playback.activeTrack != null) Container( padding: const EdgeInsets.all(6), constraints: const BoxConstraints( @@ -44,7 +44,7 @@ class PlayerTrackDetails extends HookConsumerWidget { children: [ const SizedBox(height: 4), Text( - playback?.activeTrack.name ?? "", + playback.activeTrack?.name ?? "", overflow: TextOverflow.ellipsis, style: theme.textTheme.bodyMedium?.copyWith( color: color, @@ -52,7 +52,7 @@ class PlayerTrackDetails extends HookConsumerWidget { ), Text( TypeConversionUtils.artists_X_String( - playback?.activeTrack.artists ?? [], + playback.activeTrack?.artists ?? [], ), overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall!.copyWith(color: color), @@ -66,12 +66,12 @@ class PlayerTrackDetails extends HookConsumerWidget { child: Column( children: [ Text( - playback?.activeTrack.name ?? "", + playback.activeTrack?.name ?? "", overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, color: color), ), TypeConversionUtils.artists_X_ClickableArtists( - playback?.activeTrack.artists ?? [], + playback.activeTrack?.artists ?? [], ) ], ), diff --git a/lib/components/player/sibling_tracks_sheet.dart b/lib/components/player/sibling_tracks_sheet.dart index 0a34e13e..da953006 100644 --- a/lib/components/player/sibling_tracks_sheet.dart +++ b/lib/components/player/sibling_tracks_sheet.dart @@ -7,7 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/spotube_track.dart'; -import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/utils/primitive_utils.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart'; @@ -21,11 +21,11 @@ class SiblingTracksSheet extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { final theme = Theme.of(context); - final playlist = ref.watch(PlaylistQueueNotifier.provider); - final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); - final siblings = playlist?.isLoading == false - ? (playlist!.activeTrack as SpotubeTrack).siblings + final siblings = playlist.isFetching == false + ? (playlist.activeTrack as SpotubeTrack).siblings :