ND >= 0.56.0: search songs by artist | album artist id

This commit is contained in:
Kendall Garner 2025-09-27 20:00:34 -07:00
parent c448352ec8
commit 508013958f
No known key found for this signature in database
GPG key ID: 9355F387FE765C94
4 changed files with 25 additions and 37 deletions

View file

@ -4,7 +4,6 @@ import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller
import { NDSongListSort } from '/@/shared/api/navidrome.types';
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
import {
albumArtistListSortMap,
@ -21,10 +20,11 @@ import {
sortOrderMap,
userListSortMap,
} from '/@/shared/types/domain-types';
import { ServerFeature, ServerFeatures } from '/@/shared/types/features-types';
import { ServerFeature } from '/@/shared/types/features-types';
const VERSION_INFO: VersionInfo = [
['0.55.0', { [ServerFeature.BFR]: [1] }],
['0.56.0', { [ServerFeature.TRACK_ALBUM_ARTIST_SEARCH]: [1] }],
['0.55.0', { [ServerFeature.BFR]: [1], [ServerFeature.TAGS]: [1] }],
['0.49.3', { [ServerFeature.SHARING_ALBUM_SONG]: [1] }],
['0.48.0', { [ServerFeature.PLAYLISTS_SMART]: [1] }],
];
@ -55,6 +55,9 @@ const excludeMissing = (server: null | ServerListItem) => {
return undefined;
};
const getArtistSongKey = (server: null | ServerListItem) =>
hasFeature(server, ServerFeature.TRACK_ALBUM_ARTIST_SEARCH) ? 'artists_id' : 'album_artist_id';
export const NavidromeController: ControllerEndpoint = {
addToPlaylist: async (args) => {
const { apiClientProps, body, query } = args;
@ -463,38 +466,20 @@ export const NavidromeController: ControllerEndpoint = {
ping.body.serverVersion = '0.55.0';
}
const navidromeFeatures: Record<string, number[]> = getFeatures(
VERSION_INFO,
ping.body.serverVersion!,
);
const navidromeFeatures = getFeatures(VERSION_INFO, ping.body.serverVersion!);
const subsonicArgs = await SubsonicController.getServerInfo(args);
if (ping.body.openSubsonic) {
const res = await ssApiClient(apiClientProps).getServerInfo();
if (res.status !== 200) {
throw new Error('Failed to get server extensions');
}
// 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 = {
bfr: navidromeFeatures[ServerFeature.BFR],
lyricsMultipleStructured: navidromeFeatures[SubsonicExtensions.SONG_LYRICS],
playlistsSmart: navidromeFeatures[ServerFeature.PLAYLISTS_SMART],
const features = {
...navidromeFeatures,
...subsonicArgs.features,
publicPlaylist: [1],
sharingAlbumSong: navidromeFeatures[ServerFeature.SHARING_ALBUM_SONG],
tags: navidromeFeatures[ServerFeature.BFR],
};
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
return {
features,
id: apiClientProps.server?.id,
version: ping.body.serverVersion!,
};
},
getSimilarSongs: async (args) => {
const { apiClientProps, query } = args;
@ -531,7 +516,7 @@ export const NavidromeController: ControllerEndpoint = {
_order: 'ASC',
_sort: NDSongListSort.RANDOM,
_start: 0,
album_artist_id: query.albumArtistIds,
[getArtistSongKey(apiClientProps.server)]: query.albumArtistIds,
...excludeMissing(apiClientProps.server),
},
});
@ -572,10 +557,9 @@ export const NavidromeController: ControllerEndpoint = {
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: songListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
album_artist_id: query.albumArtistIds,
album_id: query.albumIds,
artist_id: query.artistIds,
genre_id: query.genreIds,
[getArtistSongKey(apiClientProps.server)]: query.artistIds ?? query.albumArtistIds,
starred: query.favorite,
title: query.searchTerm,
...query._custom?.navidrome,

View file

@ -251,6 +251,7 @@ const songListParameters = paginationParameters.extend({
album_artist_id: z.array(z.string()).optional(),
album_id: z.array(z.string()).optional(),
artist_id: z.array(z.string()).optional(),
artists_id: z.array(z.string()).optional(),
genre_id: z.array(z.string()).optional(),
path: z.string().optional(),
starred: z.boolean().optional(),

View file

@ -36,7 +36,9 @@ export const hasFeature = (server: null | ServerListItem, feature: ServerFeature
return (server.features[feature]?.length || 0) > 0;
};
export type VersionInfo = ReadonlyArray<[string, Record<string, readonly number[]>]>;
export type VersionInfo = ReadonlyArray<
[string, Partial<Record<ServerFeature, readonly number[]>>]
>;
/**
* Returns the available server features given the version string.
@ -61,9 +63,9 @@ export type VersionInfo = ReadonlyArray<[string, Record<string, readonly number[
export const getFeatures = (
versionInfo: VersionInfo,
version: string,
): Record<string, number[]> => {
): Partial<Record<ServerFeature, number[]>> => {
const cleanVersion = semverCoerce(version);
const features: Record<string, number[]> = {};
const features: Partial<Record<ServerFeature, number[]>> = {};
let matched = cleanVersion === null;
for (const [version, supportedFeatures] of versionInfo) {

View file

@ -8,6 +8,7 @@ export enum ServerFeature {
PUBLIC_PLAYLIST = 'publicPlaylist',
SHARING_ALBUM_SONG = 'sharingAlbumSong',
TAGS = 'tags',
TRACK_ALBUM_ARTIST_SEARCH = 'trackAlbumArtistSearch',
}
export type ServerFeatures = Partial<Record<ServerFeature, number[]>>;