add server info query

This commit is contained in:
Kendall Garner 2024-02-01 08:17:31 -08:00
parent 9995b2e774
commit 9720fcc202
No known key found for this signature in database
GPG key ID: 18D2767419676C87
13 changed files with 173 additions and 10 deletions

View file

@ -48,6 +48,8 @@ import type {
SearchResponse,
LyricsArgs,
LyricsResponse,
ServerInfo,
ServerInfoArgs,
} from '/@/renderer/api/types';
import { ServerType } from '/@/renderer/types';
import { DeletePlaylistResponse, RandomSongListArgs } from './types';
@ -85,6 +87,7 @@ export type ControllerEndpoint = Partial<{
getPlaylistList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
getPlaylistSongList: (args: PlaylistSongListArgs) => Promise<SongListResponse>;
getRandomSongList: (args: RandomSongListArgs) => Promise<SongListResponse>;
getServerInfo: (args: ServerInfoArgs) => Promise<ServerInfo>;
getSongDetail: (args: SongDetailArgs) => Promise<SongDetailResponse>;
getSongList: (args: SongListArgs) => Promise<SongListResponse>;
getTopSongs: (args: TopSongListArgs) => Promise<TopSongListResponse>;
@ -129,6 +132,7 @@ const endpoints: ApiController = {
getPlaylistList: jfController.getPlaylistList,
getPlaylistSongList: jfController.getPlaylistSongList,
getRandomSongList: jfController.getRandomSongList,
getServerInfo: jfController.getServerInfo,
getSongDetail: jfController.getSongDetail,
getSongList: jfController.getSongList,
getTopSongs: jfController.getTopSongList,
@ -165,6 +169,7 @@ const endpoints: ApiController = {
getPlaylistList: ndController.getPlaylistList,
getPlaylistSongList: ndController.getPlaylistSongList,
getRandomSongList: ssController.getRandomSongList,
getServerInfo: ssController.getServerInfo,
getSongDetail: ndController.getSongDetail,
getSongList: ndController.getSongList,
getTopSongs: ssController.getTopSongList,
@ -198,6 +203,7 @@ const endpoints: ApiController = {
getMusicFolderList: ssController.getMusicFolderList,
getPlaylistDetail: undefined,
getPlaylistList: undefined,
getServerInfo: ssController.getServerInfo,
getSongDetail: undefined,
getSongList: undefined,
getTopSongs: ssController.getTopSongList,
@ -481,6 +487,15 @@ const getLyrics = async (args: LyricsArgs) => {
)?.(args);
};
const getServerInfo = async (args: ServerInfoArgs) => {
return (
apiController(
'getServerInfo',
args.apiClientProps.server?.type,
) as ControllerEndpoint['getServerInfo']
)?.(args);
};
export const controller = {
addToPlaylist,
authenticate,
@ -500,6 +515,7 @@ export const controller = {
getPlaylistList,
getPlaylistSongList,
getRandomSongList,
getServerInfo,
getSongDetail,
getSongList,
getTopSongList,

View file

@ -150,6 +150,14 @@ export const contract = c.router({
400: jfType._response.error,
},
},
getServerInfo: {
method: 'GET',
path: 'system/info',
responses: {
200: jfType._response.serverInfo,
400: jfType._response.error,
},
},
getSimilarArtistList: {
method: 'GET',
path: 'artists/:id/similar',

View file

@ -49,6 +49,8 @@ import {
genreListSortMap,
SongDetailArgs,
SongDetailResponse,
ServerInfo,
ServerInfoArgs,
} from '/@/renderer/api/types';
import { jfApiClient } from '/@/renderer/api/jellyfin/jellyfin-api';
import { jfNormalize } from './jellyfin-normalize';
@ -946,6 +948,18 @@ const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse>
return jfNormalize.song(res.body, apiClientProps.server, '');
};
const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const { apiClientProps } = args;
const res = await jfApiClient(apiClientProps).getServerInfo();
if (res.status !== 200) {
throw new Error('Failed to get song detail');
}
return { id: apiClientProps.server?.id, version: res.body.Version };
};
export const jfController = {
addToPlaylist,
authenticate,
@ -965,6 +979,7 @@ export const jfController = {
getPlaylistList,
getPlaylistSongList,
getRandomSongList,
getServerInfo,
getSongDetail,
getSongList,
getTopSongList,

View file

@ -654,6 +654,10 @@ const lyrics = z.object({
Lyrics: z.array(lyricText),
});
const serverInfo = z.object({
Version: z.string(),
});
export const jfType = {
_enum: {
albumArtistList: albumArtistListSort,
@ -707,6 +711,7 @@ export const jfType = {
removeFromPlaylist,
scrobble,
search,
serverInfo,
song,
songList,
topSongsList,

View file

@ -50,6 +50,13 @@ export const contract = c.router({
200: ssType._response.randomSongList,
},
},
getServerInfo: {
method: 'GET',
path: 'getOpenSubsonicExtensions.view',
responses: {
200: ssType._response.serverInfo,
},
},
getTopSongsList: {
method: 'GET',
path: 'getTopSongs.view',
@ -58,6 +65,13 @@ export const contract = c.router({
200: ssType._response.topSongsList,
},
},
ping: {
method: 'GET',
path: 'ping.view',
responses: {
200: ssType._response.ping,
},
},
removeFavorite: {
method: 'GET',
path: 'unstar.view',

View file

@ -21,6 +21,8 @@ import {
SearchResponse,
RandomSongListResponse,
RandomSongListArgs,
ServerInfo,
ServerInfoArgs,
} from '/@/renderer/api/types';
import { randomString } from '/@/renderer/utils';
@ -368,12 +370,40 @@ const getRandomSongList = async (args: RandomSongListArgs): Promise<RandomSongLi
};
};
const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const { apiClientProps } = args;
const ping = await ssApiClient(apiClientProps).ping();
if (ping.status !== 200) {
throw new Error('Failed to ping server');
}
if (!ping.body.openSubsonic || !ping.body.serverVersion) {
return { version: ping.body.version };
}
const res = await ssApiClient(apiClientProps).getServerInfo();
if (res.status !== 200) {
throw new Error('Failed to get server extensions');
}
const features: Record<string, number[]> = {};
for (const extension of res.body.openSubsonicExtensions) {
features[extension.name] = extension.versions;
}
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
};
export const ssController = {
authenticate,
createFavorite,
getArtistInfo,
getMusicFolderList,
getRandomSongList,
getServerInfo,
getTopSongList,
removeFavorite,
scrobble,

View file

@ -206,6 +206,21 @@ const randomSongList = z.object({
}),
});
const ping = z.object({
openSubsonic: z.boolean().optional(),
serverVersion: z.string().optional(),
version: z.string(),
});
const extension = z.object({
name: z.string(),
versions: z.number().array(),
});
const serverInfo = z.object({
openSubsonicExtensions: z.array(extension),
});
export const ssType = {
_parameters: {
albumList: albumListParameters,
@ -229,10 +244,12 @@ export const ssType = {
baseResponse,
createFavorite,
musicFolderList,
ping,
randomSongList,
removeFavorite,
scrobble,
search3,
serverInfo,
setRating,
song,
topSongsList,

View file

@ -1139,3 +1139,17 @@ export type FontData = {
postscriptName: string;
style: string;
};
export type ServerInfoArgs = BaseEndpointArgs;
export enum SubsonicExtensions {
FORM_POST = 'formPost',
SONG_LYRICS = 'songLyrics',
TRANSCODE_OFFSET = 'transcodeOffset',
}
export type ServerInfo = {
features?: Record<string, number[]>;
id?: string;
version: string;
};

View file

@ -27,6 +27,7 @@ import { FontType, PlaybackType, PlayerStatus } from '/@/renderer/types';
import '@ag-grid-community/styles/ag-grid.css';
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
import i18n from '/@/i18n/i18n';
import { useServerVersion } from '/@/renderer/hooks/use-server-version';
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
@ -49,6 +50,7 @@ export const App = () => {
const remoteSettings = useRemoteSettings();
const textStyleRef = useRef<HTMLStyleElement>();
useDiscordRpc();
useServerVersion();
useEffect(() => {
if (type === FontType.SYSTEM && system) {

View file

@ -6,6 +6,7 @@ import {
InternetProviderLyricResponse,
FullLyricsMetadata,
LyricGetQuery,
SubsonicExtensions,
} from '/@/renderer/api/types';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useLyricsSettings } from '/@/renderer/store';
@ -93,16 +94,6 @@ export const useSongLyricsBySong = (
if (!server) throw new Error('Server not found');
if (!song) return null;
if (song.lyrics) {
return {
artist: song.artists?.[0]?.name,
lyrics: formatLyrics(song.lyrics),
name: song.name,
remote: false,
source: server?.name ?? 'music server',
};
}
if (server.type === ServerType.JELLYFIN) {
const jfLyrics = await api.controller
.getLyrics({
@ -120,6 +111,16 @@ export const useSongLyricsBySong = (
source: server?.name ?? 'music server',
};
}
} else if (server.features && SubsonicExtensions.SONG_LYRICS in server.features) {
console.log(1234);
} else if (song.lyrics) {
return {
artist: song.artists?.[0]?.name,
lyrics: formatLyrics(song.lyrics),
name: song.name,
remote: false,
source: server?.name ?? 'music server',
};
}
if (fetch) {

View file

@ -12,6 +12,8 @@ import { useAuthStoreActions } from '/@/renderer/store';
import { ServerListItem, ServerType } from '/@/renderer/types';
import { api } from '/@/renderer/api';
import i18n from '/@/i18n/i18n';
import { queryClient } from '/@/renderer/lib/react-query';
import { queryKeys } from '/@/renderer/api/query-keys';
const localSettings = isElectron() ? window.electron.localSettings : null;
@ -111,6 +113,8 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
localSettings.passwordRemove(server.id);
}
}
queryClient.invalidateQueries({ queryKey: queryKeys.server.root(server.id) });
} catch (err: any) {
setIsLoading(false);
return toast.error({ message: err?.message });

View file

@ -0,0 +1,35 @@
import { useEffect } from 'react';
import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { useQuery } from '@tanstack/react-query';
import { queryKeys } from '/@/renderer/api/query-keys';
import { controller } from '/@/renderer/api/controller';
export const useServerVersion = () => {
const { updateServer } = useAuthStoreActions();
const server = useCurrentServer();
const serverInfo = useQuery({
enabled: !!server,
queryFn: async ({ signal }) => {
return controller.getServerInfo({
apiClientProps: {
server,
signal,
},
});
},
queryKey: queryKeys.server.root(server?.id),
});
useEffect(() => {
if (server && server.id === serverInfo.data?.id) {
const { version, features } = serverInfo.data;
if (version !== server.version) {
updateServer(server.id, {
features,
version,
});
}
}
}, [server, serverInfo.data, updateServer]);
};

View file

@ -62,6 +62,7 @@ export enum ServerType {
export type ServerListItem = {
credential: string;
features?: Record<string, number[]>;
id: string;
name: string;
ndCredential?: string;
@ -70,6 +71,7 @@ export type ServerListItem = {
url: string;
userId: string | null;
username: string;
version?: string;
};
export enum PlayerStatus {