@@ -57,6 +57,8 @@ final currentRecentlyPlayedLength = ValueNotifier<int>(
5757 userRecentlyPlayed.length,
5858);
5959final recentlyPlayedVersion = ValueNotifier <int >(0 );
60+ var _songLikeUpdateToken = 0 ;
61+ final _latestSongLikeUpdateTokens = < String , int > {};
6062
6163final lyrics = ValueNotifier <String ?>(null );
6264String ? lastFetchedLyrics;
@@ -213,20 +215,44 @@ List _deduplicateAndShuffle(List playlistSongs) {
213215 return uniqueSongs;
214216}
215217
216- Future <void > updateSongLikeStatus (dynamic songId, bool add) async {
218+ Future <void > updateSongLikeStatus (
219+ dynamic songId,
220+ bool add, {
221+ Map ? songData,
222+ }) async {
217223 try {
224+ final normalizedSongId = songId? .toString ().trim () ?? '' ;
225+ if (normalizedSongId.isEmpty) return ;
226+
227+ final updateToken = ++ _songLikeUpdateToken;
228+ _latestSongLikeUpdateTokens[normalizedSongId] = updateToken;
229+
230+ final songToAdd = add
231+ ? await _resolveSongForLikedStatus (normalizedSongId, songData)
232+ : null ;
233+
234+ if (_latestSongLikeUpdateTokens[normalizedSongId] != updateToken) {
235+ return ;
236+ }
237+
238+ final updatedLikedSongs = _deduplicateLikedSongs (userLikedSongsList);
239+
218240 if (add) {
219- if (! userLikedSongsList.any ((song) => song['ytid' ] == songId)) {
220- final songDetails = await getSongDetails (
221- userLikedSongsList.length,
222- songId,
223- );
224- userLikedSongsList.add (songDetails);
241+ if (songToAdd != null &&
242+ ! updatedLikedSongs.any (
243+ (song) => song['ytid' ]? .toString () == normalizedSongId,
244+ )) {
245+ updatedLikedSongs.add (songToAdd);
225246 }
226247 } else {
227- userLikedSongsList.removeWhere ((song) => song['ytid' ] == songId);
248+ updatedLikedSongs.removeWhere (
249+ (song) => song['ytid' ]? .toString () == normalizedSongId,
250+ );
228251 }
229252
253+ if (_likedSongIdsAreEqual (userLikedSongsList, updatedLikedSongs)) return ;
254+
255+ userLikedSongsList = updatedLikedSongs;
230256 currentLikedSongsLength.value = userLikedSongsList.length;
231257 unawaited (addOrUpdateData ('user' , 'likedSongs' , userLikedSongsList));
232258 } catch (e, stackTrace) {
@@ -238,6 +264,68 @@ Future<void> updateSongLikeStatus(dynamic songId, bool add) async {
238264 }
239265}
240266
267+ Future <Map ?> _resolveSongForLikedStatus (String songId, Map ? songData) async {
268+ if (songData? ['ytid' ]? .toString () == songId) {
269+ return Map <String , dynamic >.from (songData! );
270+ }
271+
272+ final cachedSong = _findSongById (userLikedSongsList, songId);
273+ if (cachedSong != null ) return Map <String , dynamic >.from (cachedSong);
274+
275+ return getSongDetails (userLikedSongsList.length, songId);
276+ }
277+
278+ Map ? _findSongById (Iterable <dynamic > songs, String songId) {
279+ for (final song in songs) {
280+ if (song is Map && song['ytid' ]? .toString () == songId) return song;
281+ }
282+
283+ return null ;
284+ }
285+
286+ List _deduplicateLikedSongs (Iterable <dynamic > likedSongs) {
287+ final seenSongIds = < String > {};
288+ final deduplicatedSongs = [];
289+
290+ for (final song in likedSongs) {
291+ if (song is ! Map ) {
292+ deduplicatedSongs.add (song);
293+ continue ;
294+ }
295+
296+ final songId = song['ytid' ]? .toString ();
297+ if (songId == null || songId.isEmpty) {
298+ deduplicatedSongs.add (song);
299+ continue ;
300+ }
301+
302+ if (seenSongIds.add (songId)) {
303+ deduplicatedSongs.add (song);
304+ }
305+ }
306+
307+ return deduplicatedSongs;
308+ }
309+
310+ bool _likedSongIdsAreEqual (List previous, List updated) {
311+ if (previous.length != updated.length) return false ;
312+
313+ for (var i = 0 ; i < previous.length; i++ ) {
314+ final previousSong = previous[i];
315+ final updatedSong = updated[i];
316+ if (previousSong is ! Map || updatedSong is ! Map ) {
317+ if (previousSong != updatedSong) return false ;
318+ continue ;
319+ }
320+
321+ if (previousSong['ytid' ]? .toString () != updatedSong['ytid' ]? .toString ()) {
322+ return false ;
323+ }
324+ }
325+
326+ return true ;
327+ }
328+
241329void moveLikedSong (int oldIndex, int newIndex) {
242330 if (oldIndex < newIndex) {
243331 newIndex -= 1 ;
@@ -275,11 +363,17 @@ Future<void> renameSongInLikedSongs(
275363 }
276364}
277365
278- bool isSongAlreadyLiked (songIdToCheck) =>
279- userLikedSongsList.any ((song) => song['ytid' ] == songIdToCheck);
366+ bool isSongAlreadyLiked (songIdToCheck) {
367+ final songId = songIdToCheck? .toString ();
368+ return userLikedSongsList.any ((song) => song['ytid' ]? .toString () == songId);
369+ }
280370
281- bool isPlaylistAlreadyLiked (playlistIdToCheck) =>
282- userLikedPlaylists.any ((playlist) => playlist['ytid' ] == playlistIdToCheck);
371+ bool isPlaylistAlreadyLiked (playlistIdToCheck) {
372+ final playlistId = playlistIdToCheck? .toString ();
373+ return userLikedPlaylists.any (
374+ (playlist) => playlist['ytid' ]? .toString () == playlistId,
375+ );
376+ }
283377
284378bool isSongAlreadyOffline (songIdToCheck) =>
285379 userOfflineSongs.any ((song) => song['ytid' ] == songIdToCheck);
0 commit comments