Add playlist list

This commit is contained in:
jeffvli 2022-12-31 03:46:12 -08:00
parent 00a21269dd
commit ec79d91d30
21 changed files with 911 additions and 47 deletions

View file

@ -199,6 +199,10 @@ const getArtistList = async (args: ArtistListArgs) => {
return (apiController('getArtistList') as ControllerEndpoint['getArtistList'])?.(args);
};
const getPlaylistList = async (args: PlaylistListArgs) => {
return (apiController('getPlaylistList') as ControllerEndpoint['getPlaylistList'])?.(args);
};
export const controller = {
getAlbumArtistList,
getAlbumDetail,
@ -206,5 +210,6 @@ export const controller = {
getArtistList,
getGenreList,
getMusicFolderList,
getPlaylistList,
getSongList,
};

View file

@ -22,6 +22,7 @@ import type {
JFGenreListResponse,
JFMusicFolderList,
JFMusicFolderListResponse,
JFPlaylist,
JFPlaylistDetail,
JFPlaylistDetailResponse,
JFPlaylistList,
@ -32,7 +33,7 @@ import type {
JFSongListResponse,
} from '/@/renderer/api/jellyfin.types';
import { JFCollectionType } from '/@/renderer/api/jellyfin.types';
import type {
import {
Album,
AlbumArtist,
AlbumArtistDetailArgs,
@ -48,13 +49,13 @@ import type {
FavoriteResponse,
GenreListArgs,
MusicFolderListArgs,
Playlist,
PlaylistDetailArgs,
PlaylistListArgs,
playlistListSortMap,
PlaylistSongListArgs,
Song,
SongListArgs,
} from '/@/renderer/api/types';
import {
songListSortMap,
albumListSortMap,
artistListSortMap,
@ -396,18 +397,20 @@ const getPlaylistSongList = async (args: PlaylistSongListArgs): Promise<JFSongLi
};
const getPlaylistList = async (args: PlaylistListArgs): Promise<JFPlaylistList> => {
const { server, signal } = args;
const { query, server, signal } = args;
const searchParams = {
fields: 'ChildCount, Genres, DateCreated, ParentId, Overview',
includeItemTypes: 'Playlist',
limit: query.limit,
recursive: true,
sortBy: 'SortName',
sortOrder: 'Ascending',
sortBy: playlistListSortMap.jellyfin[query.sortBy],
sortOrder: sortOrderMap.jellyfin[query.sortOrder],
startIndex: query.startIndex,
};
const data = await api
.get(`/users/${server?.userId}/items`, {
.get(`users/${server?.userId}/items`, {
headers: { 'X-MediaBrowser-Token': server?.credential },
prefixUrl: server?.url,
searchParams: parseSearchParams(searchParams),
@ -415,12 +418,12 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<JFPlaylistList>
})
.json<JFPlaylistListResponse>();
const playlistData = data.Items.filter((item) => item.MediaType === 'Audio');
const playlistItems = data.Items.filter((item) => item.MediaType === 'Audio');
return {
Items: playlistData,
StartIndex: 0,
TotalRecordCount: playlistData.length,
items: playlistItems,
startIndex: 0,
totalRecordCount: playlistItems.length,
};
};
@ -690,6 +693,20 @@ const normalizeAlbumArtist = (
};
};
const normalizePlaylist = (item: JFPlaylist): Playlist => {
return {
duration: item.RunTimeTicks / 10000000,
id: item.Id,
name: item.Name,
public: null,
rules: null,
size: null,
songCount: item?.ChildCount || null,
userId: null,
username: null,
};
};
// const normalizeArtist = (item: any) => {
// return {
// album: (item.album || []).map((entry: any) => normalizeAlbum(entry)),
@ -710,24 +727,6 @@ const normalizeAlbumArtist = (
// };
// };
// const normalizePlaylist = (item: any) => {
// return {
// changed: item.DateLastMediaAdded,
// comment: item.Overview,
// created: item.DateCreated,
// duration: item.RunTimeTicks / 10000000,
// genre: item.GenreItems && item.GenreItems.map((entry: any) => normalizeItem(entry)),
// id: item.Id,
// image: getCoverArtUrl(item, 350),
// owner: undefined,
// public: undefined,
// song: [],
// songCount: item.ChildCount,
// title: item.Name,
// uniqueId: nanoid(),
// };
// };
// const normalizeGenre = (item: any) => {
// return {
// albumCount: undefined,
@ -780,5 +779,6 @@ export const jellyfinApi = {
export const jfNormalize = {
album: normalizeAlbum,
albumArtist: normalizeAlbumArtist,
playlist: normalizePlaylist,
song: normalizeSong,
};

View file

@ -63,7 +63,19 @@ export interface JFPlaylistListResponse extends JFBasePaginatedResponse {
Items: JFPlaylist[];
}
export type JFPlaylistList = JFPlaylistListResponse;
export type JFPlaylistList = {
items: JFPlaylist[];
startIndex: number;
totalRecordCount: number;
};
export enum JFPlaylistListSort {
ALBUM_ARTIST = 'AlbumArtist,SortName',
DURATION = 'Runtime',
NAME = 'SortName',
RECENTLY_ADDED = 'DateCreated,SortName',
SONG_COUNT = 'ChildCount',
}
export type JFPlaylistDetailResponse = JFPlaylist;
@ -485,6 +497,7 @@ type JFBaseParams = {
imageTypeLimit?: number;
parentId?: string;
recursive?: boolean;
searchTerm?: string;
userId?: string;
};

View file

@ -31,6 +31,7 @@ import type {
NDSongList,
NDSongListResponse,
NDAlbumArtist,
NDPlaylist,
} from '/@/renderer/api/navidrome.types';
import { NDPlaylistListSort, NDSongListSort, NDSortOrder } from '/@/renderer/api/navidrome.types';
import type {
@ -51,6 +52,7 @@ import type {
CreatePlaylistResponse,
PlaylistSongListArgs,
AlbumArtist,
Playlist,
} from '/@/renderer/api/types';
import {
playlistListSortMap,
@ -329,12 +331,13 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<NDPlaylistList>
_order: query.sortOrder ? sortOrderMap.navidrome[query.sortOrder] : NDSortOrder.ASC,
_sort: query.sortBy ? playlistListSortMap.navidrome[query.sortBy] : NDPlaylistListSort.NAME,
_start: query.startIndex,
...query.ndParams,
};
const res = await api.get('api/playlist', {
headers: { 'x-nd-authorization': `Bearer ${server?.ndCredential}` },
prefixUrl: server?.url,
searchParams,
searchParams: parseSearchParams(searchParams),
signal,
});
@ -521,6 +524,20 @@ const normalizeAlbumArtist = (item: NDAlbumArtist): AlbumArtist => {
};
};
const normalizePlaylist = (item: NDPlaylist): Playlist => {
return {
duration: item.duration,
id: item.id,
name: item.name,
public: item.public,
rules: item?.rules || null,
size: item.size,
songCount: item.songCount,
userId: item.ownerId,
username: item.ownerName,
};
};
export const navidromeApi = {
authenticate,
createPlaylist,
@ -540,5 +557,6 @@ export const navidromeApi = {
export const ndNormalize = {
album: normalizeAlbum,
albumArtist: normalizeAlbumArtist,
playlist: normalizePlaylist,
song: normalizeSong,
};

View file

@ -287,7 +287,7 @@ export type NDPlaylist = {
ownerName: string;
path: string;
public: boolean;
rules: null;
rules: Record<string, any> | null;
size: number;
songCount: number;
sync: boolean;
@ -309,7 +309,7 @@ export type NDPlaylistListResponse = NDPlaylist[];
export enum NDPlaylistListSort {
DURATION = 'duration',
NAME = 'name',
OWNER = 'owner',
OWNER = 'ownerName',
PUBLIC = 'public',
SONG_COUNT = 'songCount',
UPDATED_AT = 'updatedAt',

View file

@ -4,10 +4,17 @@ import type {
JFAlbumArtist,
JFGenreList,
JFMusicFolderList,
JFPlaylist,
JFSong,
} from '/@/renderer/api/jellyfin.types';
import { ndNormalize } from '/@/renderer/api/navidrome.api';
import type { NDAlbum, NDAlbumArtist, NDGenreList, NDSong } from '/@/renderer/api/navidrome.types';
import type {
NDAlbum,
NDAlbumArtist,
NDGenreList,
NDPlaylist,
NDSong,
} from '/@/renderer/api/navidrome.types';
import { SSGenreList, SSMusicFolderList } from '/@/renderer/api/subsonic.types';
import type {
Album,
@ -16,6 +23,7 @@ import type {
RawAlbumListResponse,
RawGenreListResponse,
RawMusicFolderListResponse,
RawPlaylistListResponse,
RawSongListResponse,
} from '/@/renderer/api/types';
import { ServerListItem } from '/@/renderer/types';
@ -163,11 +171,32 @@ const albumArtistList = (
};
};
const playlistList = (data: RawPlaylistListResponse | undefined, server: ServerListItem | null) => {
let playlists;
switch (server?.type) {
case 'jellyfin':
playlists = data?.items.map((item) => jfNormalize.playlist(item as JFPlaylist));
break;
case 'navidrome':
playlists = data?.items.map((item) => ndNormalize.playlist(item as NDPlaylist));
break;
case 'subsonic':
break;
}
return {
items: playlists,
startIndex: data?.startIndex,
totalRecordCount: data?.totalRecordCount,
};
};
export const normalize = {
albumArtistList,
albumDetail,
albumList,
genreList,
musicFolderList,
playlistList,
songList,
};

View file

@ -4,6 +4,7 @@ import type {
AlbumDetailQuery,
AlbumArtistListQuery,
ArtistListQuery,
PlaylistListQuery,
} from './types';
export const queryKeys = {
@ -34,6 +35,11 @@ export const queryKeys = {
musicFolders: {
list: (serverId: string) => [serverId, 'musicFolders', 'list'] as const,
},
playlists: {
list: (serverId: string, query?: PlaylistListQuery) =>
[serverId, 'playlists', 'list', query] as const,
root: (serverId: string) => [serverId, 'playlists'] as const,
},
server: {
root: (serverId: string) => [serverId] as const,
},

View file

@ -14,6 +14,7 @@ import {
JFPlaylistList,
JFPlaylistDetail,
JFMusicFolderList,
JFPlaylistListSort,
} from '/@/renderer/api/jellyfin.types';
import {
NDSortOrder,
@ -243,14 +244,15 @@ export type MusicFolder = {
};
export type Playlist = {
duration?: number;
duration: number | null;
id: string;
name: string;
public?: boolean;
size?: number;
songCount?: number;
userId: string;
username: string;
public: boolean | null;
rules?: Record<string, any> | null;
size: number | null;
songCount: number | null;
userId: string | null;
username: string | null;
};
export type GenresResponse = Genre[];
@ -756,11 +758,21 @@ export type RawPlaylistListResponse = NDPlaylistList | JFPlaylistList | undefine
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]>;
export type PlaylistListSort = NDPlaylistListSort;
export enum PlaylistListSort {
DURATION = 'duration',
NAME = 'name',
OWNER = 'owner',
PUBLIC = 'public',
SONG_COUNT = 'songCount',
UPDATED_AT = 'updatedAt',
}
export type PlaylistListQuery = {
limit?: number;
musicFolderId?: string;
ndParams?: {
owner_id?: string;
};
searchTerm?: string;
sortBy: PlaylistListSort;
sortOrder: SortOrder;
startIndex: number;
@ -769,18 +781,18 @@ export type PlaylistListQuery = {
export type PlaylistListArgs = { query: PlaylistListQuery } & BaseEndpointArgs;
type PlaylistListSortMap = {
jellyfin: Record<PlaylistListSort, undefined>;
jellyfin: Record<PlaylistListSort, JFPlaylistListSort | undefined>;
navidrome: Record<PlaylistListSort, NDPlaylistListSort | undefined>;
subsonic: Record<PlaylistListSort, undefined>;
};
export const playlistListSortMap: PlaylistListSortMap = {
jellyfin: {
duration: undefined,
name: undefined,
duration: JFPlaylistListSort.DURATION,
name: JFPlaylistListSort.NAME,
owner: undefined,
public: undefined,
songCount: undefined,
songCount: JFPlaylistListSort.SONG_COUNT,
updatedAt: undefined,
},
navidrome: {