Skip to content

Commit

Permalink
feat: sort playlist tracks
Browse files Browse the repository at this point in the history
for local & yt playlists
  • Loading branch information
MSOB7YY committed Dec 29, 2024
1 parent 19666b8 commit 062378b
Show file tree
Hide file tree
Showing 14 changed files with 413 additions and 159 deletions.
52 changes: 41 additions & 11 deletions lib/controller/playlist_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,16 @@ import 'package:namida/core/translations/language.dart';
import 'package:namida/core/utils.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';

typedef LocalPlaylist = GeneralPlaylist<TrackWithDate>;
typedef LocalPlaylist = GeneralPlaylist<TrackWithDate, SortType>;

class PlaylistController extends PlaylistManager<TrackWithDate, Track> {
class PlaylistController extends PlaylistManager<TrackWithDate, Track, SortType> {
static PlaylistController get inst => _instance;
static final PlaylistController _instance = PlaylistController._internal();
PlaylistController._internal();

@override
Track identifyBy(TrackWithDate item) => item.track;

final canReorderTracks = false.obs;
void resetCanReorder() => canReorderTracks.value = false;

void addNewPlaylist(String name,
{List<Track> tracks = const <Track>[],
int? creationDate,
Expand Down Expand Up @@ -510,13 +507,16 @@ class PlaylistController extends PlaylistManager<TrackWithDate, Track> {
Map<String, dynamic> itemToJson(TrackWithDate item) => item.toJson();

@override
bool canRemovePlaylist(GeneralPlaylist<TrackWithDate> playlist) {
dynamic sortToJson(List<SortType> items) => items.map((e) => e.name).toList();

@override
bool canRemovePlaylist(LocalPlaylist playlist) {
_popPageIfCurrent(() => playlist.name);
return true;
}

@override
void onPlaylistRemovedFromMap(GeneralPlaylist<TrackWithDate> playlist) {
void onPlaylistRemovedFromMap(LocalPlaylist playlist) {
final plIndex = SearchSortController.inst.playlistSearchList.value.indexWhere((element) => playlist.name == element);
if (plIndex > -1) SearchSortController.inst.playlistSearchList.removeAt(plIndex);
}
Expand All @@ -532,19 +532,42 @@ class PlaylistController extends PlaylistManager<TrackWithDate, Track> {
}

@override
Future<Map<String, GeneralPlaylist<TrackWithDate>>> prepareAllPlaylistsFunction() async {
void onPlaylistItemsSort(List<SortType> sorts, bool reverse, List<TrackWithDate> items) {
final comparables = <Comparable<dynamic> Function(TrackWithDate tr)>[];
for (final s in sorts) {
if (s == SortType.dateAdded) {
Comparable<dynamic> comparable(TrackWithDate e) => e.dateAddedMS;
comparables.add(comparable);
} else {
final comparable = SearchSortController.inst.getTracksSortingComparables(s);
if (comparable != null) {
Comparable<dynamic> comparabletwd(TrackWithDate twd) => comparable(twd.track);
comparables.add(comparabletwd);
}
}
}

if (reverse) {
items.sortByReverseAlts(comparables);
} else {
items.sortByAlts(comparables);
}
}

@override
Future<Map<String, LocalPlaylist>> prepareAllPlaylistsFunction() async {
return await _readPlaylistFilesCompute.thready(playlistsDirectory);
}

@override
Future<GeneralPlaylist<TrackWithDate>?> prepareFavouritePlaylistFunction() {
Future<LocalPlaylist?> prepareFavouritePlaylistFunction() {
return _prepareFavouritesFile.thready(favouritePlaylistPath);
}

static LocalPlaylist? _prepareFavouritesFile(String path) {
try {
final response = File(path).readAsJsonSync();
return LocalPlaylist.fromJson(response, TrackWithDate.fromJson);
return LocalPlaylist.fromJson(response, TrackWithDate.fromJson, sortFromJson);
} catch (_) {}
return null;
}
Expand All @@ -558,11 +581,18 @@ class PlaylistController extends PlaylistManager<TrackWithDate, Track> {
if (f is File) {
try {
final response = f.readAsJsonSync();
final pl = LocalPlaylist.fromJson(response, TrackWithDate.fromJson);
final pl = LocalPlaylist.fromJson(response, TrackWithDate.fromJson, sortFromJson);
map[pl.name] = pl;
} catch (_) {}
}
}
return map;
}

static List<SortType>? sortFromJson(dynamic value) {
try {
return (value as List).map((e) => SortType.values.getEnum(e)!).toList();
} catch (_) {}
return null;
}
}
57 changes: 30 additions & 27 deletions lib/controller/search_sort_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:isolate';

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

import 'package:namida/base/ports_provider.dart';
import 'package:namida/class/folder.dart';
Expand Down Expand Up @@ -129,34 +128,38 @@ class SearchSortController {
}
}

late final _mediaTracksSortingComparables = <SortType, Comparable Function(Track e)>{
SortType.title: (e) => e.title.toLowerCase(),
SortType.album: (e) => e.album.toLowerCase(),
SortType.albumArtist: (e) => e.albumArtist.toLowerCase(),
SortType.year: (e) => e.yearPreferyyyyMMdd,
SortType.artistsList: (e) => e.artistsList.join().toLowerCase(),
SortType.genresList: (e) => e.genresList.join().toLowerCase(),
SortType.dateAdded: (e) => e.dateAdded,
SortType.dateModified: (e) => e.dateModified,
SortType.bitrate: (e) => e.bitrate,
SortType.composer: (e) => e.composer.toLowerCase(),
SortType.trackNo: (e) => e.trackNo,
SortType.discNo: (e) => e.discNo,
SortType.filename: (e) => e.filename.toLowerCase(),
SortType.duration: (e) => e.durationMS,
SortType.sampleRate: (e) => e.sampleRate,
SortType.size: (e) => e.size,
SortType.rating: (e) => e.effectiveRating,
SortType.mostPlayed: (e) => HistoryController.inst.topTracksMapListens.value[e]?.length ?? 0,
SortType.latestPlayed: (e) => HistoryController.inst.topTracksMapListens.value[e]?.lastOrNull ?? 0,
SortType.firstListen: (e) => HistoryController.inst.topTracksMapListens.value[e]?.firstOrNull ?? 0,
};
Comparable Function(Track e)? getTracksSortingComparables(SortType type) {
return switch (type) {
SortType.title => (e) => e.title.toLowerCase(),
SortType.album => (e) => e.album.toLowerCase(),
SortType.albumArtist => (e) => e.albumArtist.toLowerCase(),
SortType.year => (e) => e.yearPreferyyyyMMdd,
SortType.artistsList => (e) => e.artistsList.join().toLowerCase(),
SortType.genresList => (e) => e.genresList.join().toLowerCase(),
SortType.dateAdded => (e) => e.dateAdded,
SortType.dateModified => (e) => e.dateModified,
SortType.bitrate => (e) => e.bitrate,
SortType.composer => (e) => e.composer.toLowerCase(),
SortType.trackNo => (e) => e.trackNo,
SortType.discNo => (e) => e.discNo,
SortType.filename => (e) => e.filename.toLowerCase(),
SortType.duration => (e) => e.durationMS,
SortType.sampleRate => (e) => e.sampleRate,
SortType.size => (e) => e.size,
SortType.rating => (e) => e.effectiveRating,
SortType.mostPlayed => (e) => HistoryController.inst.topTracksMapListens.value[e]?.length ?? 0,
SortType.latestPlayed => (e) => HistoryController.inst.topTracksMapListens.value[e]?.lastOrNull ?? 0,
SortType.firstListen => (e) => HistoryController.inst.topTracksMapListens.value[e]?.firstOrNull ?? 0,
SortType.shuffle => null,
};
}

List<Comparable Function(Track tr)> getMediaTracksSortingComparables(MediaType media) {
final sorts = settings.mediaItemsTrackSorting.value[media] ?? <SortType>[SortType.title];
final l = <Comparable Function(Track e)>[];
sorts.loop((e) {
if (_mediaTracksSortingComparables[e] != null) l.add(_mediaTracksSortingComparables[e]!);
final sorter = getTracksSortingComparables(e);
if (sorter != null) l.add(sorter);
});
return l;
}
Expand Down Expand Up @@ -274,7 +277,7 @@ class SearchSortController {
},
isolateFunction: (itemsSendPort) async {
final params = {
'playlists': playlistsMap.value.values.map((e) => e.toJson((item) => item.toJson())).toList(),
'playlists': playlistsMap.value.values.map((e) => e.toJson((item) => item.toJson(), PlaylistController.inst.sortToJson)).toList(),
'translations': {
'k_PLAYLIST_NAME_AUTO_GENERATED': lang.AUTO_GENERATED,
'k_PLAYLIST_NAME_FAV': lang.FAVOURITES,
Expand Down Expand Up @@ -533,14 +536,14 @@ class SearchSortController {
final formatDate = DateFormat('yyyyMMdd');

final playlists = <({
GeneralPlaylist<TrackWithDate> pl,
LocalPlaylist pl,
String name,
String dateCreatedFormatted,
String dateModifiedFormatted,
})>[];
for (int i = 0; i < playlistsMap.length; i++) {
var plMap = playlistsMap[i];
final pl = GeneralPlaylist<TrackWithDate>.fromJson(plMap, (itemJson) => TrackWithDate.fromJson(itemJson));
final pl = LocalPlaylist.fromJson(plMap, (itemJson) => TrackWithDate.fromJson(itemJson), PlaylistController.sortFromJson);
final trName = translatePlName(pl.name);
final dateCreatedFormatted = formatDate.format(DateTime.fromMillisecondsSinceEpoch(pl.creationDate));
final dateModifiedFormatted = formatDate.format(DateTime.fromMillisecondsSinceEpoch(pl.modifiedDate));
Expand Down
12 changes: 12 additions & 0 deletions lib/core/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,15 @@ enum CacheVideoPriority {
low,
GETOUT,
}

enum YTSortType {
title,
channelTitle,
duration,
date,
dateAdded,
shuffle,
mostPlayed,
latestPlayed,
firstListen,
}
Loading

0 comments on commit 062378b

Please sign in to comment.