mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: handle dublicated items in playback queue correctly #1852
This commit is contained in:
parent
36d161c05a
commit
9cb828bb55
@ -15,6 +15,7 @@ import 'package:spotube/components/tracks_view/sections/body/track_view_body_hea
|
|||||||
import 'package:spotube/components/tracks_view/sections/body/use_is_user_playlist.dart';
|
import 'package:spotube/components/tracks_view/sections/body/use_is_user_playlist.dart';
|
||||||
import 'package:spotube/components/tracks_view/track_view_props.dart';
|
import 'package:spotube/components/tracks_view/track_view_props.dart';
|
||||||
import 'package:spotube/components/tracks_view/track_view_provider.dart';
|
import 'package:spotube/components/tracks_view/track_view_provider.dart';
|
||||||
|
import 'package:spotube/extensions/list.dart';
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
@ -96,7 +97,7 @@ class TrackViewBodySection extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isActive || playlist.tracks.contains(track)) {
|
if (isActive || playlist.tracks.containsBy(track, (a) => a.id)) {
|
||||||
await playlistNotifier.jumpToTrack(track);
|
await playlistNotifier.jumpToTrack(track);
|
||||||
} else {
|
} else {
|
||||||
final tracks = await props.pagination.onFetchAll();
|
final tracks = await props.pagination.onFetchAll();
|
||||||
|
19
lib/extensions/list.dart
Normal file
19
lib/extensions/list.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
extension UniqueItemExtension<T> on List<T> {
|
||||||
|
List<T> unique(bool Function(T a, T b) equals) {
|
||||||
|
final copy = <T>[];
|
||||||
|
|
||||||
|
for (final item in this) {
|
||||||
|
if (copy.any((element) => equals(element, item))) continue;
|
||||||
|
copy.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsBy(T item, dynamic Function(T a) fn) {
|
||||||
|
for (final el in this) {
|
||||||
|
if (fn(el) == fn(item)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import 'package:spotube/components/titlebar/titlebar.dart';
|
|||||||
import 'package:spotube/components/track_tile/track_options.dart';
|
import 'package:spotube/components/track_tile/track_options.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
|
import 'package:spotube/extensions/list.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
@ -167,7 +168,8 @@ class TrackPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Gap(5),
|
const Gap(5),
|
||||||
if (!isActive &&
|
if (!isActive &&
|
||||||
!playlist.tracks.contains(track))
|
!playlist.tracks
|
||||||
|
.containsBy(track, (t) => t.id))
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
label: Text(context.l10n.queue),
|
label: Text(context.l10n.queue),
|
||||||
@ -177,7 +179,8 @@ class TrackPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
const Gap(5),
|
const Gap(5),
|
||||||
if (!isActive &&
|
if (!isActive &&
|
||||||
!playlist.tracks.contains(track))
|
!playlist.tracks
|
||||||
|
.containsBy(track, (t) => t.id))
|
||||||
IconButton.outlined(
|
IconButton.outlined(
|
||||||
icon:
|
icon:
|
||||||
const Icon(SpotubeIcons.lightning),
|
const Icon(SpotubeIcons.lightning),
|
||||||
|
@ -4,6 +4,7 @@ import 'package:drift/drift.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:spotify/spotify.dart' hide Playlist;
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
|
import 'package:spotube/extensions/list.dart';
|
||||||
import 'package:spotube/extensions/track.dart';
|
import 'package:spotube/extensions/track.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
@ -256,6 +257,10 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
for (int i = 0; i < tracks.length; i++) {
|
for (int i = 0; i < tracks.length; i++) {
|
||||||
final track = tracks.elementAt(i);
|
final track = tracks.elementAt(i);
|
||||||
|
|
||||||
|
if (state.tracks.any((element) => _compareTracks(element, track))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
await audioPlayer.addTrackAt(
|
await audioPlayer.addTrackAt(
|
||||||
SpotubeMedia(track),
|
SpotubeMedia(track),
|
||||||
max(state.playlist.index, 0) + i + 1,
|
max(state.playlist.index, 0) + i + 1,
|
||||||
@ -265,6 +270,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
|
|
||||||
Future<void> addTrack(Track track) async {
|
Future<void> addTrack(Track track) async {
|
||||||
if (_blacklist.contains(track)) return;
|
if (_blacklist.contains(track)) return;
|
||||||
|
if (state.tracks.any((element) => _compareTracks(element, track))) return;
|
||||||
await audioPlayer.addTrack(SpotubeMedia(track));
|
await audioPlayer.addTrack(SpotubeMedia(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,13 +295,23 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _compareTracks(Track a, Track b) {
|
||||||
|
if ((a is LocalTrack && b is! LocalTrack) ||
|
||||||
|
(a is! LocalTrack && b is LocalTrack)) return false;
|
||||||
|
|
||||||
|
return a is LocalTrack && b is LocalTrack
|
||||||
|
? (a).path == (b).path
|
||||||
|
: a.id == b.id;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> load(
|
Future<void> load(
|
||||||
List<Track> tracks, {
|
List<Track> tracks, {
|
||||||
int initialIndex = 0,
|
int initialIndex = 0,
|
||||||
bool autoPlay = false,
|
bool autoPlay = false,
|
||||||
}) async {
|
}) async {
|
||||||
final medias =
|
final medias = (_blacklist.filter(tracks).toList() as List<Track>)
|
||||||
(_blacklist.filter(tracks).toList() as List<Track>).asMediaList();
|
.asMediaList()
|
||||||
|
.unique((a, b) => _compareTracks(a.track, b.track));
|
||||||
|
|
||||||
// Giving the initial track a boost so MediaKit won't skip
|
// Giving the initial track a boost so MediaKit won't skip
|
||||||
// because of timeout
|
// because of timeout
|
||||||
|
@ -758,14 +758,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.20.5"
|
version: "0.20.5"
|
||||||
flutter_hooks_lint:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: flutter_hooks_lint
|
|
||||||
sha256: fc6e18505b597737e5d620656e340ac60e7a58980cca29e18c1216bd15083674
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.0"
|
|
||||||
flutter_inappwebview:
|
flutter_inappwebview:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -158,7 +158,6 @@ dev_dependencies:
|
|||||||
xml: ^6.5.0
|
xml: ^6.5.0
|
||||||
io: ^1.0.4
|
io: ^1.0.4
|
||||||
drift_dev: ^2.18.0
|
drift_dev: ^2.18.0
|
||||||
flutter_hooks_lint: ^1.2.0
|
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
uuid: ^4.4.0
|
uuid: ^4.4.0
|
||||||
|
Loading…
Reference in New Issue
Block a user