mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix(mkPlayer): remove method and wrong active index on modifying playlist
This commit is contained in:
parent
eaf65b6db2
commit
3bafa7b80c
@ -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(),
|
||||
|
@ -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=', '');
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
await audioPlayer.replaceSource(
|
||||
nthSource,
|
||||
nthFetchedTrack.ytUri,
|
||||
);
|
||||
|
||||
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(),
|
||||
|
@ -458,35 +458,25 @@ class SpotubeAudioPlayer {
|
||||
final oldSourceIndex = sources.indexOf(oldSource);
|
||||
if (oldSourceIndex == -1) return;
|
||||
|
||||
// if (mkSupportedPlatform) {
|
||||
// final sourcesCp = sources.toList();
|
||||
// sourcesCp[oldSourceIndex] = newSource;
|
||||
if (mkSupportedPlatform) {
|
||||
_mkPlayer!.replace(oldSource, newSource);
|
||||
} else {
|
||||
await addTrack(newSource);
|
||||
await removeTrack(oldSourceIndex);
|
||||
|
||||
// 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);
|
||||
|
||||
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<void> clearPlaylist() async {
|
||||
|
@ -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;
|
||||
|
||||
return super.open(_playlist!.medias[_playlist!.index], play: true);
|
||||
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 {
|
||||
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<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();
|
||||
}
|
||||
} else {
|
||||
final active = _playlist!.medias[_playlist!.index];
|
||||
final newMedias = _playlist!.medias..removeAt(index);
|
||||
playlist = _playlist!.copyWith(
|
||||
medias: newMedias,
|
||||
index: newMedias.indexOf(active),
|
||||
);
|
||||
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]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user