diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 440f7ed..9486e08 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { Icon } from 'rsuite'; import { useHistory } from 'react-router-dom'; import cacheImage from '../shared/cacheImage'; -import { getAlbum, getPlaylist, getArtistSongs } from '../../api/api'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { appendPlayQueue, @@ -32,6 +31,7 @@ import { setStatus } from '../../redux/playerSlice'; import { addModalPage } from '../../redux/miscSlice'; import { notifyToast } from '../shared/toast'; import CustomTooltip from '../shared/CustomTooltip'; +import { apiController } from '../../api/controller'; const Card = ({ onClick, @@ -60,7 +60,12 @@ const Card = ({ const handlePlayClick = async () => { if (playClick.type === 'playlist') { - const res = await getPlaylist(playClick.id); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getPlaylist', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res.song); if (songs.entries.length > 0) { @@ -76,7 +81,12 @@ const Card = ({ } if (playClick.type === 'album') { - const res = await getAlbum({ id: playClick.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res.song); if (songs.entries.length > 0) { @@ -92,7 +102,12 @@ const Card = ({ } if (playClick.type === 'artist') { - const res = await getArtistSongs({ id: playClick.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res); if (songs.entries.length > 0) { @@ -110,7 +125,12 @@ const Card = ({ const handlePlayAppend = async (type: 'next' | 'later') => { if (playClick.type === 'playlist') { - const res = await getPlaylist(playClick.id); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getPlaylist', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res.song); if (songs.entries.length > 0) { @@ -122,7 +142,12 @@ const Card = ({ } if (playClick.type === 'album') { - const res = await getAlbum({ id: playClick.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res.song); if (songs.entries.length > 0) { @@ -134,7 +159,12 @@ const Card = ({ } if (playClick.type === 'artist') { - const res = await getArtistSongs({ id: playClick.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: playClick.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res); if (songs.entries.length > 0) { diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index b62d0ab..546fe45 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'; import _ from 'lodash'; import { useHistory } from 'react-router-dom'; import { useQuery, useQueryClient } from 'react-query'; -import { getAlbums, star, unstar } from '../../api/api'; import PageLoader from '../loader/PageLoader'; import GenericPage from '../layout/GenericPage'; import GenericPageHeader from '../layout/GenericPageHeader'; @@ -10,6 +9,8 @@ import ScrollingMenu from '../scrollingmenu/ScrollingMenu'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { setStar } from '../../redux/playQueueSlice'; import { setActive } from '../../redux/albumSlice'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const Dashboard = () => { const history = useHistory(); @@ -28,27 +29,63 @@ const Dashboard = () => { const { isLoading: isLoadingRecent, data: recentAlbums }: any = useQuery( ['recentAlbums', musicFolder], - () => getAlbums({ type: 'recent', size: 20, offset: 0, musicFolderId: musicFolder }) + () => + apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { type: 'recent', size: 20, offset: 0, musicFolderId: musicFolder } + : null, + }) ); const { isLoading: isLoadingNewest, data: newestAlbums }: any = useQuery( ['newestAlbums', musicFolder], - () => getAlbums({ type: 'newest', size: 20, offset: 0, musicFolderId: musicFolder }) + () => + apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { type: 'newest', size: 20, offset: 0, musicFolderId: musicFolder } + : null, + }) ); const { isLoading: isLoadingRandom, data: randomAlbums }: any = useQuery( ['randomAlbums', musicFolder], - () => getAlbums({ type: 'random', size: 20, offset: 0, musicFolderId: musicFolder }) + () => + apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { type: 'random', size: 20, offset: 0, musicFolderId: musicFolder } + : null, + }) ); const { isLoading: isLoadingFrequent, data: frequentAlbums }: any = useQuery( ['frequentAlbums', musicFolder], - () => getAlbums({ type: 'frequent', size: 20, offset: 0, musicFolderId: musicFolder }) + () => + apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { type: 'frequent', size: 20, offset: 0, musicFolderId: musicFolder } + : null, + }) ); const handleFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'star' })); queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); @@ -83,7 +120,11 @@ const Dashboard = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); diff --git a/src/components/library/AlbumList.tsx b/src/components/library/AlbumList.tsx index dbb65fe..2ac904e 100644 --- a/src/components/library/AlbumList.tsx +++ b/src/components/library/AlbumList.tsx @@ -9,7 +9,6 @@ import ListViewType from '../viewtypes/ListViewType'; import useSearchQuery from '../../hooks/useSearchQuery'; import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPage from '../layout/GenericPage'; -import { getAlbums, getGenres, star, unstar } from '../../api/api'; import PageLoader from '../loader/PageLoader'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { @@ -22,6 +21,8 @@ import { StyledInputPicker, StyledInputPickerContainer } from '../shared/styled' import { RefreshButton } from '../shared/ToolbarButtons'; import { setActive } from '../../redux/albumSlice'; import { setSearchQuery } from '../../redux/miscSlice'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const ALBUM_SORT_TYPES = [ { label: 'A-Z (Name)', value: 'alphabeticalByName', role: 'Default' }, @@ -56,18 +57,32 @@ const AlbumList = () => { ['albumList', album.active.filter, musicFolder], () => album.active.filter === 'random' - ? getAlbums({ - type: 'random', - size: config.lookAndFeel.gridView.cardSize, - offset: 0, - musicFolderId: musicFolder, + ? apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { + type: 'random', + size: 100, + offset: 0, + musicFolderId: musicFolder, + } + : null, }) - : getAlbums({ - type: album.active.filter, - size: 500, - offset: 0, - musicFolderId: musicFolder, - recursive: true, + : apiController({ + serverType: config.serverType, + endpoint: 'getAlbums', + args: + config.serverType === Server.Subsonic + ? { + type: album.active.filter, + size: 500, + offset: 0, + musicFolderId: musicFolder, + recursive: true, + } + : null, }), { cacheTime: 3600000, // Stay in cache for 1 hour @@ -75,7 +90,7 @@ const AlbumList = () => { } ); const { data: genres }: any = useQuery(['genreList'], async () => { - const res = await getGenres(); + const res = await apiController({ serverType: config.serverType, endpoint: 'getGenres' }); return res.map((genre: any) => { if (genre.albumCount !== 0) { return { @@ -130,7 +145,11 @@ const AlbumList = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['albumList', album.active.filter, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); starredIndices.forEach((index) => { @@ -140,7 +159,11 @@ const AlbumList = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['albumList', album.active.filter, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); starredIndices.forEach((index) => { diff --git a/src/components/library/AlbumView.tsx b/src/components/library/AlbumView.tsx index cd3f79c..277c75c 100644 --- a/src/components/library/AlbumView.tsx +++ b/src/components/library/AlbumView.tsx @@ -12,7 +12,6 @@ import { PlayAppendNextButton, PlayButton, } from '../shared/ToolbarButtons'; -import { getAlbum, getDownloadUrl, star, unstar } from '../../api/api'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { appendPlayQueue, @@ -51,6 +50,8 @@ import { BlurredBackgroundWrapper, PageHeaderSubtitleDataLine, } from '../layout/styled'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; interface AlbumParams { id: string; @@ -68,7 +69,11 @@ const AlbumView = ({ ...rest }: any) => { const albumId = rest.id ? rest.id : id; const { isLoading, isError, data, error }: any = useQuery(['album', albumId], () => - getAlbum({ id: albumId }) + apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: albumId }, + }) ); const filteredData = useSearchQuery(misc.searchQuery, data?.song, [ 'title', @@ -141,17 +146,29 @@ const AlbumView = ({ ...rest }: any) => { const handleFavorite = async () => { if (!data.starred) { - await star({ id: data.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: data.id, type: 'album' } : null, + }); queryClient.setQueryData(['album', id], { ...data, starred: Date.now() }); } else { - await unstar({ id: data.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: data.id, type: 'album' } : null, + }); queryClient.setQueryData(['album', id], { ...data, starred: undefined }); } }; const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'star' })); queryClient.setQueryData(['album', id], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData?.song, { id: rowData.id })); @@ -162,7 +179,11 @@ const AlbumView = ({ ...rest }: any) => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); queryClient.setQueryData(['album', id], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData?.song, { id: rowData.id })); @@ -175,16 +196,28 @@ const AlbumView = ({ ...rest }: any) => { } }; - const handleDownload = (type: 'copy' | 'download') => { + const handleDownload = async (type: 'copy' | 'download') => { // If not Navidrome (this assumes Airsonic), then we need to use a song's parent // to download. This is because Airsonic does not support downloading via album ids // that are provided by /getAlbum or /getAlbumList2 if (data.song[0]?.parent) { if (type === 'download') { - shell.openExternal(getDownloadUrl(data.song[0].parent)); + shell.openExternal( + await apiController({ + serverType: config.serverType, + endpoint: 'getDownloadUrl', + args: { id: data.song[0].parent }, + }) + ); } else { - clipboard.writeText(getDownloadUrl(data.song[0].parent)); + clipboard.writeText( + await apiController({ + serverType: config.serverType, + endpoint: 'getDownloadUrl', + args: { id: data.song[0].parent }, + }) + ); notifyToast('info', 'Download links copied!'); } } else { diff --git a/src/components/library/ArtistList.tsx b/src/components/library/ArtistList.tsx index 10df9b5..8605d71 100644 --- a/src/components/library/ArtistList.tsx +++ b/src/components/library/ArtistList.tsx @@ -4,7 +4,6 @@ import settings from 'electron-settings'; import { useQuery, useQueryClient } from 'react-query'; import { useHistory } from 'react-router'; import { ButtonToolbar } from 'rsuite'; -import { getArtists, star, unstar } from '../../api/api'; import useSearchQuery from '../../hooks/useSearchQuery'; import GenericPage from '../layout/GenericPage'; import GenericPageHeader from '../layout/GenericPageHeader'; @@ -19,6 +18,8 @@ import { } from '../../redux/multiSelectSlice'; import GridViewType from '../viewtypes/GridViewType'; import { RefreshButton } from '../shared/ToolbarButtons'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const ArtistList = () => { const dispatch = useAppDispatch(); @@ -39,7 +40,12 @@ const ArtistList = () => { const { isLoading, isError, data: artists, error }: any = useQuery( ['artistList', musicFolder], - () => getArtists({ musicFolderId: musicFolder }), + () => + apiController({ + serverType: config.serverType, + endpoint: 'getArtists', + args: config.serverType === Server.Subsonic ? { musicFolderId: musicFolder } : null, + }), { cacheTime: 3600000, // Stay in cache for 1 hour staleTime: Infinity, // Only allow manual refresh @@ -79,7 +85,11 @@ const ArtistList = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'artist' } : null, + }); queryClient.setQueryData(['artistList', musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); starredIndices.forEach((index) => { @@ -89,7 +99,11 @@ const ArtistList = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'artist' } : null, + }); queryClient.setQueryData(['artistList', musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id })); starredIndices.forEach((index) => { diff --git a/src/components/library/ArtistView.tsx b/src/components/library/ArtistView.tsx index 880fed4..d1af098 100644 --- a/src/components/library/ArtistView.tsx +++ b/src/components/library/ArtistView.tsx @@ -14,15 +14,6 @@ import { PlayAppendNextButton, PlayButton, } from '../shared/ToolbarButtons'; -import { - getAlbum, - getArtistSongs, - getArtist, - getArtistInfo, - getDownloadUrl, - star, - unstar, -} from '../../api/api'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { toggleSelected, @@ -54,6 +45,8 @@ import { import { StyledButton, StyledPopover, StyledTag } from '../shared/styled'; import { setStatus } from '../../redux/playerSlice'; import { GradientBackground, PageHeaderSubtitleDataLine } from '../layout/styled'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const fac = new FastAverageColor(); @@ -75,14 +68,20 @@ const ArtistView = ({ ...rest }: any) => { const { id } = useParams(); const artistId = rest.id ? rest.id : id; const { isLoading, isError, data, error }: any = useQuery(['artist', artistId], () => - getArtist({ id: artistId }) + apiController({ serverType: config.serverType, endpoint: 'getArtist', args: { id: artistId } }) ); const { isLoading: isLoadingAI, isError: isErrorAI, data: artistInfo, error: errorAI, - }: any = useQuery(['artistInfo', artistId], () => getArtistInfo({ id: artistId, count: 8 })); + }: any = useQuery(['artistInfo', artistId], () => + apiController({ + serverType: config.serverType, + endpoint: 'getArtistInfo', + args: config.serverType === Server.Subsonic ? { id: artistId, count: 8 } : null, + }) + ); const filteredData = useSearchQuery(misc.searchQuery, data?.album, [ 'title', @@ -116,16 +115,29 @@ const ArtistView = ({ ...rest }: any) => { const handleFavorite = async () => { if (!data.starred) { - await star({ id: data.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: data.id, type: 'artist' } : null, + }); queryClient.setQueryData(['artist', artistId], { ...data, starred: Date.now() }); } else { - await unstar({ id: data.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: data.id, type: 'artist' } : null, + }); queryClient.setQueryData(['artist', artistId], { ...data, starred: undefined }); } }; const handlePlay = async () => { - const res = await getArtistSongs({ id: data.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: data.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res); if (songs.entries.length > 0) { @@ -141,7 +153,12 @@ const ArtistView = ({ ...rest }: any) => { }; const handlePlayAppend = async (type: 'next' | 'later') => { - const res = await getArtistSongs({ id: data.id }); + const res = await await apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: data.id }, + }); + const songs = filterPlayQueue(config.playback.filters, res); if (songs.entries.length > 0) { @@ -154,7 +171,11 @@ const ArtistView = ({ ...rest }: any) => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['artist', artistId], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); starredIndices.forEach((index) => { @@ -164,7 +185,11 @@ const ArtistView = ({ ...rest }: any) => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['artist', artistId], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); starredIndices.forEach((index) => { @@ -179,9 +204,21 @@ const ArtistView = ({ ...rest }: any) => { const handleDownload = async (type: 'copy' | 'download') => { if (data.album[0]?.parent) { if (type === 'download') { - shell.openExternal(getDownloadUrl(data.album[0].parent)); + shell.openExternal( + await apiController({ + serverType: config.serverType, + endpoint: 'getDownloadUrl', + args: { id: data.album[0].parent }, + }) + ); } else { - clipboard.writeText(getDownloadUrl(data.album[0].parent)); + clipboard.writeText( + await apiController({ + serverType: config.serverType, + endpoint: 'getDownloadUrl', + args: { id: data.album[0].parent }, + }) + ); notifyToast('info', 'Download links copied!'); } } else { @@ -189,9 +226,21 @@ const ArtistView = ({ ...rest }: any) => { for (let i = 0; i < data.album.length; i += 1) { // eslint-disable-next-line no-await-in-loop - const albumRes = await getAlbum({ id: data.album[i].id }); + const albumRes = await apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: data.album[i].id }, + }); + if (albumRes.song[0]?.parent) { - downloadUrls.push(getDownloadUrl(albumRes.song[0].parent)); + downloadUrls.push( + // eslint-disable-next-line no-await-in-loop + await apiController({ + serverType: config.serverType, + endpoint: 'getDownloadUrl', + args: { id: albumRes.song[0].parent }, + }) + ); } else { notifyToast('warning', `[${albumRes.title}] No parent album found`); } diff --git a/src/components/library/FolderList.tsx b/src/components/library/FolderList.tsx index 82b335b..4b70704 100644 --- a/src/components/library/FolderList.tsx +++ b/src/components/library/FolderList.tsx @@ -4,14 +4,6 @@ import _ from 'lodash'; import { useQuery, useQueryClient } from 'react-query'; import { useHistory } from 'react-router-dom'; import { ButtonToolbar, Icon } from 'rsuite'; -import { - getIndexes, - getMusicDirectory, - getMusicFolders, - setRating, - star, - unstar, -} from '../../api/api'; import PageLoader from '../loader/PageLoader'; import ListViewType from '../viewtypes/ListViewType'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; @@ -29,6 +21,8 @@ import { setStatus } from '../../redux/playerSlice'; import useSearchQuery from '../../hooks/useSearchQuery'; import { setCurrentViewedFolder } from '../../redux/folderSlice'; import useRouterQuery from '../../hooks/useRouterQuery'; +import { Server } from '../../types'; +import { apiController } from '../../api/controller'; const FolderList = () => { const dispatch = useAppDispatch(); @@ -43,19 +37,28 @@ const FolderList = () => { const { isLoading, isError, data: indexData, error }: any = useQuery( ['indexes', musicFolder], - () => getIndexes({ musicFolderId: musicFolder }) + () => + apiController({ + serverType: config.serverType, + endpoint: 'getIndexes', + args: config.serverType === Server.Subsonic ? { musicFolderId: musicFolder } : null, + }) ); const { isLoading: isLoadingFolderData, data: folderData }: any = useQuery( ['folder', folder.currentViewedFolder], - () => getMusicDirectory({ id: folder.currentViewedFolder }), + () => + apiController({ + serverType: config.serverType, + endpoint: 'getMusicDirectory', + args: { id: folder.currentViewedFolder }, + }), { enabled: folder.currentViewedFolder !== '', } ); - const { isLoading: isLoadingMusicFolders, data: musicFolders } = useQuery( - ['musicFolders'], - getMusicFolders + const { isLoading: isLoadingMusicFolders, data: musicFolders } = useQuery(['musicFolders'], () => + apiController({ serverType: config.serverType, endpoint: 'getMusicFolders' }) ); const filteredData = useSearchQuery( @@ -112,7 +115,11 @@ const FolderList = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['folder', folder.currentViewedFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.child, { id: rowData.id })); starredIndices.forEach((index) => { @@ -122,7 +129,11 @@ const FolderList = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['folder', folder.currentViewedFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.child, { id: rowData.id })); starredIndices.forEach((index) => { @@ -135,7 +146,11 @@ const FolderList = () => { }; const handleRowRating = (rowData: any, e: number) => { - setRating({ id: rowData.id, rating: e }); + apiController({ + serverType: config.serverType, + endpoint: 'setRating', + args: { id: rowData.id, rating: e }, + }); dispatch(setRate({ id: [rowData.id], rating: e })); }; diff --git a/src/components/library/GenreList.tsx b/src/components/library/GenreList.tsx index e511d90..a7c4094 100644 --- a/src/components/library/GenreList.tsx +++ b/src/components/library/GenreList.tsx @@ -14,8 +14,8 @@ import { toggleRangeSelected, toggleSelected, } from '../../redux/multiSelectSlice'; -import { getGenres } from '../../api/api'; import { setActive } from '../../redux/albumSlice'; +import { apiController } from '../../api/controller'; const GenreList = () => { const dispatch = useAppDispatch(); @@ -24,7 +24,7 @@ const GenreList = () => { const album = useAppSelector((state) => state.album); const misc = useAppSelector((state) => state.misc); const { isLoading, isError, data: genres, error }: any = useQuery(['genrePageList'], async () => { - const res = await getGenres(); + const res = await apiController({ serverType: config.serverType, endpoint: 'getGenres' }); return _.orderBy(res, 'songCount', 'desc'); }); const filteredData = useSearchQuery(misc.searchQuery, genres, ['title']); diff --git a/src/components/player/NowPlayingMiniView.tsx b/src/components/player/NowPlayingMiniView.tsx index 0ed1e7c..99aa11c 100644 --- a/src/components/player/NowPlayingMiniView.tsx +++ b/src/components/player/NowPlayingMiniView.tsx @@ -47,7 +47,6 @@ import { getPlayedSongsNotification, isFailedResponse, } from '../../shared/utils'; -import { getGenres, getMusicFolders, getRandomSongs, star, unstar } from '../../api/api'; import { AutoPlaylistButton, ClearQueueButton, @@ -57,6 +56,8 @@ import { ShuffleButton, } from '../shared/ToolbarButtons'; import { notifyToast } from '../shared/toast'; +import { apiController } from '../../api/controller'; +import { Server, Song } from '../../types'; const NowPlayingMiniView = () => { const tableRef = useRef(); @@ -78,7 +79,7 @@ const NowPlayingMiniView = () => { const [musicFolder, setMusicFolder] = useState(folder.musicFolder); const { isLoading: isLoadingGenres, data: genres }: any = useQuery(['genreList'], async () => { - const res = await getGenres(); + const res = await apiController({ serverType: config.serverType, endpoint: 'getGenres' }); const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc'); return genresOrderedBySongCount.map((genre: any) => { return { @@ -89,9 +90,8 @@ const NowPlayingMiniView = () => { }); }); - const { isLoading: isLoadingMusicFolders, data: musicFolders } = useQuery( - ['musicFolders'], - getMusicFolders + const { isLoading: isLoadingMusicFolders, data: musicFolders } = useQuery(['musicFolders'], () => + apiController({ serverType: config.serverType, endpoint: 'getMusicFolders' }) ); useHotkeys( @@ -175,12 +175,20 @@ const NowPlayingMiniView = () => { const handlePlayRandom = async (action: 'play' | 'addNext' | 'addLater') => { setIsLoadingRandom(true); - const res = await getRandomSongs({ - size: autoPlaylistTrackCount, - fromYear: autoPlaylistFromYear !== 0 ? autoPlaylistFromYear : undefined, - toYear: autoPlaylistToYear !== 0 ? autoPlaylistToYear : undefined, - genre: randomPlaylistGenre, - musicFolderId: musicFolder, + + const res: Song[] = await apiController({ + serverType: config.serverType, + endpoint: 'getRandomSongs', + args: + config.serverType === Server.Subsonic + ? { + size: autoPlaylistTrackCount, + fromYear: autoPlaylistFromYear !== 0 ? autoPlaylistFromYear : undefined, + toYear: autoPlaylistToYear !== 0 ? autoPlaylistToYear : undefined, + genre: randomPlaylistGenre, + musicFolderId: musicFolder, + } + : null, }); if (isFailedResponse(res)) { @@ -254,10 +262,18 @@ const NowPlayingMiniView = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'star' })); } else { - await unstar({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); } }; diff --git a/src/components/player/NowPlayingView.tsx b/src/components/player/NowPlayingView.tsx index f5eae9b..7ef7d26 100644 --- a/src/components/player/NowPlayingView.tsx +++ b/src/components/player/NowPlayingView.tsx @@ -58,8 +58,9 @@ import { getPlayedSongsNotification, isFailedResponse, } from '../../shared/utils'; -import { getGenres, getMusicFolders, getRandomSongs, setRating, star, unstar } from '../../api/api'; import { notifyToast } from '../shared/toast'; +import { apiController } from '../../api/controller'; +import { Server, Song } from '../../types'; const NowPlayingView = () => { const tableRef = useRef(); @@ -81,7 +82,9 @@ const NowPlayingView = () => { const [isLoadingRandom, setIsLoadingRandom] = useState(false); const [musicFolder, setMusicFolder] = useState(folder.musicFolder); - const { data: musicFolders } = useQuery(['musicFolders'], getMusicFolders); + const { data: musicFolders } = useQuery(['musicFolders'], () => + apiController({ serverType: config.serverType, endpoint: 'getMusicFolders' }) + ); const filteredData = useSearchQuery(misc.searchQuery, playQueue.entry, [ 'title', @@ -93,7 +96,7 @@ const NowPlayingView = () => { ]); const { data: genres }: any = useQuery(['genreList'], async () => { - const res = await getGenres(); + const res = await apiController({ serverType: config.serverType, endpoint: 'getGenres' }); const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc'); return genresOrderedBySongCount.map((genre: any) => { return { @@ -186,12 +189,19 @@ const NowPlayingView = () => { const handlePlayRandom = async (action: 'play' | 'addNext' | 'addLater') => { setIsLoadingRandom(true); - const res = await getRandomSongs({ - size: autoPlaylistTrackCount, - fromYear: autoPlaylistFromYear !== 0 ? autoPlaylistFromYear : undefined, - toYear: autoPlaylistToYear !== 0 ? autoPlaylistToYear : undefined, - genre: randomPlaylistGenre, - musicFolderId: musicFolder, + const res: Song[] = await apiController({ + serverType: config.serverType, + endpoint: 'getRandomSongs', + args: + config.serverType === Server.Subsonic + ? { + size: autoPlaylistTrackCount, + fromYear: autoPlaylistFromYear !== 0 ? autoPlaylistFromYear : undefined, + toYear: autoPlaylistToYear !== 0 ? autoPlaylistToYear : undefined, + genre: randomPlaylistGenre, + musicFolderId: musicFolder, + } + : null, }); if (isFailedResponse(res)) { @@ -265,16 +275,28 @@ const NowPlayingView = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'star' })); } else { - await unstar({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); } }; const handleRowRating = (rowData: any, e: number) => { - setRating({ id: rowData.id, rating: e }); + apiController({ + serverType: config.serverType, + endpoint: 'setRating', + args: { id: rowData.id, rating: e }, + }); dispatch(setRate({ id: [rowData.id], rating: e })); }; diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index 7251ff5..e6ce951 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -28,6 +28,8 @@ import { setCurrentSeek } from '../../redux/playerSlice'; import cacheSong from '../shared/cacheSong'; import { isCached } from '../../shared/utils'; import { scrobble } from '../../api/api'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const gaplessListenHandler = ( currentPlayerRef: any, @@ -38,7 +40,8 @@ const gaplessListenHandler = ( pollingInterval: number, shouldScrobble: boolean, scrobbled: boolean, - setScrobbled: any + setScrobbled: any, + serverType: Server ) => { const currentSeek = currentPlayerRef.current?.audioEl.current?.currentTime || 0; const duration = currentPlayerRef.current?.audioEl.current?.duration; @@ -67,7 +70,12 @@ const gaplessListenHandler = ( currentSeek <= duration - 2 ) { setScrobbled(true); - scrobble({ id: playQueue.currentSongId, submission: true }); + apiController({ + serverType, + endpoint: 'scrobble', + args: + serverType === Server.Subsonic ? { id: playQueue.currentSongId, submission: true } : null, + }); } }; @@ -84,7 +92,8 @@ const listenHandler = ( debug: boolean, shouldScrobble: boolean, scrobbled: boolean, - setScrobbled: any + setScrobbled: any, + serverType: Server ) => { const currentSeek = currentPlayerRef.current?.audioEl.current?.currentTime || 0; const duration = currentPlayerRef.current?.audioEl.current?.duration; @@ -227,7 +236,12 @@ const listenHandler = ( currentSeek <= fadeAtTime ) { setScrobbled(true); - scrobble({ id: playQueue.currentSongId, submission: true }); + apiController({ + serverType, + endpoint: 'scrobble', + args: + serverType === Server.Subsonic ? { id: playQueue.currentSongId, submission: true } : null, + }); } }; @@ -238,6 +252,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { const playQueue = useAppSelector((state) => state.playQueue); const player = useAppSelector((state) => state.player); const misc = useAppSelector((state) => state.misc); + const config = useAppSelector((state) => state.config); const cacheSongs = settings.getSync('cacheSongs'); const [title] = useState(''); const [scrobbled, setScrobbled] = useState(false); @@ -348,9 +363,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { playQueue.showDebugWindow, playQueue.scrobble, scrobbled, - setScrobbled + setScrobbled, + config.serverType ); - }, [currentEntryList, dispatch, playQueue, scrobbled]); + }, [config.serverType, currentEntryList, dispatch, playQueue, scrobbled]); const handleListenPlayer2 = useCallback(() => { listenHandler( @@ -366,9 +382,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { playQueue.showDebugWindow, playQueue.scrobble, scrobbled, - setScrobbled + setScrobbled, + config.serverType ); - }, [currentEntryList, dispatch, playQueue, scrobbled]); + }, [config.serverType, currentEntryList, dispatch, playQueue, scrobbled]); const handleOnEndedPlayer1 = useCallback(() => { player1Ref.current.audioEl.current.currentTime = 0; @@ -470,9 +487,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { playQueue.pollingInterval, playQueue.scrobble, scrobbled, - setScrobbled + setScrobbled, + config.serverType ); - }, [dispatch, playQueue, scrobbled]); + }, [config.serverType, dispatch, playQueue, scrobbled]); const handleGaplessPlayer2 = useCallback(() => { gaplessListenHandler( @@ -484,9 +502,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { playQueue.pollingInterval, playQueue.scrobble, scrobbled, - setScrobbled + setScrobbled, + config.serverType ); - }, [dispatch, playQueue, scrobbled]); + }, [config.serverType, dispatch, playQueue, scrobbled]); const handleOnPlay = useCallback( (playerNumber: 1 | 2) => { diff --git a/src/components/player/PlayerBar.tsx b/src/components/player/PlayerBar.tsx index e27c6fd..e13f4ee 100644 --- a/src/components/player/PlayerBar.tsx +++ b/src/components/player/PlayerBar.tsx @@ -29,18 +29,20 @@ import { setStatus, resetPlayer } from '../../redux/playerSlice'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import Player from './Player'; import CustomTooltip from '../shared/CustomTooltip'; -import { star, unstar } from '../../api/api'; import placeholderImg from '../../img/placeholder.jpg'; import DebugWindow from '../debug/DebugWindow'; import { CoverArtWrapper } from '../layout/styled'; import { getCurrentEntryList, isCached } from '../../shared/utils'; import { StyledPopover } from '../shared/styled'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const PlayerBar = () => { const queryClient = useQueryClient(); const playQueue = useAppSelector((state) => state.playQueue); const player = useAppSelector((state) => state.player); const misc = useAppSelector((state) => state.misc); + const config = useAppSelector((state) => state.config); const dispatch = useAppDispatch(); const [seek, setSeek] = useState(0); const [isDragging, setIsDragging] = useState(false); @@ -273,7 +275,14 @@ const PlayerBar = () => { const handleFavorite = async () => { if (!playQueue[currentEntryList][playQueue.currentIndex].starred) { - await star({ id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: + config.serverType === Server.Subsonic + ? { id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'music' } + : null, + }); dispatch( setStar({ id: [playQueue[currentEntryList][playQueue.currentIndex].id], @@ -281,7 +290,14 @@ const PlayerBar = () => { }) ); } else { - await unstar({ id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: + config.serverType === Server.Subsonic + ? { id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'music' } + : null, + }); dispatch( setStar({ id: [playQueue[currentEntryList][playQueue.currentIndex].id], diff --git a/src/components/playlist/PlaylistList.tsx b/src/components/playlist/PlaylistList.tsx index 260e300..f2a75a8 100644 --- a/src/components/playlist/PlaylistList.tsx +++ b/src/components/playlist/PlaylistList.tsx @@ -4,7 +4,6 @@ import { useHistory } from 'react-router-dom'; import { Form, Whisper } from 'rsuite'; import settings from 'electron-settings'; import useSearchQuery from '../../hooks/useSearchQuery'; -import { createPlaylist } from '../../api/api'; import ListViewType from '../viewtypes/ListViewType'; import PageLoader from '../loader/PageLoader'; import GenericPage from '../layout/GenericPage'; @@ -39,7 +38,11 @@ const PlaylistList = () => { const handleCreatePlaylist = async (name: string) => { try { - const res = await createPlaylist({ name }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'createPlaylist', + args: { name }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); diff --git a/src/components/playlist/PlaylistView.tsx b/src/components/playlist/PlaylistView.tsx index 41f5649..e3cb4f5 100644 --- a/src/components/playlist/PlaylistView.tsx +++ b/src/components/playlist/PlaylistView.tsx @@ -16,16 +16,6 @@ import { SaveButton, UndoButton, } from '../shared/ToolbarButtons'; -import { - clearPlaylist, - deletePlaylist, - updatePlaylistSongsLg, - updatePlaylistSongs, - updatePlaylist, - star, - unstar, - setRating, -} from '../../api/api'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { fixPlayer2Index, @@ -76,6 +66,7 @@ import { import { PageHeaderSubtitleDataLine } from '../layout/styled'; import CustomTooltip from '../shared/CustomTooltip'; import { apiController } from '../../api/controller'; +import { Server } from '../../types'; interface PlaylistParams { id: string; @@ -224,7 +215,12 @@ const PlaylistView = ({ ...rest }) => { // Smaller playlists can use the safe /createPlaylist method of saving if (playlistData.length <= 400 && !recovery) { - res = await updatePlaylistSongs({ id: data.id, entry: playlistData }); + res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylistSongs', + args: { id: data.id, entry: playlistData }, + }); + if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); } else { @@ -236,17 +232,25 @@ const PlaylistView = ({ ...rest }) => { } else { // For larger playlists, we'll need to first clear out the playlist and then re-populate it // Tested on Airsonic instances, /createPlaylist fails with around ~350+ songId params - res = await clearPlaylist(data.id); + res = await apiController({ + serverType: config.serverType, + endpoint: 'clearPlaylist', + args: { id: data.id }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); return dispatch(removeProcessingPlaylist(data.id)); } - res = await updatePlaylistSongsLg({ id: data.id, entry: playlistData }); + res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylistSongsLg', + args: { id: data.id, entry: playlistData }, + }); if (isFailedResponse(res)) { - res.forEach((response) => { + res.forEach((response: any) => { if (isFailedResponse(response)) { notifyToast('error', errorMessages(response)[0]); } @@ -287,11 +291,18 @@ const PlaylistView = ({ ...rest }) => { const handleEdit = async () => { setIsSubmittingEdit(true); - const res = await updatePlaylist({ - id: data.id, - name: editName, - comment: editDescription, - isPublic: editPublic, + const res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylist', + args: + config.serverType === Server.Subsonic + ? { + id: data.id, + name: editName, + comment: editDescription, + isPublic: editPublic, + } + : null, }); if (isFailedResponse(res)) { @@ -308,7 +319,11 @@ const PlaylistView = ({ ...rest }) => { const handleDelete = async () => { try { - const res = await deletePlaylist({ id: data.id }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'deletePlaylist', + args: { id: data.id }, + }); if (isFailedResponse(res)) { notifyToast('error', res.error.message); @@ -334,7 +349,11 @@ const PlaylistView = ({ ...rest }) => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'star' })); dispatch(setPlaylistStar({ id: [rowData.id], type: 'star' })); @@ -347,7 +366,11 @@ const PlaylistView = ({ ...rest }) => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); dispatch(setPlaylistStar({ id: [rowData.id], type: 'unstar' })); @@ -363,7 +386,11 @@ const PlaylistView = ({ ...rest }) => { }; const handleRowRating = (rowData: any, e: number) => { - setRating({ id: rowData.id, rating: e }); + apiController({ + serverType: config.serverType, + endpoint: 'setRating', + args: { id: rowData.id, rating: e }, + }); dispatch(setRate({ id: [rowData.id], rating: e })); dispatch(setPlaylistRate({ id: [rowData.id], rating: e })); }; diff --git a/src/components/search/SearchView.tsx b/src/components/search/SearchView.tsx index 7a52e10..7b6dffc 100644 --- a/src/components/search/SearchView.tsx +++ b/src/components/search/SearchView.tsx @@ -3,7 +3,6 @@ import _ from 'lodash'; import settings from 'electron-settings'; import { useHistory } from 'react-router-dom'; import { useQuery, useQueryClient } from 'react-query'; -import { getSearch, star, unstar } from '../../api/api'; import useRouterQuery from '../../hooks/useRouterQuery'; import GenericPage from '../layout/GenericPage'; import GenericPageHeader from '../layout/GenericPageHeader'; @@ -20,6 +19,8 @@ import { fixPlayer2Index, setPlayQueueByRowClick } from '../../redux/playQueueSl import { setStatus } from '../../redux/playerSlice'; import ListViewTable from '../viewtypes/ListViewTable'; import { SectionTitle, SectionTitleWrapper, StyledPanel } from '../shared/styled'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const SearchView = () => { const dispatch = useAppDispatch(); @@ -40,7 +41,14 @@ const SearchView = () => { }, [folder]); const { isLoading, isError, data, error }: any = useQuery(['search', urlQuery, musicFolder], () => - getSearch({ query: urlQuery, songCount: 100, musicFolderId: musicFolder }) + apiController({ + serverType: config.serverType, + endpoint: 'getSearch', + args: + config.serverType === Server.Subsonic + ? { query: urlQuery, songCount: 100, musicFolderId: musicFolder } + : null, + }) ); let timeout: any = null; @@ -83,7 +91,11 @@ const SearchView = () => { const handleRowFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.song, { id: rowData.id })); starredIndices.forEach((index) => { @@ -93,7 +105,11 @@ const SearchView = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.song, { id: rowData.id })); starredIndices.forEach((index) => { @@ -107,7 +123,11 @@ const SearchView = () => { const handleArtistFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'artist' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.artist, { id: rowData.id })); starredIndices.forEach((index) => { @@ -117,7 +137,11 @@ const SearchView = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.artist, { id: rowData.id })); starredIndices.forEach((index) => { @@ -131,7 +155,11 @@ const SearchView = () => { const handleAlbumFavorite = async (rowData: any) => { if (!rowData.starred) { - await star({ id: rowData.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'star', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'artist' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.album, { id: rowData.id })); starredIndices.forEach((index) => { @@ -141,7 +169,11 @@ const SearchView = () => { return oldData; }); } else { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); queryClient.setQueryData(['search', urlQuery, musicFolder], (oldData: any) => { const starredIndices = _.keys(_.pickBy(oldData.album, { id: rowData.id })); starredIndices.forEach((index) => { diff --git a/src/components/settings/Config.tsx b/src/components/settings/Config.tsx index 6f02882..cf8c45b 100644 --- a/src/components/settings/Config.tsx +++ b/src/components/settings/Config.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { shell } from 'electron'; import { Whisper, Nav, ButtonToolbar } from 'rsuite'; -import { startScan, getScanStatus } from '../../api/api'; import GenericPage from '../layout/GenericPage'; import DisconnectButton from './DisconnectButton'; import GenericPageHeader from '../layout/GenericPageHeader'; @@ -18,6 +17,7 @@ import packageJson from '../../package.json'; import ServerConfig from './ConfigPanels/ServerConfig'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { setActive } from '../../redux/configSlice'; +import { apiController } from '../../api/controller'; const GITHUB_RELEASE_URL = 'https://api.github.com/repos/jeffvli/sonixd/releases?per_page=3'; @@ -40,7 +40,7 @@ const Config = () => { useEffect(() => { // Check scan status on render - getScanStatus() + apiController({ serverType: config.serverType, endpoint: 'getScanStatus' }) .then((status) => { if (status.scanning) { return setIsScanning(true); @@ -49,13 +49,13 @@ const Config = () => { return setScanProgress(0); }) .catch((err) => console.log(err)); - }, []); + }, [config.serverType]); useEffect(() => { // Reload scan status on interval during scan if (isScanning) { const interval = setInterval(() => { - getScanStatus() + apiController({ serverType: config.serverType, endpoint: 'getScanStatus' }) .then((status) => { if (status.scanning) { return setScanProgress(status.count); @@ -69,7 +69,7 @@ const Config = () => { return () => clearInterval(interval); } return () => clearInterval(); - }, [isScanning]); + }, [config.serverType, isScanning]); return ( { { - startScan(); + apiController({ serverType: config.serverType, endpoint: 'startScan' }); setIsScanning(true); }} disabled={isScanning} diff --git a/src/components/settings/ConfigPanels/ServerConfig.tsx b/src/components/settings/ConfigPanels/ServerConfig.tsx index 72e2a9d..a41d96f 100644 --- a/src/components/settings/ConfigPanels/ServerConfig.tsx +++ b/src/components/settings/ConfigPanels/ServerConfig.tsx @@ -5,13 +5,16 @@ import { CheckboxGroup } from 'rsuite'; import { ConfigPanel } from '../styled'; import { StyledCheckbox, StyledInputPicker, StyledInputPickerContainer } from '../../shared/styled'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; -import { getMusicFolders } from '../../../api/api'; import { setAppliedFolderViews, setMusicFolder } from '../../../redux/folderSlice'; +import { apiController } from '../../../api/controller'; const ServerConfig = () => { const dispatch = useAppDispatch(); const folder = useAppSelector((state) => state.folder); - const { isLoading, data: musicFolders } = useQuery(['musicFolders'], getMusicFolders); + const config = useAppSelector((state) => state.config); + const { isLoading, data: musicFolders } = useQuery(['musicFolders'], () => + apiController({ serverType: config.serverType, endpoint: 'getMusicFolders' }) + ); const musicFolderPickerContainerRef = useRef(null); return ( diff --git a/src/components/shared/ContextMenu.tsx b/src/components/shared/ContextMenu.tsx index 7e7ab76..e45be30 100644 --- a/src/components/shared/ContextMenu.tsx +++ b/src/components/shared/ContextMenu.tsx @@ -5,18 +5,6 @@ import { nanoid } from 'nanoid/non-secure'; import { useQuery, useQueryClient } from 'react-query'; import { useHistory } from 'react-router'; import { Col, FlexboxGrid, Form, Grid, Icon, Row, Whisper } from 'rsuite'; -import { - getPlaylists, - updatePlaylistSongsLg, - createPlaylist, - batchStar, - batchUnstar, - getAlbum, - getPlaylist, - deletePlaylist, - getArtistSongs, - getMusicDirectorySongs, -} from '../../api/api'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { addModalPage, @@ -67,6 +55,7 @@ import { isFailedResponse, } from '../../shared/utils'; import { setStatus } from '../../redux/playerSlice'; +import { apiController } from '../../api/controller'; export const ContextMenuButton = ({ text, hotkey, children, ...rest }: any) => { return ( @@ -120,7 +109,9 @@ export const GlobalContextMenu = () => { const [indexToMoveTo, setIndexToMoveTo] = useState(0); const playlistPickerContainerRef = useRef(null); - const { data: playlists }: any = useQuery(['playlists'], () => getPlaylists()); + const { data: playlists }: any = useQuery(['playlists'], () => + apiController({ serverType: config.serverType, endpoint: 'getPlaylists' }) + ); const handlePlay = async () => { dispatch(setContextMenu({ show: false })); @@ -135,7 +126,13 @@ export const GlobalContextMenu = () => { }); for (let i = 0; i < folders.length; i += 1) { - promises.push(getMusicDirectorySongs({ id: folders[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getMusicDirectorySongs', + args: { id: folders[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -154,7 +151,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' })); } else if (misc.contextMenu.type === 'playlist') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getPlaylist(multiSelect.selected[i].id)); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getPlaylist', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -172,7 +175,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' })); } else if (misc.contextMenu.type === 'album') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getAlbum({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -191,7 +200,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' })); } else if (misc.contextMenu.type === 'artist') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getArtistSongs({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -223,7 +238,13 @@ export const GlobalContextMenu = () => { }); for (let i = 0; i < folders.length; i += 1) { - promises.push(getMusicDirectorySongs({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getMusicDirectorySongs', + args: { id: folders[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -238,7 +259,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' })); } else if (misc.contextMenu.type === 'playlist') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getPlaylist(multiSelect.selected[i].id)); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getPlaylist', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -252,7 +279,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' })); } else if (misc.contextMenu.type === 'album') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getAlbum({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -266,7 +299,13 @@ export const GlobalContextMenu = () => { notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' })); } else if (misc.contextMenu.type === 'artist') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getArtistSongs({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getArtistSongs', + args: { id: multiSelect.selected[i].id }, + }) + ); } const res = await Promise.all(promises); @@ -332,7 +371,13 @@ export const GlobalContextMenu = () => { }); for (let i = 0; i < folders.length; i += 1) { - promises.push(getMusicDirectorySongs({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getMusicDirectorySongs', + args: { id: folders[i].id }, + }) + ); } const folderSongs = await Promise.all(promises); @@ -340,7 +385,11 @@ export const GlobalContextMenu = () => { folderSongs.push(_.orderBy(music, 'rowIndex', 'asc')); songs = _.flatten(folderSongs); - res = await updatePlaylistSongsLg({ id: localSelectedPlaylistId, entry: songs }); + res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylistSongsLg', + args: { id: localSelectedPlaylistId, entry: songs }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); @@ -349,12 +398,22 @@ export const GlobalContextMenu = () => { } } else if (misc.contextMenu.type === 'playlist') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getPlaylist(multiSelect.selected[i].id)); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getPlaylist', + args: { id: multiSelect.selected[i].id }, + }) + ); } res = await Promise.all(promises); songs = _.flatten(_.map(res, 'song')); - res = await updatePlaylistSongsLg({ id: localSelectedPlaylistId, entry: songs }); + res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylistSongsLg', + args: { id: localSelectedPlaylistId, entry: songs }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); @@ -363,12 +422,22 @@ export const GlobalContextMenu = () => { } } else if (misc.contextMenu.type === 'album') { for (let i = 0; i < multiSelect.selected.length; i += 1) { - promises.push(getAlbum({ id: multiSelect.selected[i].id })); + promises.push( + apiController({ + serverType: config.serverType, + endpoint: 'getAlbum', + args: { id: multiSelect.selected[i].id }, + }) + ); } res = await Promise.all(promises); songs = _.flatten(_.map(res, 'song')); - res = await updatePlaylistSongsLg({ id: localSelectedPlaylistId, entry: songs }); + res = await apiController({ + serverType: config.serverType, + endpoint: 'updatePlaylistSongsLg', + args: { id: localSelectedPlaylistId, entry: songs }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); @@ -394,7 +463,13 @@ export const GlobalContextMenu = () => { const res = []; for (let i = 0; i < multiSelect.selected.length; i += 1) { try { - res.push(await deletePlaylist({ id: multiSelect.selected[i].id })); + res.push( + await apiController({ + serverType: config.serverType, + endpoint: 'deletePlaylist', + args: { id: multiSelect.selected[i].id }, + }) + ); } catch (err) { notifyToast('error', err); } @@ -413,7 +488,11 @@ export const GlobalContextMenu = () => { const handleCreatePlaylist = async () => { try { - const res = await createPlaylist({ name: newPlaylistName }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'createPlaylist', + args: { name: newPlaylistName }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); @@ -453,7 +532,11 @@ export const GlobalContextMenu = () => { const ids = _.map(sortedEntries, 'id'); try { - const res = await batchStar({ ids, type: sortedEntries[0].type }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'batchStar', + args: { ids, type: sortedEntries[0].type }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); @@ -476,7 +559,11 @@ export const GlobalContextMenu = () => { try { // Infer the type from the first selected entry - const res = await batchUnstar({ ids, type: multiSelect.selected[0].type }); + const res = await apiController({ + serverType: config.serverType, + endpoint: 'batchUnstar', + args: { ids, type: multiSelect.selected[0].type }, + }); if (isFailedResponse(res)) { notifyToast('error', errorMessages(res)[0]); diff --git a/src/components/starred/StarredView.tsx b/src/components/starred/StarredView.tsx index 4733ab4..568390b 100644 --- a/src/components/starred/StarredView.tsx +++ b/src/components/starred/StarredView.tsx @@ -12,7 +12,6 @@ import { toggleRangeSelected, setRangeSelected, } from '../../redux/multiSelectSlice'; -import { getStarred, unstar } from '../../api/api'; import GenericPage from '../layout/GenericPage'; import GenericPageHeader from '../layout/GenericPageHeader'; import PageLoader from '../loader/PageLoader'; @@ -21,6 +20,8 @@ import GridViewType from '../viewtypes/GridViewType'; import { setStatus } from '../../redux/playerSlice'; import { StyledNavItem } from '../shared/styled'; import { setActive } from '../../redux/favoriteSlice'; +import { apiController } from '../../api/controller'; +import { Server } from '../../types'; const StarredView = () => { const history = useHistory(); @@ -40,8 +41,13 @@ const StarredView = () => { }, [folder]); const { isLoading, isError, data, error }: any = useQuery(['starred', musicFolder], () => - getStarred({ musicFolderId: musicFolder }) + apiController({ + serverType: config.serverType, + endpoint: 'getStarred', + args: config.serverType === Server.Subsonic ? { musicFolderId: musicFolder } : null, + }) ); + const filteredData = useSearchQuery( misc.searchQuery, favorite.active.tab === 'tracks' @@ -97,7 +103,11 @@ const StarredView = () => { }; const handleRowFavorite = async (rowData: any) => { - await unstar({ id: rowData.id, type: 'music' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'music' } : null, + }); dispatch(setStar({ id: [rowData.id], type: 'unstar' })); await queryClient.refetchQueries(['starred', musicFolder], { active: true, @@ -105,14 +115,22 @@ const StarredView = () => { }; const handleRowFavoriteAlbum = async (rowData: any) => { - await unstar({ id: rowData.id, type: 'album' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'album' } : null, + }); await queryClient.refetchQueries(['starred', musicFolder], { active: true, }); }; const handleRowFavoriteArtist = async (rowData: any) => { - await unstar({ id: rowData.id, type: 'artist' }); + await apiController({ + serverType: config.serverType, + endpoint: 'unstar', + args: config.serverType === Server.Subsonic ? { id: rowData.id, type: 'artist' } : null, + }); await queryClient.refetchQueries(['starred', musicFolder], { active: true, }); @@ -170,9 +188,8 @@ const StarredView = () => { /> } > - {isLoading ? ( - - ) : ( + {(isLoading || !data) && } + {data && ( <> {favorite.active.tab === 'tracks' && (