mirror of
https://github.com/jeffvli/feishin.git
synced 2024-11-20 14:37:06 +01:00
[enhancement]: better version checks for lyrics, Navidrome (#529)
- Actually make serverfeatures partial - Navidrome: only set multiple structured lyrics if extension exists - Navidrome/Subsonic: minor type checking of OS extension (Navidrome implementation detail) - Jellyfin: add separate knob for lyrics. Note, this should also probably be behind some version check...
This commit is contained in:
parent
d52d9136b8
commit
73845a9432
@ -1,6 +1,7 @@
|
|||||||
export enum ServerFeature {
|
export enum ServerFeature {
|
||||||
|
MULTIPLE_STRUCTURED_LYRICS = 'multipleStructuredLyrics',
|
||||||
|
SINGLE_STRUCTURED_LYRIC = 'singleStructuredLyric',
|
||||||
SMART_PLAYLISTS = 'smartPlaylists',
|
SMART_PLAYLISTS = 'smartPlaylists',
|
||||||
SONG_LYRICS = 'songLyrics',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ServerFeatures = Record<Partial<ServerFeature>, boolean>;
|
export type ServerFeatures = Partial<Record<ServerFeature, boolean>>;
|
||||||
|
@ -961,8 +961,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const features: ServerFeatures = {
|
const features: ServerFeatures = {
|
||||||
smartPlaylists: false,
|
singleStructuredLyric: true,
|
||||||
songLyrics: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -50,6 +50,7 @@ import {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
import { hasFeature } from '/@/renderer/api/utils';
|
import { hasFeature } from '/@/renderer/api/utils';
|
||||||
import { ServerFeature, ServerFeatures } from '/@/renderer/api/features.types';
|
import { ServerFeature, ServerFeatures } from '/@/renderer/api/features.types';
|
||||||
|
import { SubsonicExtensions } from '/@/renderer/api/subsonic/subsonic-types';
|
||||||
|
|
||||||
const authenticate = async (
|
const authenticate = async (
|
||||||
url: string,
|
url: string,
|
||||||
@ -528,20 +529,21 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
|
|||||||
throw new Error('Failed to get server extensions');
|
throw new Error('Failed to get server extensions');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const extension of res.body.openSubsonicExtensions) {
|
// The type here isn't necessarily an array (even though it's supposed to be). This is
|
||||||
navidromeFeatures[extension.name] = extension.versions;
|
// an implementation detail of Navidrome 0.50. Do a type check to make sure it's actually
|
||||||
|
// an array, and not an empty object.
|
||||||
|
if (Array.isArray(res.body.openSubsonicExtensions)) {
|
||||||
|
for (const extension of res.body.openSubsonicExtensions) {
|
||||||
|
navidromeFeatures[extension.name] = extension.versions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const features: ServerFeatures = {
|
const features: ServerFeatures = {
|
||||||
smartPlaylists: false,
|
multipleStructuredLyrics: !!navidromeFeatures[SubsonicExtensions.SONG_LYRICS],
|
||||||
songLyrics: true,
|
smartPlaylists: !!navidromeFeatures[NavidromeExtensions.SMART_PLAYLISTS],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (navidromeFeatures[NavidromeExtensions.SMART_PLAYLISTS]) {
|
|
||||||
features[ServerFeature.SMART_PLAYLISTS] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
|
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -384,10 +384,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
|
|||||||
throw new Error('Failed to ping server');
|
throw new Error('Failed to ping server');
|
||||||
}
|
}
|
||||||
|
|
||||||
const features: ServerFeatures = {
|
const features: ServerFeatures = {};
|
||||||
smartPlaylists: false,
|
|
||||||
songLyrics: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!ping.body.openSubsonic || !ping.body.serverVersion) {
|
if (!ping.body.openSubsonic || !ping.body.serverVersion) {
|
||||||
return { features, version: ping.body.version };
|
return { features, version: ping.body.version };
|
||||||
@ -400,12 +397,14 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const subsonicFeatures: Record<string, number[]> = {};
|
const subsonicFeatures: Record<string, number[]> = {};
|
||||||
for (const extension of res.body.openSubsonicExtensions) {
|
if (Array.isArray(res.body.openSubsonicExtensions)) {
|
||||||
subsonicFeatures[extension.name] = extension.versions;
|
for (const extension of res.body.openSubsonicExtensions) {
|
||||||
|
subsonicFeatures[extension.name] = extension.versions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subsonicFeatures[SubsonicExtensions.SONG_LYRICS]) {
|
if (subsonicFeatures[SubsonicExtensions.SONG_LYRICS]) {
|
||||||
features.songLyrics = true;
|
features.multipleStructuredLyrics = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
|
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
|
||||||
|
@ -218,7 +218,7 @@ const extension = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const serverInfo = z.object({
|
const serverInfo = z.object({
|
||||||
openSubsonicExtensions: z.array(extension),
|
openSubsonicExtensions: z.array(extension).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const structuredLyricsParameters = z.object({
|
const structuredLyricsParameters = z.object({
|
||||||
@ -266,7 +266,6 @@ export enum SubsonicExtensions {
|
|||||||
TRANSCODE_OFFSET = 'transcodeOffset',
|
TRANSCODE_OFFSET = 'transcodeOffset',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const ssType = {
|
export const ssType = {
|
||||||
_parameters: {
|
_parameters: {
|
||||||
albumList: albumListParameters,
|
albumList: albumListParameters,
|
||||||
|
@ -45,5 +45,5 @@ export const hasFeature = (server: ServerListItem | null, feature: ServerFeature
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return server.features[feature];
|
return server.features[feature] ?? false;
|
||||||
};
|
};
|
||||||
|
@ -96,7 +96,18 @@ export const useSongLyricsBySong = (
|
|||||||
if (!server) throw new Error('Server not found');
|
if (!server) throw new Error('Server not found');
|
||||||
if (!song) return null;
|
if (!song) return null;
|
||||||
|
|
||||||
if (server.type === ServerType.JELLYFIN) {
|
if (hasFeature(server, ServerFeature.MULTIPLE_STRUCTURED_LYRICS)) {
|
||||||
|
const subsonicLyrics = await api.controller
|
||||||
|
.getStructuredLyrics({
|
||||||
|
apiClientProps: { server, signal },
|
||||||
|
query: { songId: song.id },
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
|
if (subsonicLyrics) {
|
||||||
|
return subsonicLyrics;
|
||||||
|
}
|
||||||
|
} else if (hasFeature(server, ServerFeature.SINGLE_STRUCTURED_LYRIC)) {
|
||||||
const jfLyrics = await api.controller
|
const jfLyrics = await api.controller
|
||||||
.getLyrics({
|
.getLyrics({
|
||||||
apiClientProps: { server, signal },
|
apiClientProps: { server, signal },
|
||||||
@ -113,17 +124,6 @@ export const useSongLyricsBySong = (
|
|||||||
source: server?.name ?? 'music server',
|
source: server?.name ?? 'music server',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (hasFeature(server, ServerFeature.SONG_LYRICS)) {
|
|
||||||
const subsonicLyrics = await api.controller
|
|
||||||
.getStructuredLyrics({
|
|
||||||
apiClientProps: { server, signal },
|
|
||||||
query: { songId: song.id },
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
|
|
||||||
if (subsonicLyrics) {
|
|
||||||
return subsonicLyrics;
|
|
||||||
}
|
|
||||||
} else if (song.lyrics) {
|
} else if (song.lyrics) {
|
||||||
return {
|
return {
|
||||||
artist: song.artists?.[0]?.name,
|
artist: song.artists?.[0]?.name,
|
||||||
|
Loading…
Reference in New Issue
Block a user