Skip to content

Commit

Permalink
core: better management for duplicated tracks/playlists addition
Browse files Browse the repository at this point in the history
(which applies to yt playlists section too, instead of just adding the missing ones)
- this was done while doing the new merge system of importing yt takeout playlists
  • Loading branch information
MSOB7YY committed Nov 5, 2024
1 parent d8f9b9f commit fd01506
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 107 deletions.
84 changes: 23 additions & 61 deletions lib/controller/playlist_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,93 +45,55 @@ class PlaylistController extends PlaylistManager<TrackWithDate, Track> {
List<String> moods = const [],
String? m3uPath,
}) async {
final newTracks = tracks.mapped((e) => TrackWithDate(
dateAdded: currentTimeMS,
track: e,
source: TrackSource.local,
));
super.addNewPlaylistRaw(
name,
tracks: (playlistID) => newTracks,
tracks: tracks,
convertItem: (e, dateAdded, playlistID) => TrackWithDate(
dateAdded: dateAdded,
track: e,
source: TrackSource.local,
),
creationDate: creationDate,
comment: comment,
moods: moods,
m3uPath: m3uPath,
actionIfAlreadyExists: () => NamidaOnTaps.inst.showDuplicatedDialogAction(PlaylistAddDuplicateAction.valuesForAdd),
);
}

void addTracksToPlaylist(
LocalPlaylist playlist,
List<Track> tracks, {
TrackSource source = TrackSource.local,
List<PlaylistAddDuplicateAction> duplicationActions = PlaylistAddDuplicateAction.values,
List<PlaylistAddDuplicateAction> duplicationActions = PlaylistAddDuplicateAction.valuesForAdd,
}) async {
Iterable<TrackWithDate> convertTracks(List<Track> trs) => trs.map((e) => TrackWithDate(
dateAdded: currentTimeMS,
final originalModifyDate = playlist.modifiedDate;
final oldTracksList = List<TrackWithDate>.from(playlist.tracks); // for undo

final addedTracksLength = await super.addTracksToPlaylistRaw(
playlist,
tracks,
() => NamidaOnTaps.inst.showDuplicatedDialogAction(duplicationActions),
(e, dateAdded) {
return TrackWithDate(
dateAdded: dateAdded,
track: e,
source: source,
));
final oldTracksList = List<TrackWithDate>.from(playlist.tracks); // for undo
int addedTracksLength = tracks.length;

if (playlist.tracks.any((element) => tracks.contains(element.track))) {
TrackWithDate convertTrack(Track e) => TrackWithDate(
dateAdded: currentTimeMS,
track: e,
source: source,
);
final action = await NamidaOnTaps.inst.showDuplicatedDialogAction(duplicationActions);
switch (action) {
case PlaylistAddDuplicateAction.justAddEverything:
playlist.tracks.addAll(convertTracks(tracks));
break;
case PlaylistAddDuplicateAction.addAllAndRemoveOldOnes:
final currentTracks = <Track, List<int>>{};
playlist.tracks.loopAdv((e, index) => currentTracks.addForce(e.track, index));

final indicesToRemove = <int>[];
tracks.loop((e) {
// -- removing same tracks existing in playlist
final indexesInPlaylist = currentTracks[e];
if (indexesInPlaylist != null) {
indicesToRemove.addAll(indexesInPlaylist);
}
});
indicesToRemove.sortByReverse((e) => e);
indicesToRemove.loop((indexToRemove) => playlist.tracks.removeAt(indexToRemove));
playlist.tracks.addAll(convertTracks(tracks));
break;
case PlaylistAddDuplicateAction.addOnlyMissing:
final currentTracks = <Track, int>{};
playlist.tracks.loopAdv((e, index) => currentTracks[e.track] = index);
tracks.loop((e) {
if (currentTracks[e] == null) {
playlist.tracks.add(convertTrack(e));
} else {
addedTracksLength--;
}
});
);
},
);

break;
default:
addedTracksLength = 0;
return;
}
} else {
playlist.tracks.addAll(convertTracks(tracks));
}
if (addedTracksLength == null) return;

snackyy(
message: "${lang.ADDED} ${addedTracksLength.displayTrackKeyword}",
button: addedTracksLength > 0
? (
lang.UNDO,
() async => await updatePropertyInPlaylist(playlist.name, tracks: oldTracksList, modifiedDate: currentTimeMS),
() async => await updatePropertyInPlaylist(playlist.name, tracks: oldTracksList, modifiedDate: originalModifyDate),
)
: null,
);

super.addTracksToPlaylistRaw(playlist, [] /* added manually */);
}

