diff --git a/src/renderer/features/player/context/player-context.tsx b/src/renderer/features/player/context/player-context.tsx index caec59485..11f90986c 100644 --- a/src/renderer/features/player/context/player-context.tsx +++ b/src/renderer/features/player/context/player-context.tsx @@ -20,6 +20,7 @@ import { SongListResponse, SongListSort, SortOrder, + sortSongsByFetchedOrder, } from '/@/shared/types/domain-types'; import { Play, PlayerRepeat, PlayerShuffle } from '/@/shared/types/types'; @@ -183,11 +184,13 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => { toast.hide(toastId); } + const sortedSongs = sortSongsByFetchedOrder(songs, id, itemType); + if (typeof type === 'object' && 'edge' in type && type.edge !== null) { const edge = type.edge === 'top' ? 'top' : 'bottom'; - storeActions.addToQueueByUniqueId(songs, type.uniqueId, edge); + storeActions.addToQueueByUniqueId(sortedSongs, type.uniqueId, edge); } else { - storeActions.addToQueueByType(songs, type as Play); + storeActions.addToQueueByType(sortedSongs, type as Play); } } catch (err: any) { if (instanceOfCancellationError(err)) { diff --git a/src/shared/types/domain-types.ts b/src/shared/types/domain-types.ts index 11abc269b..d0a8bf46c 100644 --- a/src/shared/types/domain-types.ts +++ b/src/shared/types/domain-types.ts @@ -1595,6 +1595,81 @@ export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: Sor 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(); + + 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,