Add list count endpoints to jf/nd

This commit is contained in:
jeffvli
2023-12-18 11:45:04 -08:00
parent 50dd70df81
commit 5a94f70e63
2 changed files with 324 additions and 80 deletions
+227 -60
View File
@@ -1,62 +1,64 @@
import isElectron from 'is-electron';
import { z } from 'zod';
import packageJson from '../../../../package.json';
import { jfNormalize } from './jellyfin-normalize';
import { JFSongListSort, JFSortOrder } from '/@/renderer/api/jellyfin.types';
import { jfApiClient } from '/@/renderer/api/jellyfin/jellyfin-api';
import { jfType } from '/@/renderer/api/jellyfin/jellyfin-types';
import {
AuthenticationResponse,
MusicFolderListArgs,
MusicFolderListResponse,
GenreListArgs,
AlbumArtistDetailArgs,
AlbumArtistListArgs,
albumArtistListSortMap,
sortOrderMap,
ArtistListArgs,
artistListSortMap,
AlbumDetailArgs,
AlbumListArgs,
albumListSortMap,
TopSongListArgs,
SongListArgs,
songListSortMap,
AddToPlaylistArgs,
RemoveFromPlaylistArgs,
PlaylistDetailArgs,
PlaylistSongListArgs,
PlaylistListArgs,
playlistListSortMap,
AddToPlaylistResponse,
AlbumArtistDetailArgs,
AlbumArtistDetailResponse,
AlbumArtistListArgs,
AlbumArtistListResponse,
AlbumDetailArgs,
AlbumDetailResponse,
AlbumListArgs,
AlbumListResponse,
AuthenticationResponse,
ControllerEndpoint,
CreatePlaylistArgs,
CreatePlaylistResponse,
UpdatePlaylistArgs,
UpdatePlaylistResponse,
DeletePlaylistArgs,
FavoriteArgs,
FavoriteResponse,
ScrobbleArgs,
ScrobbleResponse,
GenreListArgs,
GenreListResponse,
AlbumArtistDetailResponse,
AlbumArtistListResponse,
AlbumDetailResponse,
AlbumListResponse,
SongListResponse,
AddToPlaylistResponse,
RemoveFromPlaylistResponse,
PlaylistDetailResponse,
PlaylistListResponse,
SearchArgs,
SearchResponse,
RandomSongListResponse,
RandomSongListArgs,
LyricsArgs,
LyricsResponse,
genreListSortMap,
MusicFolderListArgs,
MusicFolderListResponse,
PlaylistDetailArgs,
PlaylistDetailResponse,
PlaylistListArgs,
PlaylistListResponse,
PlaylistSongListArgs,
RandomSongListArgs,
RandomSongListResponse,
RemoveFromPlaylistArgs,
RemoveFromPlaylistResponse,
ScrobbleArgs,
ScrobbleResponse,
SearchArgs,
SearchResponse,
SongDetailArgs,
SongDetailResponse,
SongListArgs,
SongListResponse,
SongListSort,
SortOrder,
TopSongListArgs,
UpdatePlaylistArgs,
UpdatePlaylistResponse,
albumArtistListSortMap,
albumListSortMap,
genreListSortMap,
playlistListSortMap,
songListSortMap,
sortOrderMap,
} from '/@/renderer/api/types';
import { jfApiClient } from '/@/renderer/api/jellyfin/jellyfin-api';
import { jfNormalize } from './jellyfin-normalize';
import { jfType } from '/@/renderer/api/jellyfin/jellyfin-types';
import packageJson from '../../../../package.json';
import { z } from 'zod';
import { JFSongListSort, JFSortOrder } from '/@/renderer/api/jellyfin.types';
import isElectron from 'is-electron';
import { sortSongList } from '/@/renderer/api/utils';
const formatCommaDelimitedString = (value: string[]) => {
return value.join(',');
@@ -244,31 +246,56 @@ const getAlbumArtistList = async (args: AlbumArtistListArgs): Promise<AlbumArtis
};
};
const getArtistList = async (args: ArtistListArgs): Promise<AlbumArtistListResponse> => {
const getAlbumArtistListCount = async (args: AlbumArtistListArgs): Promise<number> => {
const { query, apiClientProps } = args;
const res = await jfApiClient(apiClientProps).getAlbumArtistList({
query: {
Limit: query.limit,
Fields: 'Genres, DateCreated, ExternalUrls, Overview',
ImageTypeLimit: 1,
Limit: 1,
ParentId: query.musicFolderId,
Recursive: true,
SortBy: artistListSortMap.jellyfin[query.sortBy] || 'Name,SortName',
SearchTerm: query.searchTerm,
SortBy: albumArtistListSortMap.jellyfin[query.sortBy] || 'Name,SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: 0,
UserId: apiClientProps.server?.userId || undefined,
},
});
if (res.status !== 200) {
throw new Error('Failed to get artist list');
throw new Error('Failed to get album artist list count');
}
return {
items: res.body.Items.map((item) => jfNormalize.albumArtist(item, apiClientProps.server)),
startIndex: query.startIndex,
totalRecordCount: res.body.TotalRecordCount,
};
return res.body.TotalRecordCount;
};
// const getArtistList = async (args: ArtistListArgs): Promise<ArtistListResponse> => {
// const { query, apiClientProps } = args;
// const res = await jfApiClient(apiClientProps).getAlbumArtistList({
// query: {
// Limit: query.limit,
// ParentId: query.musicFolderId,
// Recursive: true,
// SortBy: artistListSortMap.jellyfin[query.sortBy] || 'Name,SortName',
// SortOrder: sortOrderMap.jellyfin[query.sortOrder],
// StartIndex: query.startIndex,
// },
// });
// if (res.status !== 200) {
// throw new Error('Failed to get artist list');
// }
// return {
// items: res.body.Items.map((item) => jfNormalize.albumArtist(item, apiClientProps.server)),
// startIndex: query.startIndex,
// totalRecordCount: res.body.TotalRecordCount,
// };
// };
const getAlbumDetail = async (args: AlbumDetailArgs): Promise<AlbumDetailResponse> => {
const { query, apiClientProps } = args;
@@ -358,6 +385,55 @@ const getAlbumList = async (args: AlbumListArgs): Promise<AlbumListResponse> =>
};
};
const getAlbumListCount = async (args: AlbumListArgs): Promise<number> => {
const { query, apiClientProps } = args;
if (!apiClientProps.server?.userId) {
throw new Error('No userId found');
}
const yearsGroup = [];
if (query._custom?.jellyfin?.minYear && query._custom?.jellyfin?.maxYear) {
for (
let i = Number(query._custom?.jellyfin?.minYear);
i <= Number(query._custom?.jellyfin?.maxYear);
i += 1
) {
yearsGroup.push(String(i));
}
}
const yearsFilter = yearsGroup.length ? yearsGroup.join(',') : undefined;
const res = await jfApiClient(apiClientProps).getAlbumList({
params: {
userId: apiClientProps.server?.userId,
},
query: {
AlbumArtistIds: query.artistIds
? formatCommaDelimitedString(query.artistIds)
: undefined,
ContributingArtistIds: query.isCompilation ? query.artistIds?.[0] : undefined,
IncludeItemTypes: 'MusicAlbum',
Limit: 1,
ParentId: query.musicFolderId,
Recursive: true,
SearchTerm: query.searchTerm,
SortBy: albumListSortMap.jellyfin[query.sortBy] || 'SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: 0,
...query._custom?.jellyfin,
Years: yearsFilter,
},
});
if (res.status !== 200) {
throw new Error('Failed to get album list count');
}
return res.body.TotalRecordCount;
};
const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse> => {
const { apiClientProps, query } = args;
@@ -385,8 +461,11 @@ const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse>
throw new Error('Failed to get top song list');
}
const songs = res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, ''));
const songsByPlayCount = sortSongList(songs, SongListSort.PLAY_COUNT, SortOrder.DESC);
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
items: songsByPlayCount,
startIndex: 0,
totalRecordCount: res.body.TotalRecordCount,
};
@@ -450,6 +529,58 @@ const getSongList = async (args: SongListArgs): Promise<SongListResponse> => {
};
};
const getSongListCount = async (args: SongListArgs): Promise<number> => {
const { query, apiClientProps } = args;
if (!apiClientProps.server?.userId) {
throw new Error('No userId found');
}
const yearsGroup = [];
if (query._custom?.jellyfin?.minYear && query._custom?.jellyfin?.maxYear) {
for (
let i = Number(query._custom?.jellyfin?.minYear);
i <= Number(query._custom?.jellyfin?.maxYear);
i += 1
) {
yearsGroup.push(String(i));
}
}
const yearsFilter = yearsGroup.length ? formatCommaDelimitedString(yearsGroup) : undefined;
const albumIdsFilter = query.albumIds ? formatCommaDelimitedString(query.albumIds) : undefined;
const artistIdsFilter = query.artistIds
? formatCommaDelimitedString(query.artistIds)
: undefined;
const res = await jfApiClient(apiClientProps).getSongList({
params: {
userId: apiClientProps.server?.userId,
},
query: {
AlbumIds: albumIdsFilter,
ArtistIds: artistIdsFilter,
Fields: 'Genres, DateCreated, MediaSources, ParentId',
IncludeItemTypes: 'Audio',
Limit: 1,
ParentId: query.musicFolderId,
Recursive: true,
SearchTerm: query.searchTerm,
SortBy: songListSortMap.jellyfin[query.sortBy] || 'Album,SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: 0,
...query._custom?.jellyfin,
Years: yearsFilter,
},
});
if (res.status !== 200) {
throw new Error('Failed to get song list count');
}
return res.body.TotalRecordCount;
};
const addToPlaylist = async (args: AddToPlaylistArgs): Promise<AddToPlaylistResponse> => {
const { query, body, apiClientProps } = args;
@@ -481,6 +612,7 @@ const removeFromPlaylist = async (
const { query, apiClientProps } = args;
const res = await jfApiClient(apiClientProps).removeFromPlaylist({
body: null,
params: {
id: query.id,
},
@@ -588,6 +720,37 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResp
};
};
const getPlaylistListCount = async (args: PlaylistListArgs): Promise<number> => {
const { query, apiClientProps } = args;
if (!apiClientProps.server?.userId) {
throw new Error('No userId found');
}
const res = await jfApiClient(apiClientProps).getPlaylistList({
params: {
userId: apiClientProps.server?.userId,
},
query: {
Fields: 'ChildCount, Genres, DateCreated, ParentId, Overview',
IncludeItemTypes: 'Playlist',
Limit: 1,
MediaTypes: 'Audio',
Recursive: true,
SearchTerm: query.searchTerm,
SortBy: playlistListSortMap.jellyfin[query.sortBy],
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: 0,
},
});
if (res.status !== 200) {
throw new Error('Failed to get playlist list count');
}
return res.body.TotalRecordCount;
};
const createPlaylist = async (args: CreatePlaylistArgs): Promise<CreatePlaylistResponse> => {
const { body, apiClientProps } = args;
@@ -647,6 +810,7 @@ const deletePlaylist = async (args: DeletePlaylistArgs): Promise<null> => {
const { query, apiClientProps } = args;
const res = await jfApiClient(apiClientProps).deletePlaylist({
body: null,
params: {
id: query.id,
},
@@ -944,7 +1108,7 @@ const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse>
return jfNormalize.song(res.body, apiClientProps.server, '');
};
export const jfController = {
export const JellyfinController: ControllerEndpoint = {
addToPlaylist,
authenticate,
createFavorite,
@@ -953,19 +1117,22 @@ export const jfController = {
deletePlaylist,
getAlbumArtistDetail,
getAlbumArtistList,
getAlbumArtistListCount,
getAlbumDetail,
getAlbumList,
getArtistList,
getAlbumListCount,
getGenreList,
getLyrics,
getMusicFolderList,
getPlaylistDetail,
getPlaylistList,
getPlaylistListCount,
getPlaylistSongList,
getRandomSongList,
getSongDetail,
getSongList,
getTopSongList,
getSongListCount,
getTopSongs: getTopSongList,
removeFromPlaylist,
scrobble,
search,
@@ -194,6 +194,27 @@ const getAlbumArtistList = async (args: AlbumArtistListArgs): Promise<AlbumArtis
};
};
const getAlbumArtistListCount = async (args: AlbumArtistListArgs): Promise<number> => {
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).getAlbumArtistList({
query: {
_end: 1,
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: albumArtistListSortMap.navidrome[query.sortBy],
_start: 0,
name: query.searchTerm,
...query._custom?.navidrome,
},
});
if (res.status !== 200) {
throw new Error('Failed to get album artist list count');
}
return Number(res.body.headers.get('x-total-count') || 0);
};
const getAlbumDetail = async (args: AlbumDetailArgs): Promise<AlbumDetailResponse> => {
const { query, apiClientProps } = args;
@@ -251,6 +272,30 @@ const getAlbumList = async (args: AlbumListArgs): Promise<AlbumListResponse> =>
};
};
const getAlbumListCount = async (args: AlbumListArgs): Promise<number> => {
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).getAlbumList({
query: {
_end: 1,
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: albumListSortMap.navidrome[query.sortBy],
_start: 0,
artist_id: query.artistIds?.[0],
compilation: query.isCompilation,
genre_id: query.genre,
name: query.searchTerm,
...query._custom?.navidrome,
},
});
if (res.status !== 200) {
throw new Error('Failed to get album list');
}
return Number(res.body.headers.get('x-total-count') || 0);
};
const getSongList = async (args: SongListArgs): Promise<SongListResponse> => {
const { query, apiClientProps } = args;
@@ -280,6 +325,29 @@ const getSongList = async (args: SongListArgs): Promise<SongListResponse> => {
};
};
const getSongListCount = async (args: SongListArgs): Promise<number> => {
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).getSongList({
query: {
_end: 1,
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: songListSortMap.navidrome[query.sortBy],
_start: 0,
album_artist_id: query.artistIds,
album_id: query.albumIds,
title: query.searchTerm,
...query._custom?.navidrome,
},
});
if (res.status !== 200) {
throw new Error('Failed to get song list count');
}
return Number(res.body.headers.get('x-total-count') || 0);
};
const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse> => {
const { query, apiClientProps } = args;
@@ -345,6 +413,7 @@ const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistR
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).deletePlaylist({
body: null,
params: {
id: query.id,
},
@@ -384,6 +453,29 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResp
};
};
const getPlaylistListCount = async (args: PlaylistListArgs): Promise<number> => {
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).getPlaylistList({
query: {
_end: 1,
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: query.sortBy
? playlistListSortMap.navidrome[query.sortBy]
: playlistListSortMap.navidrome.name,
_start: 0,
q: query.searchTerm,
...query._custom?.navidrome,
},
});
if (res.status !== 200) {
throw new Error('Failed to get playlist list count');
}
return Number(res.body.headers.get('x-total-count') || 0);
};
const getPlaylistDetail = async (args: PlaylistDetailArgs): Promise<PlaylistDetailResponse> => {
const { query, apiClientProps } = args;
@@ -454,6 +546,7 @@ const removeFromPlaylist = async (
const { query, apiClientProps } = args;
const res = await ndApiClient(apiClientProps).removeFromPlaylist({
body: null,
params: {
id: query.id,
},
@@ -479,8 +572,10 @@ export const NavidromeController: ControllerEndpoint = {
deletePlaylist,
getAlbumArtistDetail,
getAlbumArtistList,
getAlbumArtistListCount,
getAlbumDetail,
getAlbumList,
getAlbumListCount,
getArtistDetail: undefined,
getArtistInfo: undefined,
getFavoritesList: undefined,
@@ -491,10 +586,12 @@ export const NavidromeController: ControllerEndpoint = {
getMusicFolderList: SubsonicController.getMusicFolderList,
getPlaylistDetail,
getPlaylistList,
getPlaylistListCount,
getPlaylistSongList,
getRandomSongList: SubsonicController.getRandomSongList,
getSongDetail,
getSongList,
getSongListCount,
getTopSongs: SubsonicController.getTopSongs,
getUserList,
removeFromPlaylist,
@@ -503,23 +600,3 @@ export const NavidromeController: ControllerEndpoint = {
setRating: SubsonicController.setRating,
updatePlaylist,
};
export const ndController = {
addToPlaylist,
authenticate,
createPlaylist,
deletePlaylist,
getAlbumArtistDetail,
getAlbumArtistList,
getAlbumDetail,
getAlbumList,
getGenreList,
getPlaylistDetail,
getPlaylistList,
getPlaylistSongList,
getSongDetail,
getSongList,
getUserList,
removeFromPlaylist,
updatePlaylist,
};