bool favouriteButtonOnPressed(Track track, {bool refreshNotification = true}) {
Expand Down
6 changes: 0 additions & 6 deletions lib/core/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,6 @@ enum SetMusicAsAction {
alarm,
}

enum PlaylistAddDuplicateAction {
justAddEverything,
addAllAndRemoveOldOnes,
addOnlyMissing,
}

enum YTSeekActionMode {
none,
minimizedMiniplayer,
Expand Down
9 changes: 7 additions & 2 deletions lib/core/functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:history_manager/history_manager.dart';
import 'package:namico_subscription_manager/core/enum.dart';
import 'package:playlist_manager/module/playlist_id.dart';
import 'package:playlist_manager/playlist_manager.dart';
import 'package:youtipie/class/execute_details.dart';
import 'package:youtipie/core/extensions.dart' show ThumbnailPickerExt;

Expand Down Expand Up @@ -537,8 +538,12 @@ class NamidaOnTaps {
);
}

Future<PlaylistAddDuplicateAction?> showDuplicatedDialogAction(List<PlaylistAddDuplicateAction> duplicationActions, {bool displayTitle = true}) async {
final actionRx = Rxn<PlaylistAddDuplicateAction>();
Future<PlaylistAddDuplicateAction?> showDuplicatedDialogAction(
List<PlaylistAddDuplicateAction> duplicationActions, {
bool displayTitle = true,
PlaylistAddDuplicateAction? initiallySelected,
}) async {
final actionRx = Rxn<PlaylistAddDuplicateAction>(initiallySelected);
PlaylistAddDuplicateAction? actionToUse;
await NamidaNavigator.inst.navigateDialog(
onDismissing: () {
Expand Down
3 changes: 3 additions & 0 deletions lib/core/namida_converter_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';

import 'package:history_manager/history_manager.dart';
import 'package:path/path.dart' as p;
import 'package:playlist_manager/playlist_manager.dart';
import 'package:youtipie/class/streams/audio_stream.dart';
import 'package:youtipie/class/streams/video_stream.dart';
import 'package:youtipie/class/youtipie_feed/playlist_basic_info.dart';
Expand Down Expand Up @@ -1318,6 +1319,8 @@ class _NamidaConverters {
PlaylistAddDuplicateAction.justAddEverything: lang.ADD_ALL,
PlaylistAddDuplicateAction.addAllAndRemoveOldOnes: lang.ADD_ALL_AND_REMOVE_OLD_ONES,
PlaylistAddDuplicateAction.addOnlyMissing: lang.ADD_ONLY_MISSING,
PlaylistAddDuplicateAction.mergeAndSortByAddedDate: '${lang.MERGE} + ${lang.SORT_BY}: ${lang.DATE_ADDED}',
PlaylistAddDuplicateAction.deleteAndCreateNewPlaylist: '${lang.DELETE_PLAYLIST} + ${lang.CREATE_NEW_PLAYLIST}',
},
YTSeekActionMode: {
YTSeekActionMode.none: lang.NONE,
Expand Down
1 change: 1 addition & 0 deletions lib/core/translations/keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ abstract class LanguageKeys {
String get MEMBERSHIP_SIGN_IN_TO_PATREON_ACCOUNT => _getKey('MEMBERSHIP_SIGN_IN_TO_PATREON_ACCOUNT');
String get MEMBERSHIP_UNKNOWN => _getKey('MEMBERSHIP_UNKNOWN');
String get MEMBERSHIP_YOU_NEED_MEMBERSHIP_OF_TO_ADD_MULTIPLE_ACCOUNTS => _getKey('MEMBERSHIP_YOU_NEED_MEMBERSHIP_OF_TO_ADD_MULTIPLE_ACCOUNTS');
String get MERGE => _getKey('MERGE');
String get METADATA_CACHE => _getKey('METADATA_CACHE');
String get METADATA_EDIT_FAILED => _getKey('METADATA_EDIT_FAILED');
String get METADATA_READ_FAILED => _getKey('METADATA_READ_FAILED');
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/pages/playlists_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';

import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:playlist_manager/playlist_manager.dart';

import 'package:namida/base/pull_to_refresh.dart';
import 'package:namida/class/route.dart';
Expand Down Expand Up @@ -416,6 +417,7 @@ class _PlaylistsPageState extends State<PlaylistsPage> with TickerProviderStateM
duplicationActions: [
PlaylistAddDuplicateAction.addAllAndRemoveOldOnes,
PlaylistAddDuplicateAction.addOnlyMissing,
PlaylistAddDuplicateAction.mergeAndSortByAddedDate,
],
),
),
Expand Down
76 changes: 40 additions & 36 deletions lib/youtube/controller/youtube_playlist_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import 'package:playlist_manager/module/playlist_id.dart';
import 'package:playlist_manager/playlist_manager.dart';

import 'package:namida/class/video.dart';
import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/controller/player_controller.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/enums.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/functions.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/core/utils.dart';
import 'package:namida/youtube/class/youtube_id.dart';
Expand All @@ -37,52 +39,54 @@ class YoutubePlaylistController extends PlaylistManager<YoutubeID, String> {
List<String> moods = const [],
PlaylistID? playlistID,
}) async {
final videoIdsList = videoIds?.toList() ?? [];
super.addNewPlaylistRaw(
name,
tracks: (playlistID) {
final newTracks = videoIds
?.map(
(id) => YoutubeID(
id: id,
watchNull: YTWatch(dateNull: DateTime.now(), isYTMusic: false),
playlistID: playlistID,
),
)
.toList();
return newTracks;
tracks: videoIdsList,
convertItem: (id, dateAdded, playlistID) {
return YoutubeID(
id: id,
watchNull: YTWatch(dateNull: dateAdded.milliSecondsSinceEpoch, isYTMusic: false),
playlistID: playlistID,
);
},
creationDate: creationDate,
comment: comment,
moods: moods,
playlistID: playlistID,
actionIfAlreadyExists: () => NamidaOnTaps.inst.showDuplicatedDialogAction(PlaylistAddDuplicateAction.valuesForAdd),
);
}

/// Returns added ids, when [preventDuplicates] is true;
Future<Iterable<YoutubeID>> addTracksToPlaylist(YoutubePlaylist playlist, Iterable<String> videoIds, {bool preventDuplicates = true}) async {
late Iterable<String> idsToAdd;

if (preventDuplicates) {
final existingIds = <String, bool>{};
playlist.tracks.loop((e) {
existingIds[e.id] = true;
});
// only add ids that doesnt exist inside playlist.
idsToAdd = videoIds.where((element) => existingIds[element] == null);
} else {
idsToAdd = videoIds;
}
final newtracks = idsToAdd
.map(
(id) => YoutubeID(
id: id,
watchNull: YTWatch(dateNull: DateTime.now(), isYTMusic: false),
playlistID: playlist.playlistID,
),
)
.toList();
await super.addTracksToPlaylistRaw(playlist, newtracks);
return newtracks;
Future<void> addTracksToPlaylist(YoutubePlaylist playlist, Iterable<String> videoIds) async {
final originalModifyDate = playlist.modifiedDate;
final oldVideosList = List<YoutubeID>.from(playlist.tracks); // for undo

final videoIdsList = videoIds.toList();
final addedVideosLength = await super.addTracksToPlaylistRaw(
playlist,
videoIdsList,
() => NamidaOnTaps.inst.showDuplicatedDialogAction(PlaylistAddDuplicateAction.valuesForAdd),
(id, dateAdded) {
return YoutubeID(
id: id,
watchNull: YTWatch(dateNull: dateAdded.milliSecondsSinceEpoch, isYTMusic: false),
playlistID: playlist.playlistID,
);
},
);

if (addedVideosLength == null) return;

snackyy(
message: "${lang.ADDED} ${addedVideosLength.displayVideoKeyword}",
button: addedVideosLength > 0
? (
lang.UNDO,
() async => await updatePropertyInPlaylist(playlist.name, tracks: oldVideosList, modifiedDate: originalModifyDate),
)
: null,
);
}

bool favouriteButtonOnPressed(String videoId, {bool refreshNotification = true}) {
Expand Down
2 changes: 1 addition & 1 deletion lib/youtube/functions/add_to_playlist_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:flutter/material.dart';

import 'package:playlist_manager/core/enum.dart';
import 'package:youtipie/class/playlist_for_video.dart';
import 'package:youtipie/class/youtipie_feed/playlist_info_item_user.dart';
import 'package:youtipie/core/enum.dart';
Expand All @@ -11,7 +12,6 @@ import 'package:namida/base/pull_to_refresh.dart';
import 'package:namida/controller/current_color.dart';
import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/core/enums.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/functions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: namida
description: A Beautiful and Feature-rich Music Player, With YouTube & Video Support Built in Flutter
publish_to: "none"
version: 4.6.58-beta+241105198
version: 4.6.6-beta+241105199

environment:
sdk: ">=3.4.0 <4.0.0"
Expand Down

0 comments on commit fd01506

Please sign in to comment.