diff --git a/src/renderer/api/jellyfin/jellyfin-controller.ts b/src/renderer/api/jellyfin/jellyfin-controller.ts
index ac136a63b..243bee5a1 100644
--- a/src/renderer/api/jellyfin/jellyfin-controller.ts
+++ b/src/renderer/api/jellyfin/jellyfin-controller.ts
@@ -218,24 +218,25 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('No userId found');
}
- const res = await jfApiClient(apiClientProps).getAlbumArtistDetail({
- params: {
- id: query.id,
- userId: apiClientProps.server?.userId,
- },
- query: {
- Fields: 'Genres, Overview',
- },
- });
-
- const similarArtistsRes = await jfApiClient(apiClientProps).getSimilarArtistList({
- params: {
- id: query.id,
- },
- query: {
- Limit: 10,
- },
- });
+ const [res, similarArtistsRes] = await Promise.all([
+ jfApiClient(apiClientProps).getAlbumArtistDetail({
+ params: {
+ id: query.id,
+ userId: apiClientProps.server?.userId,
+ },
+ query: {
+ Fields: 'Genres, Overview',
+ },
+ }),
+ jfApiClient(apiClientProps).getSimilarArtistList({
+ params: {
+ id: query.id,
+ },
+ query: {
+ Limit: 10,
+ },
+ }),
+ ]);
if (res.status !== 200 || similarArtistsRes.status !== 200) {
throw new Error('Failed to get album artist detail');
diff --git a/src/renderer/api/navidrome/navidrome-controller.ts b/src/renderer/api/navidrome/navidrome-controller.ts
index 833fa6536..80b82e73b 100644
--- a/src/renderer/api/navidrome/navidrome-controller.ts
+++ b/src/renderer/api/navidrome/navidrome-controller.ts
@@ -184,18 +184,19 @@ export const NavidromeController: InternalControllerEndpoint = {
getAlbumArtistDetail: async (args) => {
const { apiClientProps, query } = args;
- const res = await ndApiClient(apiClientProps).getAlbumArtistDetail({
- params: {
- id: query.id,
- },
- });
-
- const artistInfoRes = await ssApiClient(apiClientProps).getArtistInfo({
- query: {
- count: 10,
- id: query.id,
- },
- });
+ const [res, artistInfoRes] = await Promise.all([
+ ndApiClient(apiClientProps).getAlbumArtistDetail({
+ params: {
+ id: query.id,
+ },
+ }),
+ ssApiClient(apiClientProps).getArtistInfo({
+ query: {
+ count: 10,
+ id: query.id,
+ },
+ }),
+ ]);
if (res.status !== 200) {
throw new Error('Failed to get album artist detail');
diff --git a/src/renderer/api/subsonic/subsonic-controller.ts b/src/renderer/api/subsonic/subsonic-controller.ts
index edb6bb92f..7edfa4c6a 100644
--- a/src/renderer/api/subsonic/subsonic-controller.ts
+++ b/src/renderer/api/subsonic/subsonic-controller.ts
@@ -256,17 +256,18 @@ export const SubsonicController: InternalControllerEndpoint = {
getAlbumArtistDetail: async (args) => {
const { apiClientProps, query } = args;
- const artistInfoRes = await ssApiClient(apiClientProps).getArtistInfo({
- query: {
- id: query.id,
- },
- });
-
- const res = await ssApiClient(apiClientProps).getArtist({
- query: {
- id: query.id,
- },
- });
+ const [artistInfoRes, res] = await Promise.all([
+ ssApiClient(apiClientProps).getArtistInfo({
+ query: {
+ id: query.id,
+ },
+ }),
+ ssApiClient(apiClientProps).getArtist({
+ query: {
+ id: query.id,
+ },
+ }),
+ ]);
if (res.status !== 200) {
throw new Error('Failed to get album artist detail');
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 44e62b894..354cf4448 100644
--- a/src/renderer/features/artists/components/album-artist-detail-content.tsx
+++ b/src/renderer/features/artists/components/album-artist-detail-content.tsx
@@ -1,8 +1,14 @@
-import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
+import {
+ useQuery,
+ useQueryClient,
+ useSuspenseQuery,
+ UseSuspenseQueryResult,
+} from '@tanstack/react-query';
import { LayoutGroup, motion } from 'motion/react';
+import { Suspense } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { createSearchParams, generatePath, Link, useParams } from 'react-router';
+import { createSearchParams, generatePath, Link, useLocation, useParams } from 'react-router';
import styles from './album-artist-detail-content.module.css';
@@ -16,7 +22,6 @@ import { SONG_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-
import { ItemTableList } from '/@/renderer/components/item-list/item-table-list/item-table-list';
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { ItemControls } from '/@/renderer/components/item-list/types';
-import { albumQueries } from '/@/renderer/features/albums/api/album-api';
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
import { AlbumArtistGridCarousel } from '/@/renderer/features/artists/components/album-artist-grid-carousel';
import { useIsPlayerFetching, usePlayer } from '/@/renderer/features/player/context/player-context';
@@ -67,6 +72,7 @@ import {
Album,
AlbumArtist,
AlbumArtistDetailResponse,
+ AlbumListResponse,
AlbumListSort,
LibraryItem,
RelatedArtist,
@@ -214,7 +220,7 @@ interface AlbumArtistMetadataTopSongsProps {
routeId: string;
}
-const AlbumArtistMetadataTopSongs = ({
+const AlbumArtistMetadataTopSongsContent = ({
detailQuery,
routeId,
}: AlbumArtistMetadataTopSongsProps) => {
@@ -226,15 +232,22 @@ const AlbumArtistMetadataTopSongs = ({
const currentSong = usePlayerSong();
const player = usePlayer();
const serverId = useCurrentServerId();
+ const server = useCurrentServer();
- const topSongsQuery = useSuspenseQuery(
- artistsQueries.topSongs({
- query: { artist: detailQuery.data?.name || '', artistId: routeId },
+ const canStartQuery = server?.type === ServerType.JELLYFIN || !!detailQuery.data?.name;
+
+ const topSongsQuery = useQuery({
+ ...artistsQueries.topSongs({
+ query: {
+ artist: detailQuery.data?.name || '',
+ artistId: routeId,
+ },
serverId: serverId,
}),
- );
+ enabled: canStartQuery,
+ });
- const songs = useMemo(() => topSongsQuery?.data?.items || [], [topSongsQuery?.data?.items]);
+ const songs = useMemo(() => topSongsQuery.data?.items || [], [topSongsQuery.data?.items]);
const columns = useMemo(() => {
return tableConfig?.columns || [];
@@ -274,6 +287,10 @@ const AlbumArtistMetadataTopSongs = ({
};
}, [player]);
+ if (topSongsQuery.isLoading || !topSongsQuery.data) {
+ return null;
+ }
+
if (!topSongsQuery?.data?.items?.length) return null;
if (!tableConfig || columns.length === 0) {
@@ -404,6 +421,26 @@ const AlbumArtistMetadataTopSongs = ({
);
};
+const AlbumArtistMetadataTopSongs = ({
+ detailQuery,
+ routeId,
+}: AlbumArtistMetadataTopSongsProps) => {
+ const server = useCurrentServer();
+
+ const location = useLocation();
+ const artistName = location.state?.item?.name || detailQuery.data?.name;
+
+ const canStartQuery = server?.type === ServerType.JELLYFIN || !!artistName;
+
+ return (
+
+ {canStartQuery ? (
+
+ ) : null}
+
+ );
+};
+
interface AlbumArtistMetadataExternalLinksProps {
artistName?: string;
externalLinks: boolean;
@@ -543,7 +580,15 @@ const AlbumArtistMetadataSimilarArtists = ({
);
};
-export const AlbumArtistDetailContent = () => {
+interface AlbumArtistDetailContentProps {
+ albumsQuery: UseSuspenseQueryResult;
+ detailQuery: UseSuspenseQueryResult;
+}
+
+export const AlbumArtistDetailContent = ({
+ albumsQuery,
+ detailQuery,
+}: AlbumArtistDetailContentProps) => {
const { artistItems, artistRadioCount, externalLinks, lastFM, musicBrainz } =
useGeneralSettings();
const { albumArtistId, artistId } = useParams() as {
@@ -567,13 +612,6 @@ export const AlbumArtistDetailContent = () => {
return [enabled, order];
}, [artistItems]);
- const detailQuery = useSuspenseQuery(
- artistsQueries.albumArtistDetail({
- query: { id: routeId },
- serverId: server?.id,
- }),
- );
-
const artistDiscographyLink = useMemo(
() =>
`${generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY, {
@@ -662,7 +700,7 @@ export const AlbumArtistDetailContent = () => {
)}
-
+
{enabledItem.similarArtists && (
@@ -1020,10 +1058,13 @@ const releaseTypeToEnumMap: Record = {
spokenword: ArtistReleaseTypeItem.RELEASE_TYPE_SPOKENWORD,
};
-const ArtistAlbums = () => {
+interface ArtistAlbumsProps {
+ albumsQuery: UseSuspenseQueryResult;
+}
+
+const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
const { t } = useTranslation();
const { artistReleaseTypeItems } = useGeneralSettings();
- const serverId = useCurrentServerId();
const [searchTerm, setSearchTerm] = useState('');
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
const albumArtistDetailSort = useAppStore((state) => state.albumArtistDetailSort);
@@ -1038,19 +1079,6 @@ const ArtistAlbums = () => {
};
const routeId = (artistId || albumArtistId) as string;
- const albumsQuery = useSuspenseQuery(
- albumQueries.list({
- query: {
- artistIds: [routeId],
- limit: -1,
- sortBy: AlbumListSort.RELEASE_DATE,
- sortOrder: SortOrder.DESC,
- startIndex: 0,
- },
- serverId,
- }),
- );
-
const rows = useGridRows(LibraryItem.ALBUM, ItemListKey.ALBUM);
const controls = useDefaultItemListControls();
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 1a3ff8193..bb83e6c88 100644
--- a/src/renderer/features/artists/routes/album-artist-detail-route.tsx
+++ b/src/renderer/features/artists/routes/album-artist-detail-route.tsx
@@ -1,9 +1,10 @@
-import { useSuspenseQuery } from '@tanstack/react-query';
+import { useSuspenseQueries } from '@tanstack/react-query';
import { Suspense, useRef } from 'react';
import { useParams } from 'react-router';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { NativeScrollArea } from '/@/renderer/components/native-scroll-area/native-scroll-area';
+import { albumQueries } from '/@/renderer/features/albums/api/album-api';
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
import { AlbumArtistDetailContent } from '/@/renderer/features/artists/components/album-artist-detail-content';
import { AlbumArtistDetailHeader } from '/@/renderer/features/artists/components/album-artist-detail-header';
@@ -16,14 +17,15 @@ import { LibraryContainer } from '/@/renderer/features/shared/components/library
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useFastAverageColor, useWaitForColorCalculation } from '/@/renderer/hooks';
-import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
+import { useCurrentServer, useCurrentServerId, useGeneralSettings } from '/@/renderer/store';
import { Spinner } from '/@/shared/components/spinner/spinner';
-import { LibraryItem } from '/@/shared/types/domain-types';
+import { AlbumListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
const AlbumArtistDetailRouteContent = () => {
const scrollAreaRef = useRef(null);
const headerRef = useRef(null);
const server = useCurrentServer();
+ const serverId = useCurrentServerId();
const { artistBackground, artistBackgroundBlur } = useGeneralSettings();
const { albumArtistId, artistId } = useParams() as {
@@ -33,9 +35,21 @@ const AlbumArtistDetailRouteContent = () => {
const routeId = (artistId || albumArtistId) as string;
- const detailQuery = useSuspenseQuery(
- artistsQueries.albumArtistDetail({ query: { id: routeId }, serverId: server?.id }),
- );
+ const [detailQuery, albumsQuery] = useSuspenseQueries({
+ queries: [
+ artistsQueries.albumArtistDetail({ query: { id: routeId }, serverId: server?.id }),
+ albumQueries.list({
+ query: {
+ artistIds: [routeId],
+ limit: -1,
+ sortBy: AlbumListSort.RELEASE_DATE,
+ sortOrder: SortOrder.DESC,
+ startIndex: 0,
+ },
+ serverId,
+ }),
+ ],
+ });
const imageUrl = useItemImageUrl({
id: detailQuery.data?.imageId || undefined,
@@ -106,7 +120,7 @@ const AlbumArtistDetailRouteContent = () => {
)}
} />
-
+