add personal/community toggle for artist top songs (#1372)

This commit is contained in:
jeffvli
2026-02-03 23:58:13 -08:00
parent 2d963a9d23
commit f56a836ffd
8 changed files with 340 additions and 174 deletions
@@ -1281,17 +1281,26 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('No userId found');
}
const type = query.type === 'personal' ? 'personal' : 'community';
const res = await jfApiClient(apiClientProps).getTopSongsList({
params: {
userId: apiClientProps.server?.userId,
},
query: {
ArtistIds: query.artistId,
Fields: 'Genres, DateCreated, MediaSources, ParentId, SortName',
Fields:
type === 'personal'
? 'Genres, DateCreated, MediaSources, ParentId, SortName, UserData'
: 'Genres, DateCreated, MediaSources, ParentId, SortName',
IncludeItemTypes: 'Audio',
Limit: query.limit,
Recursive: true,
SortBy: 'CommunityRating,SortName',
SortBy:
type === 'personal'
? JFSongListSort.PLAY_COUNT
: JFSongListSort.COMMUNITY_RATING,
SortOrder: 'Descending',
UserId: apiClientProps.server?.userId,
},
@@ -1301,15 +1310,31 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get top song list');
}
return {
items: res.body.Items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
const items = res.body.Items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
if (type === 'personal') {
const sorted = orderBy(
items,
['playCount', 'albumId', 'trackNumber'],
['desc', 'asc', 'asc'],
);
return {
items: sorted,
startIndex: 0,
totalRecordCount: res.body.TotalRecordCount,
};
}
return {
items,
startIndex: 0,
totalRecordCount: res.body.TotalRecordCount,
};
@@ -1,4 +1,5 @@
import { set } from 'idb-keyval';
import orderBy from 'lodash/orderBy';
import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
@@ -17,7 +18,9 @@ import {
PlaylistSongListArgs,
PlaylistSongListResponse,
ServerListItemWithCredential,
SongListSort,
songListSortMap,
SortOrder,
sortOrderMap,
tagListSortMap,
userListSortMap,
@@ -807,7 +810,59 @@ export const NavidromeController: InternalControllerEndpoint = {
tags,
};
},
getTopSongs: SubsonicController.getTopSongs,
getTopSongs: async (args) => {
const { apiClientProps, query } = args;
const type = query.type === 'personal' ? 'personal' : 'community';
if (type === 'community') {
const res = await ssApiClient(apiClientProps).getTopSongsList({
query: {
artist: query.artist,
count: query.limit,
},
});
if (res.status !== 200) {
throw new Error('Failed to get top songs');
}
return {
items: (res.body.topSongs?.song || []).map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: 0,
totalRecordCount: res.body.topSongs?.song?.length || 0,
};
}
const res = await NavidromeController.getSongList({
apiClientProps,
query: {
artistIds: [query.artistId],
sortBy: SongListSort.PLAY_COUNT,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
});
const songsWithPlayCount = orderBy(
res.items.filter((song) => song.playCount > 0),
['playCount', 'albumId', 'trackNumber'],
['desc', 'asc', 'asc'],
);
return {
items: songsWithPlayCount,
startIndex: 0,
totalRecordCount: res.totalRecordCount,
};
},
getUserInfo: SubsonicController.getUserInfo,
getUserList: async (args) => {
const { apiClientProps, query } = args;
@@ -1794,29 +1794,54 @@ export const SubsonicController: InternalControllerEndpoint = {
getTopSongs: async (args) => {
const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getTopSongsList({
query: {
artist: query.artist,
count: query.limit,
},
});
const type = query.type === 'personal' ? 'personal' : 'community';
if (res.status !== 200) {
throw new Error('Failed to get top songs');
}
if (type === 'community') {
const res = await ssApiClient(apiClientProps).getTopSongsList({
query: {
artist: query.artist,
count: query.limit,
},
});
return {
items:
res.body.topSongs?.song?.map((song) =>
if (res.status !== 200) {
throw new Error('Failed to get top songs');
}
return {
items: (res.body.topSongs?.song || []).map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [],
),
startIndex: 0,
totalRecordCount: res.body.topSongs?.song?.length || 0,
};
}
const res = await SubsonicController.getSongList({
apiClientProps,
query: {
artistIds: [query.artistId],
sortBy: SongListSort.PLAY_COUNT,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
});
const songsWithPlayCount = orderBy(
res.items.filter((song) => song.playCount > 0),
['playCount', 'albumId', 'trackNumber'],
['desc', 'asc', 'asc'],
);
return {
items: songsWithPlayCount,
startIndex: 0,
totalRecordCount: res.body.topSongs?.song?.length || 0,
totalRecordCount: res.totalRecordCount,
};
},
getUserInfo: async (args) => {