From 50dd70df81d728ff5be7c1356dd838851a7477d4 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Wed, 13 Dec 2023 18:15:02 -0800 Subject: [PATCH] Add global sort utils --- .../api/subsonic/subsonic-controller.ts | 126 ++-------------- src/renderer/api/utils.ts | 137 +++++++++++++++++- 2 files changed, 152 insertions(+), 111 deletions(-) diff --git a/src/renderer/api/subsonic/subsonic-controller.ts b/src/renderer/api/subsonic/subsonic-controller.ts index 9bf4c12fb..c9a54c176 100644 --- a/src/renderer/api/subsonic/subsonic-controller.ts +++ b/src/renderer/api/subsonic/subsonic-controller.ts @@ -1,22 +1,19 @@ -import orderBy from 'lodash/orderBy'; -import shuffle from 'lodash/shuffle'; import filter from 'lodash/filter'; -import reverse from 'lodash/reverse'; +import orderBy from 'lodash/orderBy'; import md5 from 'md5'; import { fsLog } from '/@/logger'; import { subsonicApiClient } from '/@/renderer/api/subsonic/subsonic-api'; import { subsonicNormalize } from '/@/renderer/api/subsonic/subsonic-normalize'; import { AlbumListSortType, SubsonicApi } from '/@/renderer/api/subsonic/subsonic-types'; import { - AlbumArtistListSort, AlbumListSort, AuthenticationResponse, ControllerEndpoint, GenreListSort, LibraryItem, PlaylistListSort, - SongListSort, } from '/@/renderer/api/types'; +import { sortAlbumArtistList, sortSongList } from '/@/renderer/api/utils'; import { randomString } from '/@/renderer/utils'; const authenticate = async ( @@ -204,7 +201,6 @@ export const SubsonicController: ControllerEndpoint = { }, getAlbumArtistList: async (args) => { const { query, apiClientProps } = args; - const sortOrder = query.sortOrder.toLowerCase() as 'asc' | 'desc'; const res = await subsonicApiClient(apiClientProps).getArtists({ query: { @@ -221,8 +217,9 @@ export const SubsonicController: ControllerEndpoint = { (index) => index.artist, ); - let results = artists; - let totalRecordCount = artists.length; + let results = artists.map((artist) => + subsonicNormalize.albumArtist(artist, apiClientProps.server), + ); if (query.searchTerm) { const searchResults = filter(results, (artist) => { @@ -230,36 +227,16 @@ export const SubsonicController: ControllerEndpoint = { }); results = searchResults; - totalRecordCount = searchResults.length; } - switch (query.sortBy) { - case AlbumArtistListSort.ALBUM_COUNT: - results = orderBy( - artists, - ['albumCount', (v) => v.name.toLowerCase()], - [sortOrder, 'asc'], - ); - break; - case AlbumArtistListSort.NAME: - results = orderBy(artists, [(v) => v.name.toLowerCase()], [sortOrder]); - break; - case AlbumArtistListSort.FAVORITED: - results = orderBy(artists, ['starred'], [sortOrder]); - break; - case AlbumArtistListSort.RATING: - results = orderBy(artists, ['userRating'], [sortOrder]); - break; - default: - break; + if (query.sortBy) { + sortAlbumArtistList(results, query.sortBy, query.sortOrder); } return { - items: results.map((artist) => - subsonicNormalize.albumArtist(artist, apiClientProps.server), - ), + items: results, startIndex: query.startIndex, - totalRecordCount, + totalRecordCount: results?.length || 0, }; }, getAlbumArtistListCount: async (args) => { @@ -665,7 +642,6 @@ export const SubsonicController: ControllerEndpoint = { }, getPlaylistSongList: async (args) => { const { query, apiClientProps } = args; - const sortOrder = query.sortOrder.toLowerCase() as 'asc' | 'desc'; const res = await subsonicApiClient(apiClientProps).getPlaylist({ query: { @@ -678,95 +654,25 @@ export const SubsonicController: ControllerEndpoint = { throw new Error('Failed to get playlist song list'); } - let results = res.body['subsonic-response'].playlist.entry || []; + let results = + res.body['subsonic-response'].playlist.entry?.map((song) => + subsonicNormalize.song(song, apiClientProps.server, ''), + ) || []; if (query.searchTerm) { const searchResults = filter(results, (entry) => { - return entry.title.toLowerCase().includes(query.searchTerm!.toLowerCase()); + return entry.name.toLowerCase().includes(query.searchTerm!.toLowerCase()); }); results = searchResults; } if (query.sortBy) { - switch (query.sortBy) { - case SongListSort.ALBUM: - results = orderBy( - results, - [(v) => v.album?.toLowerCase(), 'discNumber', 'track'], - [sortOrder, 'asc', 'asc'], - ); - break; - case SongListSort.ALBUM_ARTIST: - results = orderBy( - results, - ['albumArtist', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], - [sortOrder, sortOrder, 'asc', 'asc'], - ); - break; - case SongListSort.ARTIST: - results = orderBy( - results, - ['artist', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], - [sortOrder, sortOrder, 'asc', 'asc'], - ); - break; - case SongListSort.DURATION: - results = orderBy(results, ['duration'], [sortOrder]); - break; - case SongListSort.FAVORITED: - results = orderBy( - results, - ['starred', (v) => v.title.toLowerCase()], - [sortOrder], - ); - break; - case SongListSort.GENRE: - results = orderBy( - results, - ['genre', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], - [sortOrder, sortOrder, 'asc', 'asc'], - ); - break; - case SongListSort.ID: - if (sortOrder === 'desc') { - results = reverse(results); - } - break; - case SongListSort.NAME: - results = orderBy(results, [(v) => v.title.toLowerCase()], [sortOrder]); - break; - case SongListSort.PLAY_COUNT: - results = orderBy(results, ['playCount'], [sortOrder]); - break; - case SongListSort.RANDOM: - results = shuffle(results); - break; - case SongListSort.RATING: - results = orderBy( - results, - ['userRating', (v) => v.title.toLowerCase()], - [sortOrder], - ); - break; - case SongListSort.RECENTLY_ADDED: - results = orderBy(results, ['created'], [sortOrder]); - break; - case SongListSort.YEAR: - results = orderBy( - results, - ['year', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], - [sortOrder, 'asc', 'asc', 'asc'], - ); - break; - - default: - break; - } + sortSongList(results, query.sortBy, query.sortOrder); } return { - items: results?.map((song) => subsonicNormalize.song(song, apiClientProps.server, '')), + items: results, startIndex: 0, totalRecordCount: results?.length || 0, }; diff --git a/src/renderer/api/utils.ts b/src/renderer/api/utils.ts index 0063fae95..335c42589 100644 --- a/src/renderer/api/utils.ts +++ b/src/renderer/api/utils.ts @@ -1,8 +1,18 @@ import { AxiosHeaders } from 'axios'; -import { z } from 'zod'; import { toast } from '/@/renderer/components'; import { useAuthStore } from '/@/renderer/store'; import { ServerListItem } from '/@/renderer/types'; +import { + AlbumArtist, + AlbumArtistListSort, + QueueSong, + SongListSort, + SortOrder, +} from '/@/renderer/api/types'; +import orderBy from 'lodash/orderBy'; +import reverse from 'lodash/reverse'; +import shuffle from 'lodash/shuffle'; +import { z } from 'zod'; // Since ts-rest client returns a strict response type, we need to add the headers to the body object export const resultWithHeaders = (itemSchema: ItemType) => { @@ -38,3 +48,128 @@ export const authenticationFailure = (currentServer: ServerListItem | null) => { useAuthStore.getState().actions.setCurrentServer(null); } }; + +export const sortSongList = (songs: QueueSong[], 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, + ['albumArtist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'], + [order, order, 'asc', 'asc'], + ); + break; + + case SongListSort.ARTIST: + results = orderBy( + results, + ['artist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'], + [order, order, 'asc', 'asc'], + ); + 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); + } + 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, ['created'], [order]); + break; + + case SongListSort.YEAR: + results = orderBy( + results, + ['year', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], + [order, 'asc', 'asc', 'asc'], + ); + break; + + default: + break; + } + + return results; +}; + +export const sortAlbumArtistList = ( + artists: AlbumArtist[], + sortBy: AlbumArtistListSort, + 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.NAME: + results = orderBy(artists, [(v) => v.name.toLowerCase()], [order]); + break; + + case AlbumArtistListSort.FAVORITED: + results = orderBy(artists, ['starred'], [order]); + break; + + case AlbumArtistListSort.RATING: + results = orderBy(artists, ['userRating'], [order]); + break; + + default: + break; + } + + return results; +};