add jellyfin, improvements

This commit is contained in:
Kendall Garner 2023-06-02 23:54:34 -07:00 committed by Jeff
parent 85d2576bdc
commit 58f38b2655
11 changed files with 168 additions and 17 deletions

View file

@ -46,6 +46,8 @@ import type {
AuthenticationResponse,
SearchArgs,
SearchResponse,
LyricsArgs,
SynchronizedLyricsArray,
} from '/@/renderer/api/types';
import { ServerType } from '/@/renderer/types';
import { DeletePlaylistResponse, RandomSongListArgs } from './types';
@ -76,6 +78,7 @@ export type ControllerEndpoint = Partial<{
getFolderList: () => void;
getFolderSongs: () => void;
getGenreList: (args: GenreListArgs) => Promise<GenreListResponse>;
getLyrics: (args: LyricsArgs) => Promise<SynchronizedLyricsArray>;
getMusicFolderList: (args: MusicFolderListArgs) => Promise<MusicFolderListResponse>;
getPlaylistDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
getPlaylistList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
@ -119,6 +122,7 @@ const endpoints: ApiController = {
getFolderList: undefined,
getFolderSongs: undefined,
getGenreList: jfController.getGenreList,
getLyrics: jfController.getLyrics,
getMusicFolderList: jfController.getMusicFolderList,
getPlaylistDetail: jfController.getPlaylistDetail,
getPlaylistList: jfController.getPlaylistList,
@ -154,6 +158,7 @@ const endpoints: ApiController = {
getFolderList: undefined,
getFolderSongs: undefined,
getGenreList: ndController.getGenreList,
getLyrics: undefined,
getMusicFolderList: ssController.getMusicFolderList,
getPlaylistDetail: ndController.getPlaylistDetail,
getPlaylistList: ndController.getPlaylistList,
@ -188,6 +193,7 @@ const endpoints: ApiController = {
getFolderList: undefined,
getFolderSongs: undefined,
getGenreList: undefined,
getLyrics: undefined,
getMusicFolderList: ssController.getMusicFolderList,
getPlaylistDetail: undefined,
getPlaylistList: undefined,
@ -448,6 +454,12 @@ const getRandomSongList = async (args: RandomSongListArgs) => {
)?.(args);
};
const getLyrics = async (args: LyricsArgs) => {
return (
apiController('getLyrics', args.apiClientProps.server?.type) as ControllerEndpoint['getLyrics']
)?.(args);
};
export const controller = {
addToPlaylist,
authenticate,
@ -461,6 +473,7 @@ export const controller = {
getAlbumList,
getArtistList,
getGenreList,
getLyrics,
getMusicFolderList,
getPlaylistDetail,
getPlaylistList,

View file

@ -174,6 +174,14 @@ export const contract = c.router({
400: jfType._response.error,
},
},
getSongLyrics: {
method: 'GET',
path: 'users/:userId/Items/:id/Lyrics',
responses: {
200: jfType._response.lyrics,
404: jfType._response.error,
},
},
getTopSongsList: {
method: 'GET',
path: 'users/:userId/items',

View file

@ -44,6 +44,8 @@ import {
SearchResponse,
RandomSongListResponse,
RandomSongListArgs,
LyricsArgs,
SynchronizedLyricsArray,
} from '/@/renderer/api/types';
import { jfApiClient } from '/@/renderer/api/jellyfin/jellyfin-api';
import { jfNormalize } from './jellyfin-normalize';
@ -846,6 +848,28 @@ const getRandomSongList = async (args: RandomSongListArgs): Promise<RandomSongLi
totalRecordCount: res.body.Items.length || 0,
};
};
const getLyrics = async (args: LyricsArgs): Promise<SynchronizedLyricsArray> => {
const { query, apiClientProps } = args;
if (!apiClientProps.server?.userId) {
throw new Error('No userId found');
}
const res = await jfApiClient(apiClientProps).getSongLyrics({
params: {
id: query.songId,
userId: apiClientProps.server?.userId,
},
});
if (res.status !== 200) {
throw new Error('Failed to get lyrics');
}
return res.body.Lyrics.map((lyric) => [lyric.Start / 1e4, lyric.Text]);
};
export const jfController = {
addToPlaylist,
authenticate,
@ -859,6 +883,7 @@ export const jfController = {
getAlbumList,
getArtistList,
getGenreList,
getLyrics,
getMusicFolderList,
getPlaylistDetail,
getPlaylistList,

View file

@ -631,6 +631,15 @@ const searchParameters = paginationParameters.merge(baseParameters);
const search = z.any();
const lyricText = z.object({
Start: z.number(),
Text: z.string(),
});
const lyrics = z.object({
Lyrics: z.array(lyricText),
});
export const jfType = {
_enum: {
collection: jfCollection,
@ -670,6 +679,7 @@ export const jfType = {
favorite,
genre,
genreList,
lyrics,
musicFolderList,
playlist,
playlistList,

View file

@ -14,6 +14,7 @@ import type {
SearchQuery,
SongDetailQuery,
RandomSongListQuery,
LyricsQuery,
} from './types';
export const queryKeys: Record<
@ -102,6 +103,10 @@ export const queryKeys: Record<
if (query) return [serverId, 'songs', 'list', query] as const;
return [serverId, 'songs', 'list'] as const;
},
lyrics: (serverId: string, query?: LyricsQuery) => {
if (query) return [serverId, 'song', 'lyrics', query] as const;
return [serverId, 'song', 'lyrics'] as const;
},
randomSongList: (serverId: string, query?: RandomSongListQuery) => {
if (query) return [serverId, 'songs', 'randomSongList', query] as const;
return [serverId, 'songs', 'randomSongList'] as const;

View file

@ -1017,6 +1017,16 @@ export type RandomSongListArgs = {
export type RandomSongListResponse = SongListResponse;
export type LyricsQuery = {
songId: string;
};
export type LyricsArgs = {
query: LyricsQuery;
} & BaseEndpointArgs;
export type SynchronizedLyricsArray = Array<[number, string]>;
export const instanceOfCancellationError = (error: any) => {
return 'revert' in error;
};