From 3bafa7b80c963fa52b90ed4cb1393fb121cac713 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 15 May 2023 11:02:32 +0600 Subject: [PATCH] fix(mkPlayer): remove method and wrong active index on modifying playlist --- lib/models/spotube_track.dart | 31 ++--- .../proxy_playlist/next_fetcher_mixin.dart | 2 + .../proxy_playlist_provider.dart | 30 +++-- lib/services/audio_player/audio_player.dart | 40 +++---- .../audio_player/mk_state_player.dart | 107 +++++++++--------- 5 files changed, 101 insertions(+), 109 deletions(-) diff --git a/lib/models/spotube_track.dart b/lib/models/spotube_track.dart index 8574e1025..a6fe070e0 100644 --- a/lib/models/spotube_track.dart +++ b/lib/models/spotube_track.dart @@ -28,10 +28,12 @@ enum SpotubeTrackMatchAlgorithm { authenticPopular, } +typedef SkipSegment = ({int start, int end}); + class SpotubeTrack extends Track { final PipedStreamResponse ytTrack; final String ytUri; - final List> skipSegments; + final List skipSegments; final List siblings; SpotubeTrack( @@ -82,7 +84,7 @@ class SpotubeTrack extends Track { } } - static Future>> getSkipSegments( + static Future> getSkipSegments( String id, UserPreferences preferences, ) async { @@ -107,23 +109,23 @@ class SpotubeTrack extends Track { )); if (res.body == "Not Found") { - return List.castFrom>([]); + return List.castFrom([]); } final data = jsonDecode(res.body) as List; final segments = data.map((obj) { - return Map.castFrom({ - "start": obj["segment"].first.toInt(), - "end": obj["segment"].last.toInt(), - }); + return ( + start: obj["segment"].first.toInt(), + end: obj["segment"].last.toInt(), + ); }).toList(); getLogger(SpotubeTrack).v( "[SponsorBlock] successfully fetched skip segments for $id", ); - return List.castFrom>(segments); + return List.castFrom(segments); } catch (e, stack) { Catcher.reportCheckedError(e, stack); - return List.castFrom>([]); + return List.castFrom([]); } } @@ -155,7 +157,7 @@ class SpotubeTrack extends Track { PipedStreamResponse ytVideo; PipedAudioStream ytStream; List siblings = []; - List> skipSegments = []; + List skipSegments = []; if (cachedTrack != null) { final responses = await Future.wait( [ @@ -167,7 +169,7 @@ class SpotubeTrack extends Track { ], ); ytVideo = responses.first as PipedStreamResponse; - skipSegments = responses.last as List>; + skipSegments = responses.last as List; ytStream = getStreamInfo(ytVideo, preferences.audioQuality); } else { final videos = await PipedSpotube.client @@ -187,7 +189,7 @@ class SpotubeTrack extends Track { ], ); ytVideo = responses.first as PipedStreamResponse; - skipSegments = responses.last as List>; + skipSegments = responses.last as List; ytStream = getStreamInfo(ytVideo, preferences.audioQuality); } @@ -239,7 +241,7 @@ class SpotubeTrack extends Track { ) async { if (siblings.none((element) => element.id == video.id)) return null; - final [PipedStreamResponse ytVideo, List> skipSegments] = + final [PipedStreamResponse ytVideo, List skipSegments] = await Future.wait( [ PipedSpotube.client.streams(video.id), @@ -329,8 +331,7 @@ class SpotubeTrack extends Track { track: Track.fromJson(map), ytTrack: PipedStreamResponse.fromJson(map["ytTrack"]), ytUri: map["ytUri"], - skipSegments: - List.castFrom>(map["skipSegments"]), + skipSegments: List.castFrom(map["skipSegments"]), siblings: List.castFrom>(map["siblings"]) .map((sibling) => PipedSearchItemStream.fromJson(sibling)) .toList(), diff --git a/lib/provider/proxy_playlist/next_fetcher_mixin.dart b/lib/provider/proxy_playlist/next_fetcher_mixin.dart index 462fe7cee..9588702f4 100644 --- a/lib/provider/proxy_playlist/next_fetcher_mixin.dart +++ b/lib/provider/proxy_playlist/next_fetcher_mixin.dart @@ -57,6 +57,8 @@ mixin NextFetcher on StateNotifier { return source.startsWith('https://youtube.com/unplayable.m4a?id='); } + bool isPlayable(String source) => !isUnPlayable(source); + /// Returns [Track.id] from [isUnPlayable] source that is not playable String getIdFromUnPlayable(String source) { return source.replaceFirst('https://youtube.com/unplayable.m4a?id=', ''); diff --git a/lib/provider/proxy_playlist/proxy_playlist_provider.dart b/lib/provider/proxy_playlist/proxy_playlist_provider.dart index 2a2a86f35..12cbbfbc3 100644 --- a/lib/provider/proxy_playlist/proxy_playlist_provider.dart +++ b/lib/provider/proxy_playlist/proxy_playlist_provider.dart @@ -101,7 +101,7 @@ class ProxyPlaylistNotifier extends StateNotifier audioPlayer.percentCompletedStream(99).listen((_) async { final nextSource = audioPlayer.sources.elementAtOrNull(audioPlayer.currentIndex + 1); - if (nextSource == null || !isUnPlayable(nextSource)) return; + if (nextSource == null || isPlayable(nextSource)) return; await audioPlayer.pause(); }); @@ -118,20 +118,17 @@ class ProxyPlaylistNotifier extends StateNotifier if (activeTrack.skipSegments.isNotEmpty == true && preferences.skipSponsorSegments) { for (final segment in activeTrack.skipSegments) { - if (pos.inSeconds < segment["start"]! || - pos.inSeconds >= segment["end"]!) continue; - await audioPlayer.seek(Duration(seconds: segment["end"]!)); + if (pos.inSeconds < segment.start || pos.inSeconds >= segment.end) { + continue; + } + await audioPlayer.seek(Duration(seconds: segment.end)); } } }); }(); } - Future ensureNthSourcePlayable( - int n, { - bool softReplace = false, - bool exclusive = false, - }) async { + Future ensureNthSourcePlayable(int n) async { final sources = audioPlayer.sources; if (n < 0 || n > sources.length - 1) return null; final nthSource = sources.elementAtOrNull(n); @@ -150,13 +147,10 @@ class ProxyPlaylistNotifier extends StateNotifier if (nthSource == nthFetchedTrack.ytUri) return null; - if (!softReplace) { - await audioPlayer.replaceSource( - nthSource, - nthFetchedTrack.ytUri, - exclusive: exclusive, - ); - } + await audioPlayer.replaceSource( + nthSource, + nthFetchedTrack.ytUri, + ); return nthFetchedTrack; } @@ -214,7 +208,9 @@ class ProxyPlaylistNotifier extends StateNotifier await SpotubeTrack.fetchFromTrack(tracks[initialIndex], preferences); state = state.copyWith( - tracks: mergeTracks([addableTrack], tracks), active: initialIndex); + tracks: mergeTracks([addableTrack], tracks), + active: initialIndex, + ); await audioPlayer.openPlaylist( state.tracks.map(makeAppropriateSource).toList(), diff --git a/lib/services/audio_player/audio_player.dart b/lib/services/audio_player/audio_player.dart index c5cb75294..561c97000 100644 --- a/lib/services/audio_player/audio_player.dart +++ b/lib/services/audio_player/audio_player.dart @@ -458,35 +458,25 @@ class SpotubeAudioPlayer { final oldSourceIndex = sources.indexOf(oldSource); if (oldSourceIndex == -1) return; - // if (mkSupportedPlatform) { - // final sourcesCp = sources.toList(); - // sourcesCp[oldSourceIndex] = newSource; - - // await _mkPlayer!.open( - // mk.Playlist( - // sourcesCp.map(mk.Media.new).toList(), - // index: currentIndex, - // ), - // play: false, - // ); - // if (exclusive) await jumpTo(oldSourceIndex); - // } else { - await addTrack(newSource); - await removeTrack(oldSourceIndex); + if (mkSupportedPlatform) { + _mkPlayer!.replace(oldSource, newSource); + } else { + await addTrack(newSource); + await removeTrack(oldSourceIndex); - int newSourceIndex = sources.indexOf(newSource); - while (newSourceIndex == -1) { - await Future.delayed(const Duration(milliseconds: 100)); - newSourceIndex = sources.indexOf(newSource); - } - await moveTrack(newSourceIndex, oldSourceIndex); - newSourceIndex = sources.indexOf(newSource); - while (newSourceIndex != oldSourceIndex) { - await Future.delayed(const Duration(milliseconds: 100)); + int newSourceIndex = sources.indexOf(newSource); + while (newSourceIndex == -1) { + await Future.delayed(const Duration(milliseconds: 100)); + newSourceIndex = sources.indexOf(newSource); + } await moveTrack(newSourceIndex, oldSourceIndex); newSourceIndex = sources.indexOf(newSource); + while (newSourceIndex != oldSourceIndex) { + await Future.delayed(const Duration(milliseconds: 100)); + await moveTrack(newSourceIndex, oldSourceIndex); + newSourceIndex = sources.indexOf(newSource); + } } - // } } Future clearPlaylist() async { diff --git a/lib/services/audio_player/mk_state_player.dart b/lib/services/audio_player/mk_state_player.dart index b5f9b1cf9..2cabc55f4 100644 --- a/lib/services/audio_player/mk_state_player.dart +++ b/lib/services/audio_player/mk_state_player.dart @@ -67,10 +67,10 @@ class MkPlayerWithState extends Player { Stream get loopModeStream => _loopModeStream.stream; Stream get playlistStream => _playlistStream.stream; Stream get indexChangeStream { - int index = playlist.index; - return playlistStream.map((event) => event.index).where((event) { - if (event != index) { - index = event; + int oldIndex = playlist.index; + return playlistStream.map((event) => event.index).where((newIndex) { + if (newIndex != oldIndex) { + oldIndex = newIndex; return true; } return false; @@ -116,16 +116,13 @@ class MkPlayerWithState extends Player { Future stop() async { await pause(); + await seek(Duration.zero); _loopMode = PlaylistMode.none; _shuffled = false; _playlist = null; _tempMedias = null; _playerStateStream.add(AudioPlaybackState.stopped); - - for (int i = 0; i < state.playlist.medias.length; i++) { - await remove(i); - } } @override @@ -141,6 +138,7 @@ class MkPlayerWithState extends Player { Playable playable, { bool play = true, }) async { + await stop(); if (playable is Playlist) { playlist = playable; super.open(playable.medias[playable.index], play: play); @@ -154,14 +152,15 @@ class MkPlayerWithState extends Player { return null; } - if (loopMode == PlaylistMode.loop && - _playlist!.index == _playlist!.medias.length - 1) { + final isLast = _playlist!.index == _playlist!.medias.length - 1; + + if (loopMode == PlaylistMode.loop && isLast) { playlist = _playlist!.copyWith(index: 0); - } else { + return super.open(_playlist!.medias[_playlist!.index], play: true); + } else if (!isLast) { playlist = _playlist!.copyWith(index: _playlist!.index + 1); + return super.open(_playlist!.medias[_playlist!.index], play: true); } - - return super.open(_playlist!.medias[_playlist!.index], play: true); } @override @@ -170,11 +169,11 @@ class MkPlayerWithState extends Player { if (loopMode == PlaylistMode.loop && _playlist!.index == 0) { playlist = _playlist!.copyWith(index: _playlist!.medias.length - 1); - } else { + return super.open(_playlist!.medias[_playlist!.index], play: true); + } else if (_playlist!.index != 0) { playlist = _playlist!.copyWith(index: _playlist!.index - 1); + return super.open(_playlist!.medias[_playlist!.index], play: true); } - - return super.open(_playlist!.medias[_playlist!.index], play: true); } @override @@ -184,7 +183,7 @@ class MkPlayerWithState extends Player { } playlist = _playlist!.copyWith(index: index); - return super.open(_playlist!.medias[_playlist!.index], play: true); + return super.open(_playlist!.medias[index], play: true); } @override @@ -210,6 +209,27 @@ class MkPlayerWithState extends Player { ); } + /// This replaces the old source with a new one + /// + /// This doesn't work when [playlist] is null + /// Or, when the current media is the one to be replaced + void replace(String oldUrl, String newUrl) { + if (_playlist == null || + _playlist!.medias[_playlist!.index].uri == oldUrl) { + return; + } + + for (var i = 0; i < _playlist!.medias.length - 1; i++) { + final media = _playlist!.medias[i]; + if (media.uri == oldUrl) { + final newMedias = _playlist!.medias.toList(); + newMedias[i] = Media(newUrl, extras: media.extras); + playlist = _playlist!.copyWith(medias: newMedias); + break; + } + } + } + @override FutureOr add(Media media) { if (_playlist == null) return null; @@ -223,45 +243,28 @@ class MkPlayerWithState extends Player { } } + /// Doesn't work when active media is the one to be removed @override FutureOr remove(int index) async { - if (_playlist == null || index >= _playlist!.medias.length) return null; - - final item = _playlist!.medias.elementAtOrNull(index); - if (shuffled && _tempMedias != null && item != null) { - _tempMedias!.remove(item); + if (_playlist == null || + index < 0 || + index > _playlist!.medias.length - 1 || + _playlist!.index == index) { + return null; } - if (_playlist!.index == index) { - final hasNext = _playlist!.index + 1 < _playlist!.medias.length; - final hasPrevious = _playlist!.index - 1 >= 0; - - if (hasNext) { - playlist = _playlist!.copyWith( - index: _playlist!.index + 1, - medias: _playlist!.medias..removeAt(index), - ); - super.open(_playlist!.medias[_playlist!.index], play: true); - } else if (hasPrevious) { - playlist = _playlist!.copyWith( - index: _playlist!.index - 1, - medias: _playlist!.medias..removeAt(index), - ); - super.open(_playlist!.medias[_playlist!.index], play: true); - } else { - playlist = _playlist!.copyWith( - medias: _playlist!.medias..removeAt(index), - index: -1, - ); - await stop(); - } - } else { - final active = _playlist!.medias[_playlist!.index]; - final newMedias = _playlist!.medias..removeAt(index); - playlist = _playlist!.copyWith( - medias: newMedias, - index: newMedias.indexOf(active), - ); + final targetItem = _playlist!.medias.elementAtOrNull(index); + if (targetItem == null) return null; + + if (shuffled && _tempMedias != null) { + _tempMedias!.remove(targetItem); } + + final newMedias = _playlist!.medias.toList()..removeAt(index); + + playlist = _playlist!.copyWith( + medias: newMedias, + index: newMedias.indexOf(_playlist!.medias[_playlist!.index]), + ); } }