From d82261cb25ece63f85af0e40216cf32dccdc9dd5 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 23 May 2024 16:56:52 +0600 Subject: [PATCH] fix: local track not showing up in queue --- lib/components/library/user_local_tracks.dart | 106 +++--- .../shared/track_tile/track_options.dart | 222 ++++++------ .../shared/track_tile/track_tile.dart | 23 +- lib/pages/library/local_folder.dart | 315 +++++++++--------- .../proxy_playlist/player_listeners.dart | 2 +- .../proxy_playlist/proxy_playlist.dart | 13 +- lib/services/audio_player/audio_player.dart | 11 +- untranslated_messages.json | 28 ++ 8 files changed, 385 insertions(+), 335 deletions(-) diff --git a/lib/components/library/user_local_tracks.dart b/lib/components/library/user_local_tracks.dart index d5115aaa..ffaae0d9 100644 --- a/lib/components/library/user_local_tracks.dart +++ b/lib/components/library/user_local_tracks.dart @@ -6,32 +6,20 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:collection/collection.dart'; -import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; import 'package:mime/mime.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotify/spotify.dart'; -import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/spotube_icons.dart'; -import 'package:spotube/components/shared/expandable_search/expandable_search.dart'; -import 'package:spotube/components/shared/fallbacks/not_found.dart'; -import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart'; -import 'package:spotube/components/shared/sort_tracks_dropdown.dart'; -import 'package:spotube/components/shared/track_tile/track_tile.dart'; -import 'package:spotube/extensions/artist_simple.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/track.dart'; import 'package:spotube/models/local_track.dart'; -import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/utils/platform.dart'; -import 'package:spotube/utils/service_utils.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FfiException; @@ -63,7 +51,8 @@ enum SortBy { album, } -final localTracksProvider = FutureProvider>>((ref) async { +final localTracksProvider = + FutureProvider>>((ref) async { try { if (kIsWeb) return {}; final Map> tracks = {}; @@ -82,7 +71,6 @@ final localTracksProvider = FutureProvider>>((ref) for (var location in [downloadLocation, ...localLibraryLocations]) { if (location.isEmpty) continue; final entities = []; - final dir = Directory(location); if (await Directory(location).exists()) { entities.addAll(Directory(location).listSync(recursive: true)); } @@ -110,7 +98,11 @@ final localTracksProvider = FutureProvider>>((ref) ); } - return {"metadata": metadata, "file": file, "art": imageFile.path}; + return { + "metadata": metadata, + "file": file, + "art": imageFile.path + }; } catch (e, stack) { if (e is FfiException) { return {"file": file}; @@ -152,7 +144,6 @@ class UserLocalTracks extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final preferencesNotifier = ref.watch(userPreferencesProvider.notifier); final preferences = ref.watch(userPreferencesProvider); @@ -163,69 +154,74 @@ class UserLocalTracks extends HookConsumerWidget { ); if (dirStr == null) return; if (preferences.localLibraryLocation.contains(dirStr)) return; - preferencesNotifier.setLocalLibraryLocation([...preferences.localLibraryLocation, dirStr]); + preferencesNotifier.setLocalLibraryLocation( + [...preferences.localLibraryLocation, dirStr]); } else { String? dirStr = await getDirectoryPath( initialDirectory: preferences.downloadLocation, ); if (dirStr == null) return; if (preferences.localLibraryLocation.contains(dirStr)) return; - preferencesNotifier.setLocalLibraryLocation([...preferences.localLibraryLocation, dirStr]); + preferencesNotifier.setLocalLibraryLocation( + [...preferences.localLibraryLocation, dirStr]); } }, [preferences.localLibraryLocation]); final removeLocalLibraryLocation = useCallback((String location) { if (!preferences.localLibraryLocation.contains(location)) return; - preferencesNotifier.setLocalLibraryLocation([...preferences.localLibraryLocation]..remove(location)); + preferencesNotifier.setLocalLibraryLocation( + [...preferences.localLibraryLocation]..remove(location), + ); }, [preferences.localLibraryLocation]); // This is just to pre-load the tracks. // For now, this gets all of them. ref.watch(localTracksProvider); - return Column( - children: [ - Padding( + return Column(children: [ + Padding( padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - const SizedBox(width: 5), - TextButton.icon( - icon: const Icon(SpotubeIcons.folderAdd), - label: Text(context.l10n.add_library_location), - onPressed: addLocalLibraryLocation, - ) - ] - ) - ), - Expanded( - child: ListView.builder( - itemCount: preferences.localLibraryLocation.length+1, + child: Row(children: [ + const SizedBox(width: 5), + TextButton.icon( + icon: const Icon(SpotubeIcons.folderAdd), + label: Text(context.l10n.add_library_location), + onPressed: addLocalLibraryLocation, + ) + ])), + Expanded( + child: ListView.builder( + itemCount: preferences.localLibraryLocation.length + 1, itemBuilder: (context, index) { late final String location; if (index == 0) { location = preferences.downloadLocation; } else { - location = preferences.localLibraryLocation[index-1]; + location = preferences.localLibraryLocation[index - 1]; } return ListTile( - title: preferences.downloadLocation != location ? Text(location) - : Text(context.l10n.downloads), - trailing: preferences.downloadLocation != location ? Tooltip( - message: context.l10n.remove_library_location, - child: IconButton( - icon: Icon(SpotubeIcons.folderRemove, color: Colors.red[400]), - onPressed: () => removeLocalLibraryLocation(location), - ), - ) : null, - onTap: () async { - context.go("/library/local${location == preferences.downloadLocation ? "?downloads=1" : ""}", extra: location); - } - ); - } - ), - ), - ] - ); + title: preferences.downloadLocation != location + ? Text(location) + : Text(context.l10n.downloads), + trailing: preferences.downloadLocation != location + ? Tooltip( + message: context.l10n.remove_library_location, + child: IconButton( + icon: Icon(SpotubeIcons.folderRemove, + color: Colors.red[400]), + onPressed: () => + removeLocalLibraryLocation(location), + ), + ) + : null, + onTap: () async { + context.go( + "/library/local${location == preferences.downloadLocation ? "?downloads=1" : ""}", + extra: location, + ); + }); + }), + ), + ]); } } diff --git a/lib/components/shared/track_tile/track_options.dart b/lib/components/shared/track_tile/track_options.dart index a9ec36b9..c917ebaa 100644 --- a/lib/components/shared/track_tile/track_options.dart +++ b/lib/components/shared/track_tile/track_options.dart @@ -197,6 +197,8 @@ class TrackOptions extends HookConsumerWidget { return downloadManager.getProgressNotifier(spotubeTrack); }); + final isLocalTrack = track is LocalTrack; + final adaptivePopSheetList = AdaptivePopSheetList( onSelected: (value) async { switch (value) { @@ -314,118 +316,120 @@ class TrackOptions extends HookConsumerWidget { ), ), ], - children: switch (track.runtimeType) { - LocalTrack() => [ - PopSheetEntry( - value: TrackOptionValue.delete, - leading: const Icon(SpotubeIcons.trash), - title: Text(context.l10n.delete), - ) - ], - _ => [ - if (mediaQuery.smAndDown) - PopSheetEntry( - value: TrackOptionValue.album, - leading: const Icon(SpotubeIcons.album), - title: Text(context.l10n.go_to_album), - subtitle: Text(track.album!.name!), - ), - if (!playlist.containsTrack(track)) ...[ - PopSheetEntry( - value: TrackOptionValue.addToQueue, - leading: const Icon(SpotubeIcons.queueAdd), - title: Text(context.l10n.add_to_queue), - ), - PopSheetEntry( - value: TrackOptionValue.playNext, - leading: const Icon(SpotubeIcons.lightning), - title: Text(context.l10n.play_next), - ), - ] else - PopSheetEntry( - value: TrackOptionValue.removeFromQueue, - enabled: playlist.activeTrack?.id != track.id, - leading: const Icon(SpotubeIcons.queueRemove), - title: Text(context.l10n.remove_from_queue), - ), - if (me.asData?.value != null) - PopSheetEntry( - value: TrackOptionValue.favorite, - leading: favorites.isLiked - ? const Icon( - SpotubeIcons.heartFilled, - color: Colors.pink, - ) - : const Icon(SpotubeIcons.heart), - title: Text( - favorites.isLiked - ? context.l10n.remove_from_favorites - : context.l10n.save_as_favorite, - ), - ), - if (auth != null) ...[ - PopSheetEntry( - value: TrackOptionValue.startRadio, - leading: const Icon(SpotubeIcons.radio), - title: Text(context.l10n.start_a_radio), - ), - PopSheetEntry( - value: TrackOptionValue.addToPlaylist, - leading: const Icon(SpotubeIcons.playlistAdd), - title: Text(context.l10n.add_to_playlist), - ), - ], - if (userPlaylist && auth != null) - PopSheetEntry( - value: TrackOptionValue.removeFromPlaylist, - leading: const Icon(SpotubeIcons.removeFilled), - title: Text(context.l10n.remove_from_playlist), - ), - PopSheetEntry( - value: TrackOptionValue.download, - enabled: !isInQueue, - leading: isInQueue - ? HookBuilder(builder: (context) { - final progress = useListenable(progressNotifier!); - return CircularProgressIndicator( - value: progress.value, - ); - }) - : const Icon(SpotubeIcons.download), - title: Text(context.l10n.download_track), + children: [ + if (isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.delete, + leading: const Icon(SpotubeIcons.trash), + title: Text(context.l10n.delete), + ), + if (mediaQuery.smAndDown) + PopSheetEntry( + value: TrackOptionValue.album, + leading: const Icon(SpotubeIcons.album), + title: Text(context.l10n.go_to_album), + subtitle: Text(track.album!.name!), + ), + if (!playlist.containsTrack(track)) ...[ + PopSheetEntry( + value: TrackOptionValue.addToQueue, + leading: const Icon(SpotubeIcons.queueAdd), + title: Text(context.l10n.add_to_queue), + ), + PopSheetEntry( + value: TrackOptionValue.playNext, + leading: const Icon(SpotubeIcons.lightning), + title: Text(context.l10n.play_next), + ), + ] else + PopSheetEntry( + value: TrackOptionValue.removeFromQueue, + enabled: playlist.activeTrack?.id != track.id, + leading: const Icon(SpotubeIcons.queueRemove), + title: Text(context.l10n.remove_from_queue), + ), + if (me.asData?.value != null && !isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.favorite, + leading: favorites.isLiked + ? const Icon( + SpotubeIcons.heartFilled, + color: Colors.pink, + ) + : const Icon(SpotubeIcons.heart), + title: Text( + favorites.isLiked + ? context.l10n.remove_from_favorites + : context.l10n.save_as_favorite, ), - PopSheetEntry( - value: TrackOptionValue.blacklist, - leading: const Icon(SpotubeIcons.playlistRemove), - iconColor: !isBlackListed ? Colors.red[400] : null, - textColor: !isBlackListed ? Colors.red[400] : null, - title: Text( - isBlackListed - ? context.l10n.remove_from_blacklist - : context.l10n.add_to_blacklist, - ), + ), + if (auth != null && !isLocalTrack) ...[ + PopSheetEntry( + value: TrackOptionValue.startRadio, + leading: const Icon(SpotubeIcons.radio), + title: Text(context.l10n.start_a_radio), + ), + PopSheetEntry( + value: TrackOptionValue.addToPlaylist, + leading: const Icon(SpotubeIcons.playlistAdd), + title: Text(context.l10n.add_to_playlist), + ), + ], + if (userPlaylist && auth != null && !isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.removeFromPlaylist, + leading: const Icon(SpotubeIcons.removeFilled), + title: Text(context.l10n.remove_from_playlist), + ), + if (!isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.download, + enabled: !isInQueue, + leading: isInQueue + ? HookBuilder(builder: (context) { + final progress = useListenable(progressNotifier!); + return CircularProgressIndicator( + value: progress.value, + ); + }) + : const Icon(SpotubeIcons.download), + title: Text(context.l10n.download_track), + ), + if (!isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.blacklist, + leading: const Icon(SpotubeIcons.playlistRemove), + iconColor: !isBlackListed ? Colors.red[400] : null, + textColor: !isBlackListed ? Colors.red[400] : null, + title: Text( + isBlackListed + ? context.l10n.remove_from_blacklist + : context.l10n.add_to_blacklist, ), - PopSheetEntry( - value: TrackOptionValue.share, - leading: const Icon(SpotubeIcons.share), - title: Text(context.l10n.share), + ), + if (!isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.share, + leading: const Icon(SpotubeIcons.share), + title: Text(context.l10n.share), + ), + if (!isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.songlink, + leading: Assets.logos.songlinkTransparent.image( + width: 22, + height: 22, + color: colorScheme.onSurface.withOpacity(0.5), ), - PopSheetEntry( - value: TrackOptionValue.songlink, - leading: Assets.logos.songlinkTransparent.image( - width: 22, - height: 22, - color: colorScheme.onSurface.withOpacity(0.5), - ), - title: Text(context.l10n.song_link), - ), - PopSheetEntry( - value: TrackOptionValue.details, - leading: const Icon(SpotubeIcons.info), - title: Text(context.l10n.details), - ), - ] - }, + title: Text(context.l10n.song_link), + ), + if (!isLocalTrack) + PopSheetEntry( + value: TrackOptionValue.details, + leading: const Icon(SpotubeIcons.info), + title: Text(context.l10n.details), + ), + ], ); //! This is the most ANTI pattern I've ever done, but it works diff --git a/lib/components/shared/track_tile/track_tile.dart b/lib/components/shared/track_tile/track_tile.dart index 30912da2..e3aea4de 100644 --- a/lib/components/shared/track_tile/track_tile.dart +++ b/lib/components/shared/track_tile/track_tile.dart @@ -195,19 +195,26 @@ class TrackTile extends HookConsumerWidget { children: [ Expanded( flex: 6, - child: LinkText( - track.name!, - "/track/${track.id}", - push: true, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), + child: switch (track) { + LocalTrack() => Text( + track.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + _ => LinkText( + track.name!, + "/track/${track.id}", + push: true, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + }, ), if (constrains.mdAndUp) ...[ const SizedBox(width: 8), Expanded( flex: 4, - child: switch (track.runtimeType) { + child: switch (track) { LocalTrack() => Text( track.album!.name!, maxLines: 1, diff --git a/lib/pages/library/local_folder.dart b/lib/pages/library/local_folder.dart index 89d70e09..7a975935 100644 --- a/lib/pages/library/local_folder.dart +++ b/lib/pages/library/local_folder.dart @@ -1,5 +1,4 @@ import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart'; @@ -46,14 +45,14 @@ class LocalLibraryPage extends HookConsumerWidget { await playback.jumpToTrack(currentTrack); } } - + @override Widget build(BuildContext context, ref) { final sortBy = useState(SortBy.none); final playlist = ref.watch(proxyPlaylistProvider); final trackSnapshot = ref.watch(localTracksProvider); - final isPlaylistPlaying = - playlist.containsTracks(trackSnapshot.asData?.value.values.flattened.toList() ?? []); + final isPlaylistPlaying = playlist.containsTracks( + trackSnapshot.asData?.value.values.flattened.toList() ?? []); final searchController = useTextEditingController(); useValueListenable(searchController); @@ -61,176 +60,178 @@ class LocalLibraryPage extends HookConsumerWidget { final isFiltering = useState(false); final controller = useScrollController(); - + return SafeArea( bottom: false, child: Scaffold( - appBar: PageWindowTitleBar( - leading: const BackButton(), - centerTitle: true, - title: Text(isDownloads ? context.l10n.downloads : location), - backgroundColor: Colors.transparent, - ), - extendBodyBehindAppBar: true, - body: Column( - children: [ - const SizedBox(height: 56), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - const SizedBox(width: 5), - FilledButton( - onPressed: trackSnapshot.asData?.value != null - ? () async { - if (trackSnapshot.asData?.value.isNotEmpty == true) { - if (!isPlaylistPlaying) { - await playLocalTracks( - ref, - trackSnapshot.asData!.value[location] ?? [], - ); + appBar: PageWindowTitleBar( + leading: const BackButton(), + centerTitle: true, + title: Text(isDownloads ? context.l10n.downloads : location), + backgroundColor: Colors.transparent, + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + const SizedBox(width: 5), + FilledButton( + onPressed: trackSnapshot.asData?.value != null + ? () async { + if (trackSnapshot.asData?.value.isNotEmpty == + true) { + if (!isPlaylistPlaying) { + await playLocalTracks( + ref, + trackSnapshot.asData!.value[location] ?? [], + ); + } } } - } - : null, - child: Row( - children: [ - Text(context.l10n.play), - Icon( - isPlaylistPlaying ? SpotubeIcons.stop : SpotubeIcons.play, - ) - ], + : null, + child: Row( + children: [ + Text(context.l10n.play), + Icon( + isPlaylistPlaying + ? SpotubeIcons.stop + : SpotubeIcons.play, + ) + ], + ), ), - ), - const Spacer(), - ExpandableSearchButton( - isFiltering: isFiltering.value, - onPressed: (value) => isFiltering.value = value, - searchFocus: searchFocus, - ), - const SizedBox(width: 10), - SortTracksDropdown( - value: sortBy.value, - onChanged: (value) { - sortBy.value = value; - }, - ), - const SizedBox(width: 5), - FilledButton( - child: const Icon(SpotubeIcons.refresh), - onPressed: () { - ref.invalidate(localTracksProvider); - }, - ) - ], + const Spacer(), + ExpandableSearchButton( + isFiltering: isFiltering.value, + onPressed: (value) => isFiltering.value = value, + searchFocus: searchFocus, + ), + const SizedBox(width: 10), + SortTracksDropdown( + value: sortBy.value, + onChanged: (value) { + sortBy.value = value; + }, + ), + const SizedBox(width: 5), + FilledButton( + child: const Icon(SpotubeIcons.refresh), + onPressed: () { + ref.invalidate(localTracksProvider); + }, + ) + ], + ), ), - ), - ExpandableSearchField( - searchController: searchController, - searchFocus: searchFocus, - isFiltering: isFiltering.value, - onChangeFiltering: (value) => isFiltering.value = value, - ), - trackSnapshot.when( - data: (tracks) { - final sortedTracks = useMemoized(() { - return ServiceUtils.sortTracks(tracks[location] ?? [], sortBy.value); - }, [sortBy.value, tracks]); + ExpandableSearchField( + searchController: searchController, + searchFocus: searchFocus, + isFiltering: isFiltering.value, + onChangeFiltering: (value) => isFiltering.value = value, + ), + trackSnapshot.when( + data: (tracks) { + final sortedTracks = useMemoized(() { + return ServiceUtils.sortTracks( + tracks[location] ?? [], sortBy.value); + }, [sortBy.value, tracks]); - final filteredTracks = useMemoized(() { - if (searchController.text.isEmpty) { - return sortedTracks; + final filteredTracks = useMemoized(() { + if (searchController.text.isEmpty) { + return sortedTracks; + } + return sortedTracks + .map((e) => ( + weightedRatio( + "${e.name} - ${e.artists?.asString() ?? ""}", + searchController.text, + ), + e, + )) + .toList() + .sorted( + (a, b) => b.$1.compareTo(a.$1), + ) + .where((e) => e.$1 > 50) + .map((e) => e.$2) + .toList() + .toList(); + }, [searchController.text, sortedTracks]); + + if (!trackSnapshot.isLoading && filteredTracks.isEmpty) { + return const Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [NotFound()], + ), + ); } - return sortedTracks - .map((e) => ( - weightedRatio( - "${e.name} - ${e.artists?.asString() ?? ""}", - searchController.text, - ), - e, - )) - .toList() - .sorted( - (a, b) => b.$1.compareTo(a.$1), - ) - .where((e) => e.$1 > 50) - .map((e) => e.$2) - .toList() - .toList(); - }, [searchController.text, sortedTracks]); - if (!trackSnapshot.isLoading && filteredTracks.isEmpty) { - return const Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [NotFound()], - ), - ); - } - - return Expanded( - child: RefreshIndicator( - onRefresh: () async { - ref.invalidate(localTracksProvider); - }, - child: InterScrollbar( - controller: controller, - child: Skeletonizer( - enabled: trackSnapshot.isLoading, - child: ListView.builder( - controller: controller, - physics: const AlwaysScrollableScrollPhysics(), - itemCount: - trackSnapshot.isLoading ? 5 : filteredTracks.length, - itemBuilder: (context, index) { - if (trackSnapshot.isLoading) { - return TrackTile( - playlist: playlist, - track: FakeData.track, - index: index, - ); - } - - final track = filteredTracks[index]; - return TrackTile( - index: index, - playlist: playlist, - track: track, - userPlaylist: false, - onTap: () async { - await playLocalTracks( - ref, - sortedTracks, - currentTrack: track, + return Expanded( + child: RefreshIndicator( + onRefresh: () async { + ref.invalidate(localTracksProvider); + }, + child: InterScrollbar( + controller: controller, + child: Skeletonizer( + enabled: trackSnapshot.isLoading, + child: ListView.builder( + controller: controller, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: trackSnapshot.isLoading + ? 5 + : filteredTracks.length, + itemBuilder: (context, index) { + if (trackSnapshot.isLoading) { + return TrackTile( + playlist: playlist, + track: FakeData.track, + index: index, ); - }, - ); - }, + } + + final track = filteredTracks[index]; + return TrackTile( + index: index, + playlist: playlist, + track: track, + userPlaylist: false, + onTap: () async { + await playLocalTracks( + ref, + sortedTracks, + currentTrack: track, + ); + }, + ); + }, + ), ), ), ), - ), - ); - }, - loading: () => Expanded( - child: Skeletonizer( - enabled: true, - child: ListView.builder( - itemCount: 5, - itemBuilder: (context, index) => TrackTile( - track: FakeData.track, - index: index, - playlist: playlist, + ); + }, + loading: () => Expanded( + child: Skeletonizer( + enabled: true, + child: ListView.builder( + itemCount: 5, + itemBuilder: (context, index) => TrackTile( + track: FakeData.track, + index: index, + playlist: playlist, + ), ), ), ), - ), - error: (error, stackTrace) => - Text(error.toString() + stackTrace.toString()), - ) - ], - ) - ), + error: (error, stackTrace) => + Text(error.toString() + stackTrace.toString()), + ) + ], + )), ); } } diff --git a/lib/provider/proxy_playlist/player_listeners.dart b/lib/provider/proxy_playlist/player_listeners.dart index f86ad3d4..bf54fa90 100644 --- a/lib/provider/proxy_playlist/player_listeners.dart +++ b/lib/provider/proxy_playlist/player_listeners.dart @@ -1,4 +1,4 @@ -// ignore_for_file: invalid_use_of_protected_member +// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member import 'dart:async'; diff --git a/lib/provider/proxy_playlist/proxy_playlist.dart b/lib/provider/proxy_playlist/proxy_playlist.dart index f70301ff..b2241ad7 100644 --- a/lib/provider/proxy_playlist/proxy_playlist.dart +++ b/lib/provider/proxy_playlist/proxy_playlist.dart @@ -45,7 +45,14 @@ class ProxyPlaylist { } bool containsTrack(TrackSimple track) { - return tracks.firstWhereOrNull((element) => element.id == track.id) != null; + return tracks.firstWhereOrNull((element) { + if (element is LocalTrack && track is LocalTrack) { + return element.path == track.path; + } + + return element.id == track.id; + }) != + null; } bool containsTracks(Iterable tracks) { @@ -65,8 +72,8 @@ class ProxyPlaylist { /// Otherwise default super.toJson() is used static Map _makeAppropriateTrackJson(Track track) { return switch (track.runtimeType) { - LocalTrack() => track.toJson(), - SourcedTrack() => track.toJson(), + LocalTrack() => (track as LocalTrack).toJson(), + SourcedTrack() => (track as SourcedTrack).toJson(), _ => track.toJson(), }; } diff --git a/lib/services/audio_player/audio_player.dart b/lib/services/audio_player/audio_player.dart index 92de192b..d67652b4 100644 --- a/lib/services/audio_player/audio_player.dart +++ b/lib/services/audio_player/audio_player.dart @@ -13,6 +13,7 @@ import 'package:media_kit/media_kit.dart' as mk; import 'package:spotube/services/audio_player/loop_mode.dart'; import 'package:spotube/services/audio_player/playback_state.dart'; +import 'package:spotube/services/sourced_track/sourced_track.dart'; part 'audio_players_streams_mixin.dart'; part 'audio_player_impl.dart'; @@ -30,12 +31,18 @@ class SpotubeMedia extends mk.Media { : "http://${InternetAddress.loopbackIPv4.address}:${PlaybackServer.port}/stream/${track.id}", extras: { ...?extras, - "track": track.toJson(), + "track": switch (track) { + LocalTrack() => track.toJson(), + SourcedTrack() => track.toJson(), + _ => track.toJson(), + }, }, ); factory SpotubeMedia.fromMedia(mk.Media media) { - final track = Track.fromJson(media.extras?["track"]); + final track = media.uri.startsWith("http") + ? Track.fromJson(media.extras?["track"]) + : LocalTrack.fromJson(media.extras?["track"]); return SpotubeMedia(track); } } diff --git a/untranslated_messages.json b/untranslated_messages.json index 91b751eb..3ea0ca23 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -41,6 +41,13 @@ "local_tab" ], + "eu": [ + "local_library", + "add_library_location", + "remove_library_location", + "local_tab" + ], + "fa": [ "local_library", "add_library_location", @@ -48,6 +55,13 @@ "local_tab" ], + "fi": [ + "local_library", + "add_library_location", + "remove_library_location", + "local_tab" + ], + "fr": [ "local_library", "add_library_location", @@ -62,6 +76,13 @@ "local_tab" ], + "id": [ + "local_library", + "add_library_location", + "remove_library_location", + "local_tab" + ], + "it": [ "local_library", "add_library_location", @@ -76,6 +97,13 @@ "local_tab" ], + "ka": [ + "local_library", + "add_library_location", + "remove_library_location", + "local_tab" + ], + "ko": [ "local_library", "add_library_location",