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,
}
typedef SkipSegment = ({int start, int end});
class SpotubeTrack extends Track {
final PipedStreamResponse ytTrack;
final String ytUri;
final List<Map<String, int>> skipSegments;
final List<SkipSegment> skipSegments;
final List<PipedSearchItemStream> siblings;
SpotubeTrack(
@ -82,7 +84,7 @@ class SpotubeTrack extends Track {
}
}
static Future<List<Map<String, int>>> getSkipSegments(
static Future<List<SkipSegment>> getSkipSegments(
String id,
UserPreferences preferences,
) async {
@ -107,23 +109,23 @@ class SpotubeTrack extends Track {
));
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 segments = data.map((obj) {
return Map.castFrom<String, dynamic, String, int>({
"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<dynamic, Map<String, int>>(segments);
return List.castFrom<dynamic, SkipSegment>(segments);
} catch (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;
PipedAudioStream ytStream;
List<PipedSearchItemStream> siblings = [];
List<Map<String, int>> skipSegments = [];
List<SkipSegment> 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<Map<String, int>>;
skipSegments = responses.last as List<SkipSegment>;
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<Map<String, int>>;
skipSegments = responses.last as List<SkipSegment>;
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<Map<String, int>> skipSegments] =
final [PipedStreamResponse ytVideo, List<SkipSegment> skipSegments] =
await Future.wait<dynamic>(
[
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<dynamic, Map<String, int>>(map["skipSegments"]),
skipSegments: List.castFrom<dynamic, SkipSegment>(map["skipSegments"]),
siblings: List.castFrom<dynamic, Map<String, dynamic>>(map["siblings"])
.map((sibling) => PipedSearchItemStream.fromJson(sibling))
.toList(),

View File

@ -57,6 +57,8 @@ mixin NextFetcher on StateNotifier<ProxyPlaylist> {
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=', '');

View File

@ -101,7 +101,7 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
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<ProxyPlaylist>
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<SpotubeTrack?> ensureNthSourcePlayable(
int n, {
bool softReplace = false,
bool exclusive = false,
}) async {
Future<SpotubeTrack?> 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<ProxyPlaylist>
if (nthSource == nthFetchedTrack.ytUri) return null;
if (!softReplace) {
await audioPlayer.replaceSource(
nthSource,
nthFetchedTrack.ytUri,
exclusive: exclusive,
);
}
return nthFetchedTrack;
}
@ -214,7 +208,9 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
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(),

View File

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

View File

@ -67,10 +67,10 @@ class MkPlayerWithState extends Player {
Stream<PlaylistMode> get loopModeStream => _loopModeStream.stream;
Stream<Playlist> get playlistStream => _playlistStream.stream;
Stream<int> 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<void> 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) {
playlist = _playlist!.copyWith(index: 0);
} else {
playlist = _playlist!.copyWith(index: _playlist!.index + 1);
}
final isLast = _playlist!.index == _playlist!.medias.length - 1;
if (loopMode == PlaylistMode.loop && isLast) {
playlist = _playlist!.copyWith(index: 0);
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
@ -170,11 +169,11 @@ class MkPlayerWithState extends Player {
if (loopMode == PlaylistMode.loop && _playlist!.index == 0) {
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);
} else if (_playlist!.index != 0) {
playlist = _playlist!.copyWith(index: _playlist!.index - 1);
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<void> 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<void> 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;
final targetItem = _playlist!.medias.elementAtOrNull(index);
if (targetItem == null) return null;
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();
if (shuffled && _tempMedias != null) {
_tempMedias!.remove(targetItem);
}
} else {
final active = _playlist!.medias[_playlist!.index];
final newMedias = _playlist!.medias..removeAt(index);
final newMedias = _playlist!.medias.toList()..removeAt(index);
playlist = _playlist!.copyWith(
medias: newMedias,
index: newMedias.indexOf(active),
index: newMedias.indexOf(_playlist!.medias[_playlist!.index]),
);
}
}
}