Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[enhancement]: better version checks for lyrics, Navidrome #529

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/renderer/api/features.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum ServerFeature {
MULTIPLE_STRUCTURED_LYRICS = 'multipleStructuredLyrics',
SINGLE_STRUCTURED_LYRIC = 'singleStructuredLyric',
SMART_PLAYLISTS = 'smartPlaylists',
SONG_LYRICS = 'songLyrics',
}

export type ServerFeatures = Record<Partial<ServerFeature>, boolean>;
export type ServerFeatures = Partial<Record<ServerFeature, boolean>>;
3 changes: 1 addition & 2 deletions src/renderer/api/jellyfin/jellyfin-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,8 +961,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
}

const features: ServerFeatures = {
smartPlaylists: false,
songLyrics: true,
singleStructuredLyric: true,
};

return {
Expand Down
18 changes: 10 additions & 8 deletions src/renderer/api/navidrome/navidrome-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
} from '../types';
import { hasFeature } from '/@/renderer/api/utils';
import { ServerFeature, ServerFeatures } from '/@/renderer/api/features.types';
import { SubsonicExtensions } from '/@/renderer/api/subsonic/subsonic-types';

const authenticate = async (
url: string,
Expand Down Expand Up @@ -528,20 +529,21 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
throw new Error('Failed to get server extensions');
}

for (const extension of res.body.openSubsonicExtensions) {
navidromeFeatures[extension.name] = extension.versions;
// The type here isn't necessarily an array (even though it's supposed to be). This is
// 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 = {
smartPlaylists: false,
songLyrics: true,
multipleStructuredLyrics: !!navidromeFeatures[SubsonicExtensions.SONG_LYRICS],
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! };
};

Expand Down
13 changes: 6 additions & 7 deletions src/renderer/api/subsonic/subsonic-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
throw new Error('Failed to ping server');
}

const features: ServerFeatures = {
smartPlaylists: false,
songLyrics: false,
};
const features: ServerFeatures = {};

if (!ping.body.openSubsonic || !ping.body.serverVersion) {
return { features, version: ping.body.version };
Expand All @@ -400,12 +397,14 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
}

const subsonicFeatures: Record<string, number[]> = {};
for (const extension of res.body.openSubsonicExtensions) {
subsonicFeatures[extension.name] = extension.versions;
if (Array.isArray(res.body.openSubsonicExtensions)) {
for (const extension of res.body.openSubsonicExtensions) {
subsonicFeatures[extension.name] = extension.versions;
}
}

if (subsonicFeatures[SubsonicExtensions.SONG_LYRICS]) {
features.songLyrics = true;
features.multipleStructuredLyrics = true;
}

return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
Expand Down
3 changes: 1 addition & 2 deletions src/renderer/api/subsonic/subsonic-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const extension = z.object({
});

const serverInfo = z.object({
openSubsonicExtensions: z.array(extension),
openSubsonicExtensions: z.array(extension).optional(),
});

const structuredLyricsParameters = z.object({
Expand Down Expand Up @@ -266,7 +266,6 @@ export enum SubsonicExtensions {
TRANSCODE_OFFSET = 'transcodeOffset',
}


export const ssType = {
_parameters: {
albumList: albumListParameters,
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ export const hasFeature = (server: ServerListItem | null, feature: ServerFeature
return false;
}

return server.features[feature];
return server.features[feature] ?? false;
};
24 changes: 12 additions & 12 deletions src/renderer/features/lyrics/queries/lyric-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,18 @@ export const useSongLyricsBySong = (
if (!server) throw new Error('Server not found');
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
.getLyrics({
apiClientProps: { server, signal },
Expand All @@ -113,17 +124,6 @@ export const useSongLyricsBySong = (
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) {
return {
artist: song.artists?.[0]?.name,
Expand Down
Loading