diff --git a/src/renderer/api/jellyfin/jellyfin-controller.ts b/src/renderer/api/jellyfin/jellyfin-controller.ts index 1dae79ee..e6ff74e5 100644 --- a/src/renderer/api/jellyfin/jellyfin-controller.ts +++ b/src/renderer/api/jellyfin/jellyfin-controller.ts @@ -700,7 +700,9 @@ export const JellyfinController: ControllerEndpoint = { : undefined; const artistIdsFilter = query.artistIds ? formatCommaDelimitedString(query.artistIds) - : undefined; + : query.albumArtistIds + ? formatCommaDelimitedString(query.albumArtistIds) + : undefined; const res = await jfApiClient(apiClientProps).getSongList({ params: { diff --git a/src/renderer/api/navidrome/navidrome-controller.ts b/src/renderer/api/navidrome/navidrome-controller.ts index 59f869d3..4c0c0443 100644 --- a/src/renderer/api/navidrome/navidrome-controller.ts +++ b/src/renderer/api/navidrome/navidrome-controller.ts @@ -548,8 +548,9 @@ export const NavidromeController: ControllerEndpoint = { _order: sortOrderMap.navidrome[query.sortOrder], _sort: songListSortMap.navidrome[query.sortBy], _start: query.startIndex, - album_artist_id: query.artistIds, + album_artist_id: query.albumArtistIds, album_id: query.albumIds, + artist_id: query.artistIds, genre_id: query.genreIds, starred: query.favorite, title: query.searchTerm, diff --git a/src/renderer/api/types.ts b/src/renderer/api/types.ts index a1a9c432..a73b8834 100644 --- a/src/renderer/api/types.ts +++ b/src/renderer/api/types.ts @@ -494,6 +494,7 @@ export interface SongListQuery extends BaseQuery { jellyfin?: Partial>; navidrome?: Partial>; }; + albumArtistIds?: string[]; albumIds?: string[]; artistIds?: string[]; favorite?: boolean; diff --git a/src/renderer/features/artists/components/album-artist-detail-content.tsx b/src/renderer/features/artists/components/album-artist-detail-content.tsx index 3f4f2c03..523a3e2e 100644 --- a/src/renderer/features/artists/components/album-artist-detail-content.tsx +++ b/src/renderer/features/artists/components/album-artist-detail-content.tsx @@ -66,7 +66,11 @@ interface AlbumArtistDetailContentProps { export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailContentProps) => { const { t } = useTranslation(); const { artistItems, externalLinks } = useGeneralSettings(); - const { albumArtistId } = useParams() as { albumArtistId: string }; + const { albumArtistId, artistId } = useParams() as { + albumArtistId?: string; + artistId?: string; + }; + const routeId = (artistId || albumArtistId) as string; const cq = useContainerQuery(); const handlePlayQueueAdd = usePlayQueueAdd(); const server = useCurrentServer(); @@ -85,24 +89,24 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten }, [artistItems]); const detailQuery = useAlbumArtistDetail({ - query: { id: albumArtistId }, + query: { id: routeId }, serverId: server?.id, }); const artistDiscographyLink = `${generatePath( AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY, { - albumArtistId, + albumArtistId: routeId, }, )}?${createSearchParams({ - artistId: albumArtistId, + artistId: routeId, artistName: detailQuery?.data?.name || '', })}`; const artistSongsLink = `${generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_SONGS, { - albumArtistId, + albumArtistId: routeId, })}?${createSearchParams({ - artistId: albumArtistId, + artistId: routeId, artistName: detailQuery?.data?.name || '', })}`; @@ -111,7 +115,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten enabled: enabledItem.recentAlbums, }, query: { - artistIds: [albumArtistId], + artistIds: [routeId], limit: 15, sortBy: AlbumListSort.RELEASE_DATE, sortOrder: SortOrder.DESC, @@ -125,7 +129,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten enabled: enabledItem.compilations && server?.type !== ServerType.SUBSONIC, }, query: { - artistIds: [albumArtistId], + artistIds: [routeId], compilation: true, limit: 15, sortBy: AlbumListSort.RELEASE_DATE, @@ -141,7 +145,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten }, query: { artist: detailQuery?.data?.name || '', - artistId: albumArtistId, + artistId: routeId, }, serverId: server?.id, }); @@ -292,8 +296,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten const handlePlay = async (playType?: Play) => { handlePlayQueueAdd?.({ byItemType: { - id: [albumArtistId], - type: LibraryItem.ALBUM_ARTIST, + id: [routeId], + type: albumArtistId ? LibraryItem.ALBUM : LibraryItem.ALBUM_ARTIST, }, playType: playType || playButtonBehavior, }); @@ -528,7 +532,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten to={generatePath( AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_TOP_SONGS, { - albumArtistId, + albumArtistId: routeId, }, )} variant="subtle" diff --git a/src/renderer/features/artists/components/album-artist-detail-header.tsx b/src/renderer/features/artists/components/album-artist-detail-header.tsx index 81af884c..647eb800 100644 --- a/src/renderer/features/artists/components/album-artist-detail-header.tsx +++ b/src/renderer/features/artists/components/album-artist-detail-header.tsx @@ -16,11 +16,15 @@ interface AlbumArtistDetailHeaderProps { export const AlbumArtistDetailHeader = forwardRef( ({ background }: AlbumArtistDetailHeaderProps, ref: Ref) => { - const { albumArtistId } = useParams() as { albumArtistId: string }; + const { albumArtistId, artistId } = useParams() as { + albumArtistId?: string; + artistId?: string; + }; + const routeId = (artistId || albumArtistId) as string; const server = useCurrentServer(); const { t } = useTranslation(); const detailQuery = useAlbumArtistDetail({ - query: { id: albumArtistId }, + query: { id: routeId }, serverId: server?.id, }); diff --git a/src/renderer/features/artists/components/artist-list-grid-view.tsx b/src/renderer/features/artists/components/artist-list-grid-view.tsx index ba8d6ddd..9734da73 100644 --- a/src/renderer/features/artists/components/artist-list-grid-view.tsx +++ b/src/renderer/features/artists/components/artist-list-grid-view.tsx @@ -156,12 +156,12 @@ export const ArtistListGridView = ({ itemCount, gridRef }: ArtistListGridViewPro itemCount={itemCount || 0} itemGap={grid?.itemGap ?? 10} itemSize={grid?.itemSize || 200} - itemType={LibraryItem.ALBUM_ARTIST} + itemType={LibraryItem.ARTIST} loading={itemCount === undefined || itemCount === null} minimumBatchSize={40} route={{ - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], + route: AppRoute.LIBRARY_ARTISTS_DETAIL, + slugs: [{ idProperty: 'id', slugProperty: 'artistId' }], }} width={width} onScroll={handleGridScroll} diff --git a/src/renderer/features/artists/routes/album-artist-detail-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-route.tsx index 7ec212fa..3f385dde 100644 --- a/src/renderer/features/artists/routes/album-artist-detail-route.tsx +++ b/src/renderer/features/artists/routes/album-artist-detail-route.tsx @@ -16,15 +16,21 @@ const AlbumArtistDetailRoute = () => { const headerRef = useRef(null); const server = useCurrentServer(); - const { albumArtistId } = useParams() as { albumArtistId: string }; + const { albumArtistId, artistId } = useParams() as { + albumArtistId?: string; + artistId?: string; + }; + + const routeId = (artistId || albumArtistId) as string; + const handlePlayQueueAdd = usePlayQueueAdd(); const playButtonBehavior = usePlayButtonBehavior(); const detailQuery = useAlbumArtistDetail({ - query: { id: albumArtistId }, + query: { id: routeId }, serverId: server?.id, }); const { color: background, colorId } = useFastAverageColor({ - id: albumArtistId, + id: routeId, src: detailQuery.data?.imageUrl, srcLoaded: !detailQuery.isLoading, }); @@ -32,19 +38,19 @@ const AlbumArtistDetailRoute = () => { const handlePlay = () => { handlePlayQueueAdd?.({ byItemType: { - id: [albumArtistId], + id: [routeId], type: LibraryItem.ALBUM_ARTIST, }, playType: playButtonBehavior, }); }; - if (!background || colorId !== albumArtistId) { + if (!background || colorId !== routeId) { return ; } return ( - + { const tableRef = useRef(null); - const { albumArtistId } = useParams() as { albumArtistId: string }; + const { albumArtistId, artistId } = useParams() as { + albumArtistId?: string; + artistId?: string; + }; + const routeId = (artistId || albumArtistId) as string; const server = useCurrentServer(); const pageKey = LibraryItem.SONG; const detailQuery = useAlbumArtistDetail({ - query: { id: albumArtistId }, + query: { id: routeId }, serverId: server?.id, }); const topSongsQuery = useTopSongsList({ options: { enabled: !!detailQuery?.data?.name }, - query: { artist: detailQuery?.data?.name || '', artistId: albumArtistId }, + query: { artist: detailQuery?.data?.name || '', artistId: routeId }, serverId: server?.id, }); @@ -31,10 +35,10 @@ const AlbumArtistDetailTopSongsListRoute = () => { const providerValue = useMemo(() => { return { - id: albumArtistId, + id: routeId, pageKey, }; - }, [albumArtistId, pageKey]); + }, [routeId, pageKey]); return ( 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 f1fc53ed..57e86681 100644 --- a/src/renderer/features/player/hooks/use-handle-playqueue-add.ts +++ b/src/renderer/features/player/hooks/use-handle-playqueue-add.ts @@ -20,6 +20,7 @@ import { getAlbumArtistSongsById, getSongsByQuery, getGenreSongsById, + getArtistSongsById, } from '/@/renderer/features/player/utils'; import { queryKeys } from '/@/renderer/api/query-keys'; import { useTranslation } from 'react-i18next'; @@ -75,6 +76,8 @@ export const useHandlePlayQueueAdd = () => { let songs: QueueSong[] | null = null; let initialSongIndex = 0; + console.log('options :>> ', options); + if (byItemType) { let songList: SongListResponse | undefined; const { type: itemType, id } = byItemType; @@ -119,6 +122,13 @@ export const useHandlePlayQueueAdd = () => { queryClient, server, }); + } else if (itemType === LibraryItem.ARTIST) { + songList = await getArtistSongsById({ + id, + query, + queryClient, + server, + }); } else if (itemType === LibraryItem.GENRE) { songList = await getGenreSongsById({ id, query, queryClient, server }); } else if (itemType === LibraryItem.SONG) { diff --git a/src/renderer/features/player/utils.ts b/src/renderer/features/player/utils.ts index 55b47fe4..6fa6a312 100644 --- a/src/renderer/features/player/utils.ts +++ b/src/renderer/features/player/utils.ts @@ -146,7 +146,7 @@ export const getAlbumArtistSongsById = async (args: { const { id, queryClient, server, query } = args; const queryFilter: SongListQuery = { - artistIds: id || [], + albumArtistIds: id || [], sortBy: SongListSort.ALBUM_ARTIST, sortOrder: SortOrder.ASC, startIndex: 0, @@ -174,6 +174,43 @@ export const getAlbumArtistSongsById = async (args: { return res; }; +export const getArtistSongsById = async (args: { + id: string[]; + query?: Partial; + queryClient: QueryClient; + server: ServerListItem; +}) => { + const { id, queryClient, server, query } = args; + + const queryFilter: SongListQuery = { + artistIds: id, + sortBy: SongListSort.ALBUM, + sortOrder: SortOrder.ASC, + startIndex: 0, + ...query, + }; + + 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 getSongsByQuery = async (args: { query?: Partial; queryClient: QueryClient; diff --git a/src/renderer/router/app-router.tsx b/src/renderer/router/app-router.tsx index 2188b26c..cc03d915 100644 --- a/src/renderer/router/app-router.tsx +++ b/src/renderer/router/app-router.tsx @@ -150,6 +150,24 @@ export const AppRouter = () => { errorElement={} path={AppRoute.LIBRARY_ARTISTS} /> + + } + /> + } + path={AppRoute.LIBRARY_ARTISTS_DETAIL_DISCOGRAPHY} + /> + } + path={AppRoute.LIBRARY_ARTISTS_DETAIL_SONGS} + /> + } + path={AppRoute.LIBRARY_ARTISTS_DETAIL_TOP_SONGS} + /> + } errorElement={} diff --git a/src/renderer/router/routes.ts b/src/renderer/router/routes.ts index 1155d252..76b3fc5f 100644 --- a/src/renderer/router/routes.ts +++ b/src/renderer/router/routes.ts @@ -12,6 +12,9 @@ export enum AppRoute { LIBRARY_ALBUM_ARTISTS_DETAIL_TOP_SONGS = '/library/album-artists/:albumArtistId/top-songs', LIBRARY_ARTISTS = '/library/artists', LIBRARY_ARTISTS_DETAIL = '/library/artists/:artistId', + LIBRARY_ARTISTS_DETAIL_DISCOGRAPHY = '/library/artists/:artistId/discography', + LIBRARY_ARTISTS_DETAIL_SONGS = '/library/artists/:artistId/songs', + LIBRARY_ARTISTS_DETAIL_TOP_SONGS = '/library/artists/:artistId/top-songs', LIBRARY_FOLDERS = '/library/folders', LIBRARY_GENRES = '/library/genres', LIBRARY_GENRES_ALBUMS = '/library/genres/:genreId/albums',