mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 10:23:33 +00:00
add navidrome version check for smart playlists
This commit is contained in:
parent
46cc1a635f
commit
aba7cb302f
11 changed files with 201 additions and 212 deletions
|
|
@ -173,7 +173,7 @@ const endpoints: ApiController = {
|
|||
getPlaylistList: ndController.getPlaylistList,
|
||||
getPlaylistSongList: ndController.getPlaylistSongList,
|
||||
getRandomSongList: ssController.getRandomSongList,
|
||||
getServerInfo: ssController.getServerInfo,
|
||||
getServerInfo: ndController.getServerInfo,
|
||||
getSongDetail: ndController.getSongDetail,
|
||||
getSongList: ndController.getSongList,
|
||||
getStructuredLyrics: ssController.getStructuredLyrics,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
|
||||
import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize';
|
||||
import { NavidromeExtensions, 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,
|
||||
|
|
@ -39,11 +45,10 @@ import {
|
|||
RemoveFromPlaylistResponse,
|
||||
RemoveFromPlaylistArgs,
|
||||
genreListSortMap,
|
||||
ServerInfo,
|
||||
ServerInfoArgs,
|
||||
} from '../types';
|
||||
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 { hasFeature } from '/@/renderer/api/utils';
|
||||
|
||||
const authenticate = async (
|
||||
url: string,
|
||||
|
|
@ -355,6 +360,16 @@ const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistR
|
|||
|
||||
const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const customQuery = query._custom?.navidrome;
|
||||
|
||||
// Smart playlists only became available in 0.48.0. Do not filter for previous versions
|
||||
if (
|
||||
customQuery &&
|
||||
customQuery.smart !== undefined &&
|
||||
!hasFeature(apiClientProps.server, NavidromeExtensions.SMART_PLAYLISTS)
|
||||
) {
|
||||
customQuery.smart = undefined;
|
||||
}
|
||||
|
||||
const res = await ndApiClient(apiClientProps).getPlaylistList({
|
||||
query: {
|
||||
|
|
@ -363,7 +378,7 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResp
|
|||
_sort: query.sortBy ? playlistListSortMap.navidrome[query.sortBy] : undefined,
|
||||
_start: query.startIndex,
|
||||
q: query.searchTerm,
|
||||
...query._custom?.navidrome,
|
||||
...customQuery,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -465,6 +480,61 @@ const removeFromPlaylist = async (
|
|||
return null;
|
||||
};
|
||||
|
||||
const VERSION_INFO: Array<[string, Record<string, number[]>]> = [
|
||||
['0.48.0', { [NavidromeExtensions.SMART_PLAYLISTS]: [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;
|
||||
|
||||
// Navidrome will always populate serverVersion
|
||||
const ping = await ssApiClient(apiClientProps).ping();
|
||||
|
||||
if (ping.status !== 200) {
|
||||
throw new Error('Failed to ping server');
|
||||
}
|
||||
|
||||
const features: Record<string, number[]> = getFeatures(ping.body.serverVersion!);
|
||||
|
||||
if (ping.body.openSubsonic) {
|
||||
const res = await ssApiClient(apiClientProps).getServerInfo();
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get server extensions');
|
||||
}
|
||||
|
||||
for (const extension of res.body.openSubsonicExtensions) {
|
||||
features[extension.name] = extension.versions;
|
||||
}
|
||||
}
|
||||
|
||||
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
|
||||
};
|
||||
|
||||
export const ndController = {
|
||||
addToPlaylist,
|
||||
authenticate,
|
||||
|
|
@ -478,6 +548,7 @@ export const ndController = {
|
|||
getPlaylistDetail,
|
||||
getPlaylistList,
|
||||
getPlaylistSongList,
|
||||
getServerInfo,
|
||||
getSongDetail,
|
||||
getSongList,
|
||||
getUserList,
|
||||
|
|
|
|||
|
|
@ -342,6 +342,10 @@ const removeFromPlaylistParameters = z.object({
|
|||
id: z.array(z.string()),
|
||||
});
|
||||
|
||||
export enum NavidromeExtensions {
|
||||
SMART_PLAYLISTS = 'smartPlaylists',
|
||||
}
|
||||
|
||||
export const ndType = {
|
||||
_enum: {
|
||||
albumArtistList: ndAlbumArtistListSort,
|
||||
|
|
|
|||
|
|
@ -57,13 +57,16 @@ export type User = {
|
|||
|
||||
export type ServerListItem = {
|
||||
credential: string;
|
||||
features?: Record<string, number[]>;
|
||||
id: string;
|
||||
name: string;
|
||||
ndCredential?: string;
|
||||
savePassword?: boolean;
|
||||
type: ServerType;
|
||||
url: string;
|
||||
userId: string | null;
|
||||
username: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
export enum ServerType {
|
||||
|
|
|
|||
|
|
@ -38,3 +38,20 @@ export const authenticationFailure = (currentServer: ServerListItem | null) => {
|
|||
useAuthStore.getState().actions.setCurrentServer(null);
|
||||
}
|
||||
};
|
||||
|
||||
export const hasFeature = (
|
||||
server: ServerListItem | null,
|
||||
feature: string,
|
||||
version = 1,
|
||||
): boolean => {
|
||||
if (!server || !server.features) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const versions = server.features[feature];
|
||||
if (!versions || versions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return versions.includes(version);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue