fix(mkPlayer): remove method and wrong active index on modifying playlist

This commit is contained in:
Kingkor Roy Tirtho 2023-05-15 11:02:32 +06:00
parent eaf65b6db2
commit 3bafa7b80c
5 changed files with 102 additions and 110 deletions

View File

@ -28,10 +28,12 @@ enum SpotubeTrackMatchAlgorithm {
authenticPopular, authenticPopular,
} }
typedef SkipSegment = ({int start, int end});
class SpotubeTrack extends Track { class SpotubeTrack extends Track {
final PipedStreamResponse ytTrack; final PipedStreamResponse ytTrack;
final String ytUri; final String ytUri;
final List<Map<String, int>> skipSegments; final List<SkipSegment> skipSegments;
final List<PipedSearchItemStream> siblings; final List<PipedSearchItemStream> siblings;
SpotubeTrack( SpotubeTrack(
@ -82,7 +84,7 @@ class SpotubeTrack extends Track {
} }
} }
static Future<List<Map<String, int>>> getSkipSegments( static Future<List<SkipSegment>> getSkipSegments(
String id, String id,
UserPreferences preferences, UserPreferences preferences,
) async { ) async {
@ -107,23 +109,23 @@ class SpotubeTrack extends Track {
)); ));
if (res.body == "Not Found") { if (res.body == "Not Found") {
return List.castFrom<dynamic, Map<String, int>>([]); return List.castFrom<dynamic, SkipSegment>([]);
} }
final data = jsonDecode(res.body) as List; final data = jsonDecode(res.body) as List;
final segments = data.map((obj) { final segments = data.map((obj) {
return Map.castFrom<String, dynamic, String, int>({ return (
"start": obj["segment"].first.toInt(), start: obj["segment"].first.toInt(),
"end": obj["segment"].last.toInt(), end: obj["segment"].last.toInt(),
}); );
}).toList(); }).toList();
getLogger(SpotubeTrack).v( getLogger(SpotubeTrack).v(
"[SponsorBlock] successfully fetched skip segments for $id", "[SponsorBlock] successfully fetched skip segments for $id",
); );
return List.castFrom<dynamic, Map<String, int>>(segments); return List.castFrom<dynamic, SkipSegment>(segments);
} catch (e, stack) { } catch (e, stack) {
Catcher.reportCheckedError(e, stack); Catcher.reportCheckedError(e, stack);
return List.castFrom<dynamic, Map<String, int>>([]); return List.castFrom<dynamic, SkipSegment>([]);
} }
} }
@ -155,7 +157,7 @@ class SpotubeTrack extends Track {
PipedStreamResponse ytVideo; PipedStreamResponse ytVideo;
PipedAudioStream ytStream; PipedAudioStream ytStream;
List<PipedSearchItemStream> siblings = []; List<PipedSearchItemStream> siblings = [];
List<Map<String, int>> skipSegments = []; List<SkipSegment> skipSegments = [];
if (cachedTrack != null) { if (cachedTrack != null) {
final responses = await Future.wait( final responses = await Future.wait(
[ [
@ -167,7 +169,7 @@ class SpotubeTrack extends Track {
], ],
); );
ytVideo = responses.first as PipedStreamResponse; ytVideo = responses.first as PipedStreamResponse;
skipSegments = responses.last as List<Map<String, int>>; skipSegments = responses.last as List<SkipSegment>;
ytStream = getStreamInfo(ytVideo, preferences.audioQuality); ytStream = getStreamInfo(ytVideo, preferences.audioQuality);
} else { } else {
final videos = await PipedSpotube.client final videos = await PipedSpotube.client
@ -187,7 +189,7 @@ class SpotubeTrack extends Track {
], ],
); );
ytVideo = responses.first as PipedStreamResponse; ytVideo = responses.first as PipedStreamResponse;
skipSegments = responses.last as List<Map<String, int>>; skipSegments = responses.last as List<SkipSegment>;
ytStream = getStreamInfo(ytVideo, preferences.audioQuality); ytStream = getStreamInfo(ytVideo, preferences.audioQuality);
} }
@ -239,7 +241,7 @@ class SpotubeTrack extends Track {
) async { ) async {
if (siblings.none((element) => element.id == video.id)) return null; if (siblings.none((element) => element.id == video.id)) return null;
final [PipedStreamResponse ytVideo, List<Map<String, int>> skipSegments] = final [PipedStreamResponse ytVideo, List<SkipSegment> skipSegments] =
await Future.wait<dynamic>( await Future.wait<dynamic>(
[ [
PipedSpotube.client.streams(video.id), PipedSpotube.client.streams(video.id),
@ -329,8 +331,7 @@ class SpotubeTrack extends Track {
track: Track.fromJson(map), track: Track.fromJson(map),
ytTrack: PipedStreamResponse.fromJson(map["ytTrack"]), ytTrack: PipedStreamResponse.fromJson(map["ytTrack"]),
ytUri: map["ytUri"], ytUri: map["ytUri"],
skipSegments: skipSegments: List.castFrom<dynamic, SkipSegment>(map["skipSegments"]),
List.castFrom<dynamic, Map<String, int>>(map["skipSegments"]),
siblings: List.castFrom<dynamic, Map<String, dynamic>>(map["siblings"]) siblings: List.castFrom<dynamic, Map<String, dynamic>>(map["siblings"])
.map((sibling) => PipedSearchItemStream.fromJson(sibling)) .map((sibling) => PipedSearchItemStream.fromJson(sibling))
.toList(), .toList(),

View File

@ -57,6 +57,8 @@ mixin NextFetcher on StateNotifier<ProxyPlaylist> {
return source.startsWith('https://youtube.com/unplayable.m4a?id='); 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 /// Returns [Track.id] from [isUnPlayable] source that is not playable
String getIdFromUnPlayable(String source) { String getIdFromUnPlayable(String source) {
return source.replaceFirst('https://youtube.com/unplayable.m4a?id=', ''); return source.replaceFirst('https://youtube.com/unplayable.m4a?id=', '');

View File

@ -101,7 +101,7 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
audioPlayer.percentCompletedStream(99).listen((_) async { audioPlayer.percentCompletedStream(99).listen((_) async {
final nextSource = final nextSource =
audioPlayer.sources.elementAtOrNull(audioPlayer.currentIndex + 1); audioPlayer.sources.elementAtOrNull(audioPlayer.currentIndex + 1);
if (nextSource == null || !isUnPlayable(nextSource)) return; if (nextSource == null || isPlayable(nextSource)) return;
await audioPlayer.pause(); await audioPlayer.pause();
}); });
@ -118,20 +118,17 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
if (activeTrack.skipSegments.isNotEmpty == true && if (activeTrack.skipSegments.isNotEmpty == true &&
preferences.skipSponsorSegments) { preferences.skipSponsorSegments) {
for (final segment in activeTrack.skipSegments) { for (final segment in activeTrack.skipSegments) {
if (pos.inSeconds < segment["start"]! || if (pos.inSeconds < segment.start || pos.inSeconds >= segment.end) {
pos.inSeconds >= segment["end"]!) continue; continue;
await audioPlayer.seek(Duration(seconds: segment["end"]!)); }
await audioPlayer.seek(Duration(seconds: segment.end));
} }
} }
}); });
}(); }();
} }
Future<SpotubeTrack?> ensureNthSourcePlayable( Future<SpotubeTrack?> ensureNthSourcePlayable(int n) async {
int n, {
bool softReplace = false,
bool exclusive = false,
}) async {
final sources = audioPlayer.sources; final sources = audioPlayer.sources;
if (n < 0 || n > sources.length - 1) return null; if (n < 0 || n > sources.length - 1) return null;
final nthSource = sources.elementAtOrNull(n); final nthSource = sources.elementAtOrNull(n);
@ -150,13 +147,10 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
if (nthSource == nthFetchedTrack.ytUri) return null; if (nthSource == nthFetchedTrack.ytUri) return null;
if (!softReplace) {
await audioPlayer.replaceSource( await audioPlayer.replaceSource(
nthSource, nthSource,
nthFetchedTrack.ytUri, nthFetchedTrack.ytUri,
exclusive: exclusive,
); );
}
return nthFetchedTrack; return nthFetchedTrack;
} }
@ -214,7 +208,9 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
await SpotubeTrack.fetchFromTrack(tracks[initialIndex], preferences); await SpotubeTrack.fetchFromTrack(tracks[initialIndex], preferences);
state = state.copyWith( state = state.copyWith(
tracks: mergeTracks([addableTrack], tracks), active: initialIndex); tracks: mergeTracks([addableTrack], tracks),
active: initialIndex,
);
await audioPlayer.openPlaylist( await audioPlayer.openPlaylist(
state.tracks.map(makeAppropriateSource).toList(), state.tracks.map(makeAppropriateSource).toList(),

View File

@ -458,19 +458,9 @@ class SpotubeAudioPlayer {
final oldSourceIndex = sources.indexOf(oldSource); final oldSourceIndex = sources.indexOf(oldSource);
if (oldSourceIndex == -1) return; if (oldSourceIndex == -1) return;
// if (mkSupportedPlatform) { if (mkSupportedPlatform) {
// final sourcesCp = sources.toList(); _mkPlayer!.replace(oldSource, newSource);
// sourcesCp[oldSourceIndex] = newSource; } else {
// 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 addTrack(newSource);
await removeTrack(oldSourceIndex); await removeTrack(oldSourceIndex);
@ -486,7 +476,7 @@ class SpotubeAudioPlayer {
await moveTrack(newSourceIndex, oldSourceIndex); await moveTrack(newSourceIndex, oldSourceIndex);
newSourceIndex = sources.indexOf(newSource); newSourceIndex = sources.indexOf(newSource);
} }
// } }
} }
Future<void> clearPlaylist() async { Future<void> clearPlaylist() async {

View File

@ -67,10 +67,10 @@ class MkPlayerWithState extends Player {
Stream<PlaylistMode> get loopModeStream => _loopModeStream.stream; Stream<PlaylistMode> get loopModeStream => _loopModeStream.stream;
Stream<Playlist> get playlistStream => _playlistStream.stream; Stream<Playlist> get playlistStream => _playlistStream.stream;
Stream<int> get indexChangeStream { Stream<int> get indexChangeStream {
int index = playlist.index; int oldIndex = playlist.index;
return playlistStream.map((event) => event.index).where((event) { return playlistStream.map((event) => event.index).where((newIndex) {
if (event != index) { if (newIndex != oldIndex) {
index = event; oldIndex = newIndex;
return true; return true;
} }
return false; return false;
@ -116,16 +116,13 @@ class MkPlayerWithState extends Player {
Future<void> stop() async { Future<void> stop() async {
await pause(); await pause();
await seek(Duration.zero);
_loopMode = PlaylistMode.none; _loopMode = PlaylistMode.none;
_shuffled = false; _shuffled = false;
_playlist = null; _playlist = null;
_tempMedias = null; _tempMedias = null;
_playerStateStream.add(AudioPlaybackState.stopped); _playerStateStream.add(AudioPlaybackState.stopped);
for (int i = 0; i < state.playlist.medias.length; i++) {
await remove(i);
}
} }
@override @override
@ -141,6 +138,7 @@ class MkPlayerWithState extends Player {
Playable playable, { Playable playable, {
bool play = true, bool play = true,
}) async { }) async {
await stop();
if (playable is Playlist) { if (playable is Playlist) {
playlist = playable; playlist = playable;
super.open(playable.medias[playable.index], play: play); super.open(playable.medias[playable.index], play: play);
@ -154,14 +152,15 @@ class MkPlayerWithState extends Player {
return null; return null;
} }
if (loopMode == PlaylistMode.loop && final isLast = _playlist!.index == _playlist!.medias.length - 1;
_playlist!.index == _playlist!.medias.length - 1) {
playlist = _playlist!.copyWith(index: 0);
} else {
playlist = _playlist!.copyWith(index: _playlist!.index + 1);
}
if (loopMode == PlaylistMode.loop && isLast) {
playlist = _playlist!.copyWith(index: 0);
return super.open(_playlist!.medias[_playlist!.index], play: true); 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);
}
} }
@override @override
@ -170,11 +169,11 @@ class MkPlayerWithState extends Player {
if (loopMode == PlaylistMode.loop && _playlist!.index == 0) { if (loopMode == PlaylistMode.loop && _playlist!.index == 0) {
playlist = _playlist!.copyWith(index: _playlist!.medias.length - 1); playlist = _playlist!.copyWith(index: _playlist!.medias.length - 1);
} else {
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);
} else if (_playlist!.index != 0) {
playlist = _playlist!.copyWith(index: _playlist!.index - 1);
return super.open(_playlist!.medias[_playlist!.index], play: true);
}
} }
@override @override
@ -184,7 +183,7 @@ class MkPlayerWithState extends Player {
} }
playlist = _playlist!.copyWith(index: index); playlist = _playlist!.copyWith(index: index);
return super.open(_playlist!.medias[_playlist!.index], play: true); return super.open(_playlist!.medias[index], play: true);
} }
@override @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 @override
FutureOr<void> add(Media media) { FutureOr<void> add(Media media) {
if (_playlist == null) return null; 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 @override
FutureOr<void> remove(int index) async { FutureOr<void> remove(int index) async {
if (_playlist == null || index >= _playlist!.medias.length) return null; if (_playlist == null ||
index < 0 ||
final item = _playlist!.medias.elementAtOrNull(index); index > _playlist!.medias.length - 1 ||
if (shuffled && _tempMedias != null && item != null) { _playlist!.index == index) {
_tempMedias!.remove(item); return null;
} }
if (_playlist!.index == index) { final targetItem = _playlist!.medias.elementAtOrNull(index);
final hasNext = _playlist!.index + 1 < _playlist!.medias.length; if (targetItem == null) return null;
final hasPrevious = _playlist!.index - 1 >= 0;
if (hasNext) { if (shuffled && _tempMedias != null) {
playlist = _playlist!.copyWith( _tempMedias!.remove(targetItem);
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.toList()..removeAt(index);
final newMedias = _playlist!.medias..removeAt(index);
playlist = _playlist!.copyWith( playlist = _playlist!.copyWith(
medias: newMedias, medias: newMedias,
index: newMedias.indexOf(active), index: newMedias.indexOf(_playlist!.medias[_playlist!.index]),
); );
} }
}
} }