mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 20:40:15 +02:00
add initial playlist reimplementation
This commit is contained in:
+284
-1
@@ -1,10 +1,24 @@
|
||||
import { AxiosHeaders } from 'axios';
|
||||
import isElectron from 'is-electron';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import reverse from 'lodash/reverse';
|
||||
import shuffle from 'lodash/shuffle';
|
||||
import semverCoerce from 'semver/functions/coerce';
|
||||
import semverGte from 'semver/functions/gte';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ServerListItem } from '/@/shared/types/domain-types';
|
||||
import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
AlbumArtistListSort,
|
||||
AlbumListSort,
|
||||
ArtistListSort,
|
||||
LibraryItem,
|
||||
ServerListItem,
|
||||
Song,
|
||||
SongListSort,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ServerFeature } from '/@/shared/types/features-types';
|
||||
|
||||
// Since ts-rest client returns a strict response type, we need to add the headers to the body object
|
||||
@@ -113,3 +127,272 @@ export const getClientType = (): string => {
|
||||
};
|
||||
|
||||
export const SEPARATOR_STRING = ' · ';
|
||||
|
||||
export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: SortOrder) => {
|
||||
let results = songs;
|
||||
|
||||
const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case SongListSort.ALBUM:
|
||||
results = orderBy(
|
||||
results,
|
||||
[(v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
[(v) => v.albumArtists[0]?.name.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
[(v) => v.artistName?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.BPM:
|
||||
results = orderBy(results, ['bpm'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.CHANNELS:
|
||||
results = orderBy(results, ['channels'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.COMMENT:
|
||||
results = orderBy(results, ['comment'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.FAVORITED:
|
||||
results = orderBy(results, ['userFavorite', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.GENRE:
|
||||
results = orderBy(
|
||||
results,
|
||||
[
|
||||
(v) => v.genres?.[0]?.name.toLowerCase(),
|
||||
(v) => v.album?.toLowerCase(),
|
||||
'discNumber',
|
||||
'trackNumber',
|
||||
],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ID:
|
||||
if (order === 'desc') {
|
||||
results = reverse(results as any);
|
||||
}
|
||||
break;
|
||||
|
||||
case SongListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
|
||||
case SongListSort.RATING:
|
||||
results = orderBy(results, ['userRating', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['createdAt'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RECENTLY_PLAYED:
|
||||
results = orderBy(results, ['lastPlayedAt'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RELEASE_DATE:
|
||||
results = orderBy(results, ['releaseDate'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.YEAR:
|
||||
results = orderBy(
|
||||
results,
|
||||
['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
||||
[order, 'asc', 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const sortSongsByFetchedOrder = (
|
||||
songs: Song[],
|
||||
fetchedIds: string[],
|
||||
itemType: LibraryItem,
|
||||
): Song[] => {
|
||||
// Group songs by the fetched ID they belong to
|
||||
const songsByFetchedId = new Map<string, Song[]>();
|
||||
|
||||
for (const song of songs) {
|
||||
let matchedId: string | undefined;
|
||||
|
||||
switch (itemType) {
|
||||
case LibraryItem.ALBUM:
|
||||
matchedId = fetchedIds.find((id) => song.albumId === id);
|
||||
break;
|
||||
case LibraryItem.ALBUM_ARTIST:
|
||||
matchedId = fetchedIds.find((id) =>
|
||||
song.albumArtists.some((artist) => artist.id === id),
|
||||
);
|
||||
break;
|
||||
case LibraryItem.ARTIST:
|
||||
matchedId = fetchedIds.find((id) =>
|
||||
song.artists.some((artist) => artist.id === id),
|
||||
);
|
||||
break;
|
||||
case LibraryItem.GENRE:
|
||||
matchedId = fetchedIds.find((id) => song.genres.some((genre) => genre.id === id));
|
||||
break;
|
||||
case LibraryItem.PLAYLIST:
|
||||
// For playlists, we might need to track which playlist each song came from
|
||||
// This is a simplified approach - you may need to adjust based on your data structure
|
||||
matchedId = fetchedIds.find((id) => song.playlistItemId === id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (matchedId) {
|
||||
if (!songsByFetchedId.has(matchedId)) {
|
||||
songsByFetchedId.set(matchedId, []);
|
||||
}
|
||||
songsByFetchedId.get(matchedId)!.push(song);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort each group by discNumber and trackNumber
|
||||
for (const [fetchedId, groupSongs] of songsByFetchedId.entries()) {
|
||||
const sortedGroup = orderBy(groupSongs, ['discNumber', 'trackNumber'], ['asc', 'asc']);
|
||||
songsByFetchedId.set(fetchedId, sortedGroup);
|
||||
}
|
||||
|
||||
// Combine groups in the order of fetchedIds
|
||||
const result: Song[] = [];
|
||||
for (const fetchedId of fetchedIds) {
|
||||
const groupSongs = songsByFetchedId.get(fetchedId);
|
||||
if (groupSongs) {
|
||||
result.push(...groupSongs);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any songs that didn't match any fetched ID at the end
|
||||
const matchedIds = new Set(result.map((s) => s.id));
|
||||
const unmatchedSongs = songs.filter((s) => !matchedIds.has(s.id));
|
||||
if (unmatchedSongs.length > 0) {
|
||||
const sortedUnmatched = orderBy(
|
||||
unmatchedSongs,
|
||||
['discNumber', 'trackNumber'],
|
||||
['asc', 'asc'],
|
||||
);
|
||||
result.push(...sortedUnmatched);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const sortAlbumArtistList = (
|
||||
artists: AlbumArtist[],
|
||||
sortBy: AlbumArtistListSort | ArtistListSort,
|
||||
sortOrder: SortOrder,
|
||||
) => {
|
||||
const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
let results = artists;
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumArtistListSort.ALBUM_COUNT:
|
||||
results = orderBy(artists, ['albumCount', (v) => v.name.toLowerCase()], [order, 'asc']);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.FAVORITED:
|
||||
results = orderBy(artists, ['starred'], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.NAME:
|
||||
results = orderBy(artists, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.RATING:
|
||||
results = orderBy(artists, ['userRating'], [order]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: SortOrder) => {
|
||||
let results = albums;
|
||||
|
||||
const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['albumArtist', (v) => v.name.toLowerCase()],
|
||||
[order, 'asc'],
|
||||
);
|
||||
break;
|
||||
case AlbumListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
case AlbumListSort.FAVORITED:
|
||||
results = orderBy(results, ['starred'], [order]);
|
||||
break;
|
||||
case AlbumListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
case AlbumListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
case AlbumListSort.RATING:
|
||||
results = orderBy(results, ['userRating'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['createdAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_PLAYED:
|
||||
results = orderBy(results, ['lastPlayedAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.SONG_COUNT:
|
||||
results = orderBy(results, ['songCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.YEAR:
|
||||
results = orderBy(results, ['releaseYear'], [order]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user