Add top song list for jellyfin
This commit is contained in:
parent
f08538cbfb
commit
c0a703be7a
7 changed files with 48 additions and 12 deletions
|
@ -115,7 +115,7 @@ const endpoints: ApiController = {
|
||||||
getPlaylistSongList: jellyfinApi.getPlaylistSongList,
|
getPlaylistSongList: jellyfinApi.getPlaylistSongList,
|
||||||
getSongDetail: undefined,
|
getSongDetail: undefined,
|
||||||
getSongList: jellyfinApi.getSongList,
|
getSongList: jellyfinApi.getSongList,
|
||||||
getTopSongs: undefined,
|
getTopSongs: jellyfinApi.getTopSongList,
|
||||||
getUserList: undefined,
|
getUserList: undefined,
|
||||||
removeFromPlaylist: jellyfinApi.removeFromPlaylist,
|
removeFromPlaylist: jellyfinApi.removeFromPlaylist,
|
||||||
scrobble: jellyfinApi.scrobble,
|
scrobble: jellyfinApi.scrobble,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import ky from 'ky';
|
import ky from 'ky';
|
||||||
import { nanoid } from 'nanoid/non-secure';
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
import type {
|
import {
|
||||||
JFAddToPlaylist,
|
JFAddToPlaylist,
|
||||||
JFAddToPlaylistParams,
|
JFAddToPlaylistParams,
|
||||||
JFAlbum,
|
JFAlbum,
|
||||||
|
@ -35,8 +35,9 @@ import type {
|
||||||
JFSongList,
|
JFSongList,
|
||||||
JFSongListParams,
|
JFSongListParams,
|
||||||
JFSongListResponse,
|
JFSongListResponse,
|
||||||
|
JFSongListSort,
|
||||||
|
JFCollectionType,
|
||||||
} from '/@/renderer/api/jellyfin.types';
|
} from '/@/renderer/api/jellyfin.types';
|
||||||
import { JFCollectionType } from '/@/renderer/api/jellyfin.types';
|
|
||||||
import {
|
import {
|
||||||
Album,
|
Album,
|
||||||
AlbumArtist,
|
AlbumArtist,
|
||||||
|
@ -72,6 +73,8 @@ import {
|
||||||
AddToPlaylistArgs,
|
AddToPlaylistArgs,
|
||||||
ScrobbleArgs,
|
ScrobbleArgs,
|
||||||
RawScrobbleResponse,
|
RawScrobbleResponse,
|
||||||
|
TopSongListArgs,
|
||||||
|
SortOrder,
|
||||||
} from '/@/renderer/api/types';
|
} from '/@/renderer/api/types';
|
||||||
import { useAuthStore } from '/@/renderer/store';
|
import { useAuthStore } from '/@/renderer/store';
|
||||||
import { ServerListItem, ServerType } from '/@/renderer/types';
|
import { ServerListItem, ServerType } from '/@/renderer/types';
|
||||||
|
@ -326,6 +329,36 @@ const getAlbumList = async (args: AlbumListArgs): Promise<JFAlbumList> => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTopSongList = async (args: TopSongListArgs): Promise<any> => {
|
||||||
|
const { signal, server, query } = args;
|
||||||
|
|
||||||
|
const searchParams: any = {
|
||||||
|
artistIds: query.artistId,
|
||||||
|
fields: 'Genres, DateCreated, MediaSources, ParentId',
|
||||||
|
includeItemTypes: 'Audio',
|
||||||
|
limit: query.limit,
|
||||||
|
recursive: true,
|
||||||
|
sortBy: JFSongListSort.COMMUNITY_RATING,
|
||||||
|
sortOrder: SortOrder.DESC,
|
||||||
|
userId: server?.userId || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await api
|
||||||
|
.get(`users/${server?.userId}/items`, {
|
||||||
|
headers: { 'X-MediaBrowser-Token': server?.credential },
|
||||||
|
prefixUrl: server?.url,
|
||||||
|
searchParams: parseSearchParams(searchParams),
|
||||||
|
signal,
|
||||||
|
})
|
||||||
|
.json<any>();
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: data.Items,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: data.TotalRecordCount,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const getSongList = async (args: SongListArgs): Promise<JFSongList> => {
|
const getSongList = async (args: SongListArgs): Promise<JFSongList> => {
|
||||||
const { query, server, signal } = args;
|
const { query, server, signal } = args;
|
||||||
|
|
||||||
|
@ -1007,6 +1040,7 @@ export const jellyfinApi = {
|
||||||
getPlaylistList,
|
getPlaylistList,
|
||||||
getPlaylistSongList,
|
getPlaylistSongList,
|
||||||
getSongList,
|
getSongList,
|
||||||
|
getTopSongList,
|
||||||
removeFromPlaylist,
|
removeFromPlaylist,
|
||||||
scrobble,
|
scrobble,
|
||||||
updatePlaylist,
|
updatePlaylist,
|
||||||
|
|
|
@ -567,6 +567,7 @@ export enum JFSongListSort {
|
||||||
ALBUM = 'Album,SortName',
|
ALBUM = 'Album,SortName',
|
||||||
ALBUM_ARTIST = 'AlbumArtist,Album,SortName',
|
ALBUM_ARTIST = 'AlbumArtist,Album,SortName',
|
||||||
ARTIST = 'Artist,Album,SortName',
|
ARTIST = 'Artist,Album,SortName',
|
||||||
|
COMMUNITY_RATING = 'CommunityRating,SortName',
|
||||||
DURATION = 'Runtime,AlbumArtist,Album,SortName',
|
DURATION = 'Runtime,AlbumArtist,Album,SortName',
|
||||||
NAME = 'Name,SortName',
|
NAME = 'Name,SortName',
|
||||||
PLAY_COUNT = 'PlayCount,SortName',
|
PLAY_COUNT = 'PlayCount,SortName',
|
||||||
|
|
|
@ -99,6 +99,7 @@ const topSongList = (data: RawTopSongListResponse | undefined, server: ServerLis
|
||||||
|
|
||||||
switch (server?.type) {
|
switch (server?.type) {
|
||||||
case 'jellyfin':
|
case 'jellyfin':
|
||||||
|
songs = data?.items.map((item) => jfNormalize.song(item as JFSong, server, ''));
|
||||||
break;
|
break;
|
||||||
case 'navidrome':
|
case 'navidrome':
|
||||||
songs = data?.items?.map((item) => ssNormalize.song(item as SSSong, server, ''));
|
songs = data?.items?.map((item) => ssNormalize.song(item as SSSong, server, ''));
|
||||||
|
|
|
@ -1009,12 +1009,13 @@ export const userListSortMap: UserListSortMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Top Songs List
|
// Top Songs List
|
||||||
export type RawTopSongListResponse = SSTopSongList | undefined;
|
export type RawTopSongListResponse = SSTopSongList | JFSongList | undefined;
|
||||||
|
|
||||||
export type TopSongListResponse = BasePaginatedResponse<Song[]>;
|
export type TopSongListResponse = BasePaginatedResponse<Song[]>;
|
||||||
|
|
||||||
export type TopSongListQuery = {
|
export type TopSongListQuery = {
|
||||||
artist: string;
|
artist: string;
|
||||||
|
artistId: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@ export const AlbumArtistDetailContent = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const topSongsQuery = useTopSongsList(
|
const topSongsQuery = useTopSongsList(
|
||||||
{ artist: detailQuery?.data?.name || '' },
|
{ artist: detailQuery?.data?.name || '', artistId: albumArtistId },
|
||||||
{ enabled: server?.type !== ServerType.JELLYFIN && !!detailQuery?.data?.name },
|
{ enabled: !!detailQuery?.data?.name },
|
||||||
);
|
);
|
||||||
|
|
||||||
const topSongsColumnDefs: ColDef[] = useMemo(
|
const topSongsColumnDefs: ColDef[] = useMemo(
|
||||||
|
@ -270,11 +270,13 @@ export const AlbumArtistDetailContent = () => {
|
||||||
ARTIST_CONTEXT_MENU_ITEMS,
|
ARTIST_CONTEXT_MENU_ITEMS,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('topSongsQuery?.data :>> ', topSongsQuery?.data);
|
||||||
|
|
||||||
const topSongs = topSongsQuery?.data?.items?.slice(0, 10);
|
const topSongs = topSongsQuery?.data?.items?.slice(0, 10);
|
||||||
|
|
||||||
const showBiography =
|
const showBiography =
|
||||||
detailQuery?.data?.biography !== undefined && detailQuery?.data?.biography !== null;
|
detailQuery?.data?.biography !== undefined && detailQuery?.data?.biography !== null;
|
||||||
const showTopSongs = server?.type !== ServerType.JELLYFIN && topSongsQuery?.data?.items?.length;
|
const showTopSongs = topSongsQuery?.data?.items?.length;
|
||||||
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
|
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
|
|
|
@ -6,19 +6,16 @@ import { AlbumArtistDetailTopSongsListHeader } from '/@/renderer/features/artist
|
||||||
import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query';
|
import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query';
|
||||||
import { useTopSongsList } from '/@/renderer/features/artists/queries/top-songs-list-query';
|
import { useTopSongsList } from '/@/renderer/features/artists/queries/top-songs-list-query';
|
||||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
|
||||||
import { ServerType } from '/@/renderer/types';
|
|
||||||
|
|
||||||
const AlbumArtistDetailTopSongsListRoute = () => {
|
const AlbumArtistDetailTopSongsListRoute = () => {
|
||||||
const tableRef = useRef<AgGridReactType | null>(null);
|
const tableRef = useRef<AgGridReactType | null>(null);
|
||||||
const { albumArtistId } = useParams() as { albumArtistId: string };
|
const { albumArtistId } = useParams() as { albumArtistId: string };
|
||||||
const server = useCurrentServer();
|
|
||||||
|
|
||||||
const detailQuery = useAlbumArtistDetail({ id: albumArtistId });
|
const detailQuery = useAlbumArtistDetail({ id: albumArtistId });
|
||||||
|
|
||||||
const topSongsQuery = useTopSongsList(
|
const topSongsQuery = useTopSongsList(
|
||||||
{ artist: detailQuery?.data?.name || '' },
|
{ artist: detailQuery?.data?.name || '', artistId: albumArtistId },
|
||||||
{ enabled: server?.type !== ServerType.JELLYFIN && !!detailQuery?.data?.name },
|
{ enabled: !!detailQuery?.data?.name },
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemCount = topSongsQuery?.data?.items?.length || 0;
|
const itemCount = topSongsQuery?.data?.items?.length || 0;
|
||||||
|
|
Reference in a new issue