diff --git a/src/renderer/features/player/hooks/use-handle-playqueue-add.ts b/src/renderer/features/player/hooks/use-handle-playqueue-add.ts index 87412d5f..e5d145a5 100644 --- a/src/renderer/features/player/hooks/use-handle-playqueue-add.ts +++ b/src/renderer/features/player/hooks/use-handle-playqueue-add.ts @@ -1,18 +1,25 @@ import { useCallback } from 'react'; import { useQueryClient } from '@tanstack/react-query'; -import { api } from '/@/renderer/api/index'; -import { queryKeys } from '/@/renderer/api/query-keys'; import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store'; import { usePlayerType } from '/@/renderer/store/settings.store'; import { PlayQueueAddOptions, Play, PlaybackType } from '/@/renderer/types'; import { toast } from '/@/renderer/components/toast/index'; import isElectron from 'is-electron'; import { nanoid } from 'nanoid/non-secure'; -import { LibraryItem, SongListSort, SortOrder, Song } from '/@/renderer/api/types'; +import { LibraryItem, QueueSong, Song, SongListResponse } from '/@/renderer/api/types'; +import { + getPlaylistSongsById, + getSongById, + getAlbumSongsById, + getAlbumArtistSongsById, +} from '/@/renderer/features/player/utils'; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; const utils = isElectron() ? window.electron.utils : null; const mpris = isElectron() && utils?.isLinux() ? window.electron.mpris : null; + +const addToQueue = usePlayerStore.getState().actions.addToQueue; + export const useHandlePlayQueueAdd = () => { const queryClient = useQueryClient(); const playerType = usePlayerType(); @@ -22,129 +29,22 @@ export const useHandlePlayQueueAdd = () => { const handlePlayQueueAdd = useCallback( async (options: PlayQueueAddOptions) => { if (!server) return toast.error({ message: 'No server selected', type: 'error' }); - let songs = null; + const { initialIndex, playType, byData, byItemType } = options; + let songs: QueueSong[] | null = null; - // const itemCount = options.byItemType?.id?.length || 0; - // const fetchId = itemCount > 1 ? nanoid() : null; - - if (options.byItemType) { - let songsList: any; - let queryFilter: any; - let queryKey: any; - - if (options.byItemType.type === LibraryItem.PLAYLIST) { - // if (fetchId) { - // toast.success({ - // autoClose: false, - // id: fetchId, - // loading: true, - // message: `This may take a while...`, - // title: `Adding ${itemCount} albums to the queue`, - // }); - // } - - queryFilter = { - id: options.byItemType?.id || [], - sortBy: 'id', - sortOrder: SortOrder.ASC, - startIndex: 0, - }; - - queryKey = queryKeys.playlists.songList( - server?.id, - options.byItemType?.id?.[0] || '', - queryFilter, - ); - } else if (options.byItemType.type === LibraryItem.ALBUM) { - // if (fetchId) { - // toast.success({ - // autoClose: false, - // id: fetchId, - // loading: true, - // message: `This may take a while...`, - // title: `Adding ${itemCount} albums to the queue`, - // }); - // } - - queryFilter = { - albumIds: options.byItemType?.id || [], - sortBy: SongListSort.ALBUM, - sortOrder: SortOrder.ASC, - startIndex: 0, - }; - - queryKey = queryKeys.songs.list(server?.id, queryFilter); - } else if (options.byItemType.type === LibraryItem.ALBUM_ARTIST) { - // if (fetchId) { - // toast.success({ - // autoClose: false, - // id: fetchId, - // loading: true, - // message: `This may take a while...`, - // title: `Adding ${itemCount} album artists to the queue`, - // }); - // } - - queryFilter = { - artistIds: options.byItemType?.id || [], - sortBy: SongListSort.ALBUM_ARTIST, - sortOrder: SortOrder.ASC, - startIndex: 0, - }; - - queryKey = queryKeys.songs.list(server?.id, queryFilter); - } else if (options.byItemType.type === LibraryItem.SONG) { - queryFilter = { id: options.byItemType.id }; - queryKey = queryKeys.songs.detail(server?.id, queryFilter); - } + if (byItemType) { + let songList: SongListResponse | undefined; + const { type: itemType, id } = byItemType; try { - if (options.byItemType?.type === LibraryItem.PLAYLIST) { - songsList = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.getPlaylistSongList({ - apiClientProps: { - server, - signal, - }, - query: queryFilter, - }), - { - cacheTime: 1000 * 60, - staleTime: 1000 * 60, - }, - ); - } else if (options.byItemType?.type === LibraryItem.SONG) { - const song = (await queryClient.fetchQuery(queryKey, async ({ signal }) => - api.controller.getSongDetail({ - apiClientProps: { - server, - signal, - }, - query: queryFilter, - }), - )) as Song; - - if (song) { - songsList = { items: [song], startIndex: 0, totalRecordCount: 1 }; - } + if (itemType === LibraryItem.PLAYLIST) { + songList = await getPlaylistSongsById({ id, queryClient, server }); + } else if (itemType === LibraryItem.ALBUM) { + songList = await getAlbumSongsById({ id, queryClient, server }); + } else if (itemType === LibraryItem.ALBUM_ARTIST) { + songList = await getAlbumArtistSongsById({ id, queryClient, server }); } else { - songsList = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.getSongList({ - apiClientProps: { - server, - signal, - }, - query: queryFilter, - }), - { - cacheTime: 1000 * 60, - staleTime: 1000 * 60, - }, - ); + songList = await getSongById({ id, queryClient, server }); } } catch (err: any) { return toast.error({ @@ -153,24 +53,25 @@ export const useHandlePlayQueueAdd = () => { }); } - if (!songsList) return toast.warn({ message: 'No songs found' }); - songs = songsList.items?.map((song: Song) => ({ ...song, uniqueId: nanoid() })); - } else if (options.byData) { - songs = options.byData.map((song) => ({ ...song, uniqueId: nanoid() })); + songs = songList?.items?.map((song: Song) => ({ ...song, uniqueId: nanoid() })) || null; + } else if (byData) { + songs = byData.map((song) => ({ ...song, uniqueId: nanoid() })); } if (!songs) return toast.warn({ message: 'No songs found' }); - const playerData = usePlayerStore.getState().actions.addToQueue(songs, options.play); + const playerData = addToQueue({ initialIndex: initialIndex || 0, playType, songs }); if (playerType === PlaybackType.LOCAL) { - if (options.play === Play.NEXT || options.play === Play.LAST) { - mpvPlayer.setQueueNext(playerData); + mpvPlayer?.volume(usePlayerStore.getState().volume); + + if (playType === Play.NEXT || playType === Play.LAST) { + mpvPlayer?.setQueueNext(playerData); } - if (options.play === Play.NOW) { - mpvPlayer.setQueue(playerData); - mpvPlayer.play(); + if (playType === Play.NOW) { + mpvPlayer?.setQueue(playerData); + mpvPlayer?.play(); } } @@ -184,21 +85,6 @@ export const useHandlePlayQueueAdd = () => { status: 'Playing', }); - // if (fetchId) { - // toast.update({ - // autoClose: 1000, - // id: fetchId, - // message: '', - // title: `Added ${songs.length} items to the queue`, - // }); - // // toast.hide(fetchId); - // } else { - // toast.success({ - // // message: 'Success', - // title: `Added ${songs.length} items to the queue`, - // }); - // } - return null; }, [play, playerType, queryClient, server], diff --git a/src/renderer/features/player/index.ts b/src/renderer/features/player/index.ts index bf7f3353..a191d50b 100644 --- a/src/renderer/features/player/index.ts +++ b/src/renderer/features/player/index.ts @@ -3,3 +3,4 @@ export * from './components/left-controls'; export * from './components/playerbar'; export * from './context/play-queue-handler-context'; export * from './hooks/use-playqueue-add'; +export * from './utils'; diff --git a/src/renderer/features/player/utils.ts b/src/renderer/features/player/utils.ts new file mode 100644 index 00000000..1af7caae --- /dev/null +++ b/src/renderer/features/player/utils.ts @@ -0,0 +1,155 @@ +import { QueryClient } from '@tanstack/react-query'; +import { api } from '/@/renderer/api'; +import { queryKeys } from '/@/renderer/api/query-keys'; +import { + PlaylistSongListQuery, + SongDetailQuery, + SongListQuery, + SongListResponse, + SongListSort, + SortOrder, +} from '/@/renderer/api/types'; +import { ServerListItem } from '/@/renderer/types'; + +export const getPlaylistSongsById = async (args: { + id: string; + queryClient: QueryClient; + server: ServerListItem; +}) => { + const { id, queryClient, server } = args; + + const queryFilter: PlaylistSongListQuery = { + id, + sortBy: SongListSort.ID, + sortOrder: SortOrder.ASC, + startIndex: 0, + }; + + const queryKey = queryKeys.playlists.songList(server?.id, id, queryFilter); + + const res = await queryClient.fetchQuery( + queryKey, + async ({ signal }) => + api.controller.getPlaylistSongList({ + apiClientProps: { + server, + signal, + }, + query: queryFilter, + }), + { + cacheTime: 1000 * 60, + staleTime: 1000 * 60, + }, + ); + + return res; +}; + +export const getAlbumSongsById = async (args: { + id: string[]; + orderByIds?: boolean; + queryClient: QueryClient; + server: ServerListItem; +}) => { + const { id, queryClient, server } = args; + + const queryFilter: SongListQuery = { + albumIds: id, + sortBy: SongListSort.ALBUM, + sortOrder: SortOrder.ASC, + startIndex: 0, + }; + + const queryKey = queryKeys.songs.list(server?.id, queryFilter); + + const res = await queryClient.fetchQuery( + queryKey, + async ({ signal }) => + api.controller.getSongList({ + apiClientProps: { + server, + signal, + }, + query: queryFilter, + }), + { + cacheTime: 1000 * 60, + staleTime: 1000 * 60, + }, + ); + + return res; +}; + +export const getAlbumArtistSongsById = async (args: { + id: string[]; + orderByIds?: boolean; + queryClient: QueryClient; + server: ServerListItem; +}) => { + const { id, queryClient, server } = args; + + const queryFilter: SongListQuery = { + artistIds: id || [], + sortBy: SongListSort.ALBUM_ARTIST, + sortOrder: SortOrder.ASC, + startIndex: 0, + }; + + const queryKey = queryKeys.songs.list(server?.id, queryFilter); + + const res = await queryClient.fetchQuery( + queryKey, + async ({ signal }) => + api.controller.getSongList({ + apiClientProps: { + server, + signal, + }, + query: queryFilter, + }), + { + cacheTime: 1000 * 60, + staleTime: 1000 * 60, + }, + ); + + return res; +}; + +export const getSongById = async (args: { + id: string; + queryClient: QueryClient; + server: ServerListItem; +}): Promise => { + const { id, queryClient, server } = args; + + const queryFilter: SongDetailQuery = { id }; + + const queryKey = queryKeys.songs.detail(server?.id, queryFilter); + + const res = await queryClient.fetchQuery( + queryKey, + async ({ signal }) => + api.controller.getSongDetail({ + apiClientProps: { + server, + signal, + }, + query: queryFilter, + }), + { + cacheTime: 1000 * 60, + staleTime: 1000 * 60, + }, + ); + + if (!res) throw new Error('Song not found'); + + return { + items: [res], + startIndex: 0, + totalRecordCount: 1, + }; +}; diff --git a/src/renderer/store/player.store.ts b/src/renderer/store/player.store.ts index bd544963..f6a05175 100644 --- a/src/renderer/store/player.store.ts +++ b/src/renderer/store/player.store.ts @@ -96,7 +96,7 @@ export const usePlayerStore = create()( immer((set, get) => ({ actions: { addToQueue: (args) => { - const { playType, songs } = args; + const { initialIndex, playType, songs } = args; const { shuffledIndex } = get().current; const shuffledQueue = get().queue.shuffled; const queueSongs = map(songs, (song) => ({ @@ -107,29 +107,30 @@ export const usePlayerStore = create()( if (playType === Play.NOW) { if (get().shuffle === PlayerShuffle.TRACK) { const shuffledSongs = shuffle(queueSongs); - const foundIndex = queueSongs.findIndex( - (song) => song.uniqueId === shuffledSongs[0].uniqueId, + const index = initialIndex || 0; + const initialSongUniqueId = queueSongs[index].uniqueId; + const initialSongIndex = shuffledSongs.findIndex( + (song) => song.uniqueId === initialSongUniqueId, ); - set((state) => { - state.queue.shuffled = shuffledSongs.map((song) => song.uniqueId); - }); set((state) => { + state.queue.shuffled = shuffledSongs.map((song) => song.uniqueId); state.queue.default = queueSongs; state.current.time = 0; state.current.player = 1; - state.current.index = foundIndex; + state.current.index = initialSongIndex; state.current.shuffledIndex = 0; - state.current.song = shuffledSongs[0]; + state.current.song = shuffledSongs[initialSongIndex]; }); } else { + const index = initialIndex || 0; set((state) => { state.queue.default = queueSongs; state.current.time = 0; state.current.player = 1; - state.current.index = 0; + state.current.index = index; state.current.shuffledIndex = 0; - state.current.song = queueSongs[0]; + state.current.song = queueSongs[index]; }); } } else if (playType === Play.LAST) {