From ed079e3e156fa4790323ffbb8b7071ddc0b21f30 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 9 Apr 2024 21:18:42 +0600 Subject: [PATCH] feat: add source change support and re-add prefetching tracks --- lib/components/player/player_queue.dart | 2 +- .../proxy_playlist/player_listeners.dart | 16 +++++++++++ .../proxy_playlist_provider.dart | 1 + lib/provider/server/server.dart | 14 +++++----- lib/provider/server/sourced_track.dart | 27 +++++++++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 lib/provider/server/sourced_track.dart diff --git a/lib/components/player/player_queue.dart b/lib/components/player/player_queue.dart index 0bf61da4..29be4830 100644 --- a/lib/components/player/player_queue.dart +++ b/lib/components/player/player_queue.dart @@ -161,7 +161,7 @@ class PlayerQueue extends HookConsumerWidget { snap: false, backgroundColor: Colors.transparent, elevation: 0, - automaticallyImplyLeading: !isSearching.value, + automaticallyImplyLeading: false, title: BackdropFilter( filter: ImageFilter.blur( sigmaX: 10, diff --git a/lib/provider/proxy_playlist/player_listeners.dart b/lib/provider/proxy_playlist/player_listeners.dart index ed648bf7..bc91ae0a 100644 --- a/lib/provider/proxy_playlist/player_listeners.dart +++ b/lib/provider/proxy_playlist/player_listeners.dart @@ -6,6 +6,7 @@ import 'package:catcher_2/catcher_2.dart'; import 'package:spotube/models/local_track.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/proxy_playlist/skip_segments.dart'; +import 'package:spotube/provider/server/sourced_track.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; extension ProxyPlaylistListeners on ProxyPlaylistNotifier { @@ -63,6 +64,21 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier { }); } + StreamSubscription subscribeToPosition() { + String lastTrack = ""; // used to prevent multiple calls to the same track + return audioPlayer.positionStream.listen((event) async { + if (event < const Duration(seconds: 3) || + state.active == null || + state.active == state.tracks.length - 1) return; + final nextTrack = state.tracks.elementAt(state.active! + 1); + + if (lastTrack == nextTrack.id) return; + + await ref.read(sourcedTrackProvider(nextTrack).future); + lastTrack = nextTrack.id!; + }); + } + StreamSubscription subscribeToPlayerError() { return audioPlayer.errorStream.listen((event) {}); } diff --git a/lib/provider/proxy_playlist/proxy_playlist_provider.dart b/lib/provider/proxy_playlist/proxy_playlist_provider.dart index 7d6a33c6..13c7a25c 100644 --- a/lib/provider/proxy_playlist/proxy_playlist_provider.dart +++ b/lib/provider/proxy_playlist/proxy_playlist_provider.dart @@ -51,6 +51,7 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier { // These are subscription methods from player_listeners.dart subscribeToPlaylist(), subscribeToSkipSponsor(), + subscribeToPosition(), subscribeToScrobbleChanged(), ]; } diff --git a/lib/provider/server/server.dart b/lib/provider/server/server.dart index 41d1388d..4dd8e6b9 100644 --- a/lib/provider/server/server.dart +++ b/lib/provider/server/server.dart @@ -13,9 +13,9 @@ import 'package:spotube/models/logger.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/server/active_sourced_track.dart'; +import 'package:spotube/provider/server/sourced_track.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; -import 'package:spotube/services/sourced_track/sourced_track.dart'; class PlaybackServer { final Ref ref; @@ -38,7 +38,6 @@ class PlaybackServer { if (kDebugMode) { pipeline.addMiddleware(logRequests()); - dio.interceptors.add(LogInterceptor()); } serve(pipeline.addHandler(router.call), InternetAddress.loopbackIPv4, port) @@ -58,13 +57,15 @@ class PlaybackServer { try { final track = playlist.tracks.firstWhere((element) => element.id == trackId); - final sourcedTrack = - await SourcedTrack.fetchFromTrack(track: track, ref: ref); + final activeSourcedTrack = ref.read(activeSourcedTrackProvider); + final sourcedTrack = activeSourcedTrack?.id == track.id + ? activeSourcedTrack + : await ref.read(sourcedTrackProvider(track).future); ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack); final res = await dio.get( - sourcedTrack.url, + sourcedTrack!.url, options: Options( headers: { ...request.headers, @@ -75,13 +76,14 @@ class PlaybackServer { "Connection": "keep-alive", }, responseType: ResponseType.stream, + validateStatus: (status) => status! < 500, ), ); final audioStream = res.data?.stream as Stream?; return Response( - 200, + res.statusCode!, body: audioStream, context: { "shelf.io.buffer_output": false, diff --git a/lib/provider/server/sourced_track.dart b/lib/provider/server/sourced_track.dart new file mode 100644 index 00000000..e589b96a --- /dev/null +++ b/lib/provider/server/sourced_track.dart @@ -0,0 +1,27 @@ +import 'package:collection/collection.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; +import 'package:spotube/services/sourced_track/sourced_track.dart'; + +final sourcedTrackProvider = + FutureProvider.family((ref, track) async { + if (track == null) { + return null; + } + + ref.listen( + ProxyPlaylistNotifier.provider, + (old, next) { + if (next.tracks.isEmpty || + next.tracks.none((element) => element.id == track.id)) { + ref.invalidateSelf(); + } + }, + ); + + final sourcedTrack = + await SourcedTrack.fetchFromTrack(track: track, ref: ref); + + return sourcedTrack; +});