handle playback on new artist list

This commit is contained in:
jeffvli 2025-05-06 14:43:42 -07:00
parent b9611589ba
commit 4a3604b1a8
12 changed files with 121 additions and 31 deletions

View file

@ -700,6 +700,8 @@ export const JellyfinController: ControllerEndpoint = {
: undefined;
const artistIdsFilter = query.artistIds
? formatCommaDelimitedString(query.artistIds)
: query.albumArtistIds
? formatCommaDelimitedString(query.albumArtistIds)
: undefined;
const res = await jfApiClient(apiClientProps).getSongList({

View file

@ -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,

View file

@ -494,6 +494,7 @@ export interface SongListQuery extends BaseQuery<SongListSort> {
jellyfin?: Partial<z.infer<typeof jfType._parameters.songList>>;
navidrome?: Partial<z.infer<typeof ndType._parameters.songList>>;
};
albumArtistIds?: string[];
albumIds?: string[];
artistIds?: string[];
favorite?: boolean;

View file

@ -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"

View file

@ -16,11 +16,15 @@ interface AlbumArtistDetailHeaderProps {
export const AlbumArtistDetailHeader = forwardRef(
({ background }: AlbumArtistDetailHeaderProps, ref: Ref<HTMLDivElement>) => {
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,
});

View file

@ -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}

View file

@ -16,15 +16,21 @@ const AlbumArtistDetailRoute = () => {
const headerRef = useRef<HTMLDivElement>(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 <Spinner container />;
}
return (
<AnimatedPage key={`album-artist-detail-${albumArtistId}`}>
<AnimatedPage key={`album-artist-detail-${routeId}`}>
<NativeScrollArea
ref={scrollAreaRef}
pageHeaderProps={{

View file

@ -12,18 +12,22 @@ import { ListContext } from '/@/renderer/context/list-context';
const AlbumArtistDetailTopSongsListRoute = () => {
const tableRef = useRef<AgGridReactType | null>(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 (
<AnimatedPage>

View file

@ -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) {

View file

@ -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<SongListQuery>;
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<SongListQuery>;
queryClient: QueryClient;

View file

@ -150,6 +150,24 @@ export const AppRouter = () => {
errorElement={<RouteErrorBoundary />}
path={AppRoute.LIBRARY_ARTISTS}
/>
<Route path={AppRoute.LIBRARY_ARTISTS_DETAIL}>
<Route
index
element={<AlbumArtistDetailRoute />}
/>
<Route
element={<AlbumListRoute />}
path={AppRoute.LIBRARY_ARTISTS_DETAIL_DISCOGRAPHY}
/>
<Route
element={<SongListRoute />}
path={AppRoute.LIBRARY_ARTISTS_DETAIL_SONGS}
/>
<Route
element={<AlbumArtistDetailTopSongsListRoute />}
path={AppRoute.LIBRARY_ARTISTS_DETAIL_TOP_SONGS}
/>
</Route>
<Route
element={<DummyAlbumDetailRoute />}
errorElement={<RouteErrorBoundary />}

View file

@ -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',