From c0a703be7ae756534aff33e9a9f395c8713185f8 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Mon, 27 Feb 2023 12:17:22 -0800 Subject: [PATCH] Add top song list for jellyfin --- src/renderer/api/controller.ts | 2 +- src/renderer/api/jellyfin.api.ts | 38 ++++++++++++++++++- src/renderer/api/jellyfin.types.ts | 1 + src/renderer/api/normalize.ts | 1 + src/renderer/api/types.ts | 3 +- .../album-artist-detail-content.tsx | 8 ++-- ...bum-artist-detail-top-songs-list-route.tsx | 7 +--- 7 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/renderer/api/controller.ts b/src/renderer/api/controller.ts index ff823a7d..3c43cf37 100644 --- a/src/renderer/api/controller.ts +++ b/src/renderer/api/controller.ts @@ -115,7 +115,7 @@ const endpoints: ApiController = { getPlaylistSongList: jellyfinApi.getPlaylistSongList, getSongDetail: undefined, getSongList: jellyfinApi.getSongList, - getTopSongs: undefined, + getTopSongs: jellyfinApi.getTopSongList, getUserList: undefined, removeFromPlaylist: jellyfinApi.removeFromPlaylist, scrobble: jellyfinApi.scrobble, diff --git a/src/renderer/api/jellyfin.api.ts b/src/renderer/api/jellyfin.api.ts index 2efb3d9d..d4ae8fc3 100644 --- a/src/renderer/api/jellyfin.api.ts +++ b/src/renderer/api/jellyfin.api.ts @@ -1,6 +1,6 @@ import ky from 'ky'; import { nanoid } from 'nanoid/non-secure'; -import type { +import { JFAddToPlaylist, JFAddToPlaylistParams, JFAlbum, @@ -35,8 +35,9 @@ import type { JFSongList, JFSongListParams, JFSongListResponse, + JFSongListSort, + JFCollectionType, } from '/@/renderer/api/jellyfin.types'; -import { JFCollectionType } from '/@/renderer/api/jellyfin.types'; import { Album, AlbumArtist, @@ -72,6 +73,8 @@ import { AddToPlaylistArgs, ScrobbleArgs, RawScrobbleResponse, + TopSongListArgs, + SortOrder, } from '/@/renderer/api/types'; import { useAuthStore } from '/@/renderer/store'; import { ServerListItem, ServerType } from '/@/renderer/types'; @@ -326,6 +329,36 @@ const getAlbumList = async (args: AlbumListArgs): Promise => { }; }; +const getTopSongList = async (args: TopSongListArgs): Promise => { + 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(); + + return { + items: data.Items, + startIndex: 0, + totalRecordCount: data.TotalRecordCount, + }; +}; + const getSongList = async (args: SongListArgs): Promise => { const { query, server, signal } = args; @@ -1007,6 +1040,7 @@ export const jellyfinApi = { getPlaylistList, getPlaylistSongList, getSongList, + getTopSongList, removeFromPlaylist, scrobble, updatePlaylist, diff --git a/src/renderer/api/jellyfin.types.ts b/src/renderer/api/jellyfin.types.ts index 4f9bc9f6..e58d13f0 100644 --- a/src/renderer/api/jellyfin.types.ts +++ b/src/renderer/api/jellyfin.types.ts @@ -567,6 +567,7 @@ export enum JFSongListSort { ALBUM = 'Album,SortName', ALBUM_ARTIST = 'AlbumArtist,Album,SortName', ARTIST = 'Artist,Album,SortName', + COMMUNITY_RATING = 'CommunityRating,SortName', DURATION = 'Runtime,AlbumArtist,Album,SortName', NAME = 'Name,SortName', PLAY_COUNT = 'PlayCount,SortName', diff --git a/src/renderer/api/normalize.ts b/src/renderer/api/normalize.ts index 38528a47..2ce5a0ef 100644 --- a/src/renderer/api/normalize.ts +++ b/src/renderer/api/normalize.ts @@ -99,6 +99,7 @@ const topSongList = (data: RawTopSongListResponse | undefined, server: ServerLis switch (server?.type) { case 'jellyfin': + songs = data?.items.map((item) => jfNormalize.song(item as JFSong, server, '')); break; case 'navidrome': songs = data?.items?.map((item) => ssNormalize.song(item as SSSong, server, '')); diff --git a/src/renderer/api/types.ts b/src/renderer/api/types.ts index 7a0b5fc7..2f1357ca 100644 --- a/src/renderer/api/types.ts +++ b/src/renderer/api/types.ts @@ -1009,12 +1009,13 @@ export const userListSortMap: UserListSortMap = { }; // Top Songs List -export type RawTopSongListResponse = SSTopSongList | undefined; +export type RawTopSongListResponse = SSTopSongList | JFSongList | undefined; export type TopSongListResponse = BasePaginatedResponse; export type TopSongListQuery = { artist: string; + artistId: string; limit?: number; }; 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 096ea832..ad0c4222 100644 --- a/src/renderer/features/artists/components/album-artist-detail-content.tsx +++ b/src/renderer/features/artists/components/album-artist-detail-content.tsx @@ -105,8 +105,8 @@ export const AlbumArtistDetailContent = () => { }); const topSongsQuery = useTopSongsList( - { artist: detailQuery?.data?.name || '' }, - { enabled: server?.type !== ServerType.JELLYFIN && !!detailQuery?.data?.name }, + { artist: detailQuery?.data?.name || '', artistId: albumArtistId }, + { enabled: !!detailQuery?.data?.name }, ); const topSongsColumnDefs: ColDef[] = useMemo( @@ -270,11 +270,13 @@ export const AlbumArtistDetailContent = () => { ARTIST_CONTEXT_MENU_ITEMS, ); + console.log('topSongsQuery?.data :>> ', topSongsQuery?.data); + const topSongs = topSongsQuery?.data?.items?.slice(0, 10); const showBiography = 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 isLoading = diff --git a/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx index a427f00c..bf22e974 100644 --- a/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx +++ b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx @@ -6,19 +6,16 @@ import { AlbumArtistDetailTopSongsListHeader } from '/@/renderer/features/artist import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query'; import { useTopSongsList } from '/@/renderer/features/artists/queries/top-songs-list-query'; import { AnimatedPage } from '/@/renderer/features/shared'; -import { useCurrentServer } from '/@/renderer/store'; -import { ServerType } from '/@/renderer/types'; const AlbumArtistDetailTopSongsListRoute = () => { const tableRef = useRef(null); const { albumArtistId } = useParams() as { albumArtistId: string }; - const server = useCurrentServer(); const detailQuery = useAlbumArtistDetail({ id: albumArtistId }); const topSongsQuery = useTopSongsList( - { artist: detailQuery?.data?.name || '' }, - { enabled: server?.type !== ServerType.JELLYFIN && !!detailQuery?.data?.name }, + { artist: detailQuery?.data?.name || '', artistId: albumArtistId }, + { enabled: !!detailQuery?.data?.name }, ); const itemCount = topSongsQuery?.data?.items?.length || 0;