[enhancement]: use jellyfin 10.9.0 lyrics

This commit is contained in:
Kendall Garner 2024-04-22 19:44:10 -07:00
parent cb2597d2c8
commit 087ea44737
No known key found for this signature in database
GPG key ID: 18D2767419676C87
4 changed files with 63 additions and 37 deletions

View file

@ -204,7 +204,7 @@ export const contract = c.router({
},
getSongLyrics: {
method: 'GET',
path: 'users/:userId/Items/:id/Lyrics',
path: 'audio/:id/Lyrics',
responses: {
200: jfType._response.lyrics,
404: jfType._response.error,

View file

@ -61,7 +61,8 @@ import packageJson from '../../../../package.json';
import { z } from 'zod';
import { JFSongListSort, JFSortOrder } from '/@/renderer/api/jellyfin.types';
import isElectron from 'is-electron';
import { ServerFeatures } from '/@/renderer/api/features-types';
import { ServerFeature } from '/@/renderer/api/features-types';
import { VersionInfo, getFeatures } from '/@/renderer/api/utils';
const formatCommaDelimitedString = (value: string[]) => {
return value.join(',');
@ -937,7 +938,6 @@ const getLyrics = async (args: LyricsArgs): Promise<LyricsResponse> => {
const res = await jfApiClient(apiClientProps).getSongLyrics({
params: {
id: query.songId,
userId: apiClientProps.server?.userId,
},
});
@ -969,6 +969,8 @@ const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse>
return jfNormalize.song(res.body, apiClientProps.server, '');
};
const VERSION_INFO: VersionInfo = [['10.9.0', { [ServerFeature.LYRICS_SINGLE_STRUCTURED]: [1] }]];
const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const { apiClientProps } = args;
@ -978,9 +980,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
throw new Error('Failed to get server info');
}
const features: ServerFeatures = {
lyricsSingleStructured: true,
};
const features = getFeatures(VERSION_INFO, res.body.Version);
return {
features,

View file

@ -2,8 +2,6 @@ import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize';
import { ndType } from '/@/renderer/api/navidrome/navidrome-types';
import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
import semverCoerce from 'semver/functions/coerce';
import semverGte from 'semver/functions/gte';
import {
AlbumArtistDetailArgs,
AlbumArtistDetailResponse,
@ -52,7 +50,7 @@ import {
SimilarSongsArgs,
Song,
} from '../types';
import { hasFeature } from '/@/renderer/api/utils';
import { VersionInfo, getFeatures, hasFeature } from '/@/renderer/api/utils';
import { ServerFeature, ServerFeatures } from '/@/renderer/api/features-types';
import { SubsonicExtensions } from '/@/renderer/api/subsonic/subsonic-types';
import { NDSongListSort } from '/@/renderer/api/navidrome.types';
@ -486,37 +484,11 @@ const removeFromPlaylist = async (
return null;
};
// The order should be in decreasing version, as the highest version match
// will automatically consider all lower versions matched
const VERSION_INFO: Array<[string, Record<string, number[]>]> = [
const VERSION_INFO: VersionInfo = [
['0.49.3', { [ServerFeature.SHARING_ALBUM_SONG]: [1] }],
['0.48.0', { [ServerFeature.PLAYLISTS_SMART]: [1] }],
];
const getFeatures = (version: string): Record<string, number[]> => {
const cleanVersion = semverCoerce(version);
const features: Record<string, number[]> = {};
let matched = cleanVersion === null;
for (const [version, supportedFeatures] of VERSION_INFO) {
if (!matched) {
matched = semverGte(cleanVersion!, version);
}
if (matched) {
for (const [feature, feat] of Object.entries(supportedFeatures)) {
if (feature in features) {
features[feature].push(...feat);
} else {
features[feature] = feat;
}
}
}
}
return features;
};
const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const { apiClientProps } = args;
@ -527,7 +499,10 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
throw new Error('Failed to ping server');
}
const navidromeFeatures: Record<string, number[]> = getFeatures(ping.body.serverVersion!);
const navidromeFeatures: Record<string, number[]> = getFeatures(
VERSION_INFO,
ping.body.serverVersion!,
);
if (ping.body.openSubsonic) {
const res = await ssApiClient(apiClientProps).getServerInfo();

View file

@ -1,4 +1,6 @@
import { AxiosHeaders } from 'axios';
import semverCoerce from 'semver/functions/coerce';
import semverGte from 'semver/functions/gte';
import { z } from 'zod';
import { toast } from '/@/renderer/components';
import { useAuthStore } from '/@/renderer/store';
@ -48,4 +50,53 @@ export const hasFeature = (server: ServerListItem | null, feature: ServerFeature
return server.features[feature] ?? false;
};
export type VersionInfo = ReadonlyArray<[string, Record<string, readonly number[]>]>;
/**
* Returns the available server features given the version string.
* @param versionInfo a list, in DECREASING VERSION order, of the features supported by the server.
* The first version match will automatically consider the rest matched.
* @example
* ```
* // The CORRECT way to order
* const VERSION_INFO: VersionInfo = [
* ['0.49.3', { [ServerFeature.SHARING_ALBUM_SONG]: [1] }],
* ['0.48.0', { [ServerFeature.PLAYLISTS_SMART]: [1] }],
* ];
* // INCORRECT way to order
* const VERSION_INFO: VersionInfo = [
* ['0.48.0', { [ServerFeature.PLAYLISTS_SMART]: [1] }],
* ['0.49.3', { [ServerFeature.SHARING_ALBUM_SONG]: [1] }],
* ];
* ```
* @param version the version string (SemVer)
* @returns a Record containing the matched features (if any) and their versions
*/
export const getFeatures = (
versionInfo: VersionInfo,
version: string,
): Record<string, number[]> => {
const cleanVersion = semverCoerce(version);
const features: Record<string, number[]> = {};
let matched = cleanVersion === null;
for (const [version, supportedFeatures] of versionInfo) {
if (!matched) {
matched = semverGte(cleanVersion!, version);
}
if (matched) {
for (const [feature, feat] of Object.entries(supportedFeatures)) {
if (feature in features) {
features[feature].push(...feat);
} else {
features[feature] = [...feat];
}
}
}
}
return features;
};
export const SEPARATOR_STRING = ' · ';