import { useCallback, useRef } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store'; import { usePlayerType } from '/@/renderer/store/settings.store'; import { PlayQueueAddOptions, Play, PlaybackType, PlayerStatus, PlayerShuffle, } from '/@/renderer/types'; import { toast } from '/@/renderer/components/toast/index'; import isElectron from 'is-electron'; import { nanoid } from 'nanoid/non-secure'; import { LibraryItem, QueueSong, Song, SongListResponse, instanceOfCancellationError, } from '/@/renderer/api/types'; import { getPlaylistSongsById, getSongById, getAlbumSongsById, getAlbumArtistSongsById, getSongsByQuery, } from '/@/renderer/features/player/utils'; import { queryKeys } from '/@/renderer/api/query-keys'; const getRootQueryKey = (itemType: LibraryItem, serverId: string) => { let queryKey; switch (itemType) { case LibraryItem.ALBUM: queryKey = queryKeys.songs.list(serverId); break; case LibraryItem.ALBUM_ARTIST: queryKey = queryKeys.songs.list(serverId); break; case LibraryItem.PLAYLIST: queryKey = queryKeys.playlists.songList(serverId); break; case LibraryItem.SONG: queryKey = queryKeys.songs.list(serverId); break; default: queryKey = queryKeys.songs.list(serverId); break; } return queryKey; }; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; const remote = isElectron() ? window.electron.remote : null; const addToQueue = usePlayerStore.getState().actions.addToQueue; export const useHandlePlayQueueAdd = () => { const queryClient = useQueryClient(); const playerType = usePlayerType(); const server = useCurrentServer(); const { play } = usePlayerControls(); const timeoutIds = useRef> | null>({}); const handlePlayQueueAdd = useCallback( async (options: PlayQueueAddOptions) => { if (!server) return toast.error({ message: 'No server selected', type: 'error' }); const { initialIndex, initialSongId, playType, byData, byItemType, query } = options; let songs: QueueSong[] | null = null; let initialSongIndex = 0; if (byItemType) { let songList: SongListResponse | undefined; const { type: itemType, id } = byItemType; const fetchId = nanoid(); timeoutIds.current = { ...timeoutIds.current, [fetchId]: setTimeout(() => { toast.info({ autoClose: false, id: fetchId, message: 'This is taking a while... close the notification to cancel the request', onClose: () => { queryClient.cancelQueries({ exact: false, queryKey: getRootQueryKey(itemType, server?.id), }); }, title: 'Adding to queue', }); }, 2000), }; try { if (itemType === LibraryItem.PLAYLIST) { songList = await getPlaylistSongsById({ id: id?.[0], query, queryClient, server, }); } else if (itemType === LibraryItem.ALBUM) { songList = await getAlbumSongsById({ id, query, queryClient, server }); } else if (itemType === LibraryItem.ALBUM_ARTIST) { songList = await getAlbumArtistSongsById({ id, query, queryClient, server, }); } else if (itemType === LibraryItem.SONG) { if (id?.length === 1) { songList = await getSongById({ id: id?.[0], queryClient, server }); } else { songList = await getSongsByQuery({ query, queryClient, server }); } } clearTimeout(timeoutIds.current[fetchId] as ReturnType); delete timeoutIds.current[fetchId]; toast.hide(fetchId); } catch (err: any) { if (instanceOfCancellationError(err)) { return null; } clearTimeout(timeoutIds.current[fetchId] as ReturnType); delete timeoutIds.current[fetchId]; toast.hide(fetchId); return toast.error({ message: err.message, title: 'Play queue add failed', }); } songs = songList?.items?.map((song: Song) => ({ ...song, uniqueId: nanoid() })) || null; } else if (byData) { songs = byData.map((song) => ({ ...song, uniqueId: nanoid() })) || null; } if (!songs || songs?.length === 0) return toast.warn({ message: 'The query returned no results', title: 'No tracks added', }); if (initialIndex) { initialSongIndex = initialIndex; } else if (initialSongId) { initialSongIndex = songs.findIndex((song) => song.id === initialSongId); } const playerData = addToQueue({ initialIndex: initialSongIndex, playType, songs }); if (playerType === PlaybackType.LOCAL) { mpvPlayer!.volume(usePlayerStore.getState().volume); if (playType === Play.NEXT || playType === Play.LAST) { mpvPlayer!.setQueueNext(playerData); } if (playType === Play.NOW) { mpvPlayer!.setQueue(playerData); mpvPlayer!.play(); } } play(); remote?.updateSong({ currentTime: usePlayerStore.getState().current.time, repeat: usePlayerStore.getState().repeat, shuffle: usePlayerStore.getState().shuffle !== PlayerShuffle.NONE, song: playerData.current.song, status: PlayerStatus.PLAYING, }); return null; }, [play, playerType, queryClient, server], ); return handlePlayQueueAdd; };