mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 18:13:31 +00:00
add multiple genre support for nd albums/tracks
This commit is contained in:
parent
6df270ba34
commit
4a48598260
4 changed files with 77 additions and 20 deletions
|
|
@ -271,6 +271,10 @@ export const NavidromeController: ControllerEndpoint = {
|
|||
getAlbumList: async (args) => {
|
||||
const { apiClientProps, query } = args;
|
||||
|
||||
const genres = hasFeature(apiClientProps.server, ServerFeature.BFR)
|
||||
? query.genres
|
||||
: query.genres?.[0];
|
||||
|
||||
const res = await ndApiClient(apiClientProps).getAlbumList({
|
||||
query: {
|
||||
_end: query.startIndex + (query.limit || 0),
|
||||
|
|
@ -279,7 +283,7 @@ export const NavidromeController: ControllerEndpoint = {
|
|||
_start: query.startIndex,
|
||||
artist_id: query.artistIds?.[0],
|
||||
compilation: query.compilation,
|
||||
genre_id: query.genres?.[0],
|
||||
genre_id: genres,
|
||||
name: query.searchTerm,
|
||||
...query._custom?.navidrome,
|
||||
starred: query.favorite,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,21 @@ import debounce from 'lodash/debounce';
|
|||
import { ChangeEvent, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SelectWithInvalidData } from '/@/renderer/components/select-with-invalid-data';
|
||||
import {
|
||||
MultiSelectWithInvalidData,
|
||||
SelectWithInvalidData,
|
||||
} from '/@/renderer/components/select-with-invalid-data';
|
||||
import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { useTagList } from '/@/renderer/features/tag/queries/use-tag-list';
|
||||
import { AlbumListFilter, useListStoreActions, useListStoreByKey } from '/@/renderer/store';
|
||||
import {
|
||||
AlbumListFilter,
|
||||
getServerById,
|
||||
useListStoreActions,
|
||||
useListStoreByKey,
|
||||
} from '/@/renderer/store';
|
||||
import { NDSongQueryFields } from '/@/shared/api/navidrome.types';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
|
|
@ -23,6 +32,7 @@ import {
|
|||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ServerFeature } from '/@/shared/types/features-types';
|
||||
|
||||
interface NavidromeAlbumFiltersProps {
|
||||
customFilters?: Partial<AlbumListFilter>;
|
||||
|
|
@ -42,6 +52,7 @@ export const NavidromeAlbumFilters = ({
|
|||
const { t } = useTranslation();
|
||||
const { filter } = useListStoreByKey<AlbumListQuery>({ key: pageKey });
|
||||
const { setFilter } = useListStoreActions();
|
||||
const server = getServerById(serverId);
|
||||
|
||||
const genreListQuery = useGenreList({
|
||||
options: {
|
||||
|
|
@ -64,12 +75,14 @@ export const NavidromeAlbumFilters = ({
|
|||
}));
|
||||
}, [genreListQuery.data]);
|
||||
|
||||
const handleGenresFilter = debounce((e: null | string) => {
|
||||
const hasBrf = hasFeature(server, ServerFeature.BFR);
|
||||
|
||||
const handleGenresFilter = debounce((e: null | string[]) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: filter._custom,
|
||||
genres: e ? [e] : undefined,
|
||||
genres: e ? e : undefined,
|
||||
},
|
||||
itemType: LibraryItem.ALBUM,
|
||||
key: pageKey,
|
||||
|
|
@ -269,15 +282,29 @@ export const NavidromeAlbumFilters = ({
|
|||
min={0}
|
||||
onChange={(e) => handleYearFilter(e)}
|
||||
/>
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
data={genreList}
|
||||
defaultValue={filter.genres && filter.genres[0]}
|
||||
label={t('entity.genre', { count: 1, postProcess: 'titleCase' })}
|
||||
onChange={handleGenresFilter}
|
||||
searchable
|
||||
/>
|
||||
{!hasBrf && (
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
data={genreList}
|
||||
defaultValue={filter.genres && filter.genres[0]}
|
||||
label={t('entity.genre', { count: 1, postProcess: 'titleCase' })}
|
||||
onChange={(value) => handleGenresFilter(value !== null ? [value] : null)}
|
||||
searchable
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
{hasBrf && (
|
||||
<Group grow>
|
||||
<MultiSelectWithInvalidData
|
||||
clearable
|
||||
data={genreList}
|
||||
defaultValue={filter.genres}
|
||||
label={t('entity.genre', { count: 2, postProcess: 'sentenceCase' })}
|
||||
onChange={handleGenresFilter}
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
<Group grow>
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
|
|
|
|||
|
|
@ -2,11 +2,20 @@ import debounce from 'lodash/debounce';
|
|||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SelectWithInvalidData } from '/@/renderer/components/select-with-invalid-data';
|
||||
import {
|
||||
MultiSelectWithInvalidData,
|
||||
SelectWithInvalidData,
|
||||
} from '/@/renderer/components/select-with-invalid-data';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { useTagList } from '/@/renderer/features/tag/queries/use-tag-list';
|
||||
import { SongListFilter, useListFilterByKey, useListStoreActions } from '/@/renderer/store';
|
||||
import {
|
||||
getServerById,
|
||||
SongListFilter,
|
||||
useListFilterByKey,
|
||||
useListStoreActions,
|
||||
} from '/@/renderer/store';
|
||||
import { NDSongQueryFields } from '/@/shared/api/navidrome.types';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
|
|
@ -14,6 +23,7 @@ import { Stack } from '/@/shared/components/stack/stack';
|
|||
import { Text } from '/@/shared/components/text/text';
|
||||
import { YesNoSelect } from '/@/shared/components/yes-no-select/yes-no-select';
|
||||
import { GenreListSort, LibraryItem, SongListQuery, SortOrder } from '/@/shared/types/domain-types';
|
||||
import { ServerFeature } from '/@/shared/types/features-types';
|
||||
|
||||
interface NavidromeSongFiltersProps {
|
||||
customFilters?: Partial<SongListFilter>;
|
||||
|
|
@ -31,6 +41,7 @@ export const NavidromeSongFilters = ({
|
|||
const { t } = useTranslation();
|
||||
const { setFilter } = useListStoreActions();
|
||||
const filter = useListFilterByKey<SongListQuery>({ key: pageKey });
|
||||
const server = getServerById(serverId);
|
||||
|
||||
const isGenrePage = customFilters?.genreIds !== undefined;
|
||||
|
||||
|
|
@ -58,12 +69,14 @@ export const NavidromeSongFilters = ({
|
|||
}));
|
||||
}, [genreListQuery.data]);
|
||||
|
||||
const handleGenresFilter = debounce((e: null | string) => {
|
||||
const hasBrf = hasFeature(server, ServerFeature.BFR);
|
||||
|
||||
const handleGenresFilter = debounce((e: null | string[]) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: filter._custom,
|
||||
genreIds: e ? [e] : undefined,
|
||||
genreIds: e ? e : undefined,
|
||||
},
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
|
|
@ -148,18 +161,30 @@ export const NavidromeSongFilters = ({
|
|||
value={filter._custom?.navidrome?.year}
|
||||
width={50}
|
||||
/>
|
||||
{!isGenrePage && (
|
||||
{!isGenrePage && !hasBrf && (
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
data={genreList}
|
||||
defaultValue={filter.genreIds ? filter.genreIds[0] : undefined}
|
||||
label={t('entity.genre', { count: 1, postProcess: 'titleCase' })}
|
||||
onChange={handleGenresFilter}
|
||||
onChange={(value) => handleGenresFilter(value !== null ? [value] : null)}
|
||||
searchable
|
||||
width={150}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
{!isGenrePage && hasBrf && (
|
||||
<Group grow>
|
||||
<MultiSelectWithInvalidData
|
||||
clearable
|
||||
data={genreList}
|
||||
defaultValue={filter.genreIds}
|
||||
label={t('entity.genre', { count: 2, postProcess: 'sentenceCase' })}
|
||||
onChange={handleGenresFilter}
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
{tagsQuery.data?.enumTags?.length &&
|
||||
tagsQuery.data.enumTags.length > 0 &&
|
||||
tagsQuery.data.enumTags.map((tag) => (
|
||||
|
|
|
|||
|
|
@ -169,7 +169,8 @@ const albumListParameters = paginationParameters.extend({
|
|||
album_id: z.string().optional(),
|
||||
artist_id: z.string().optional(),
|
||||
compilation: z.boolean().optional(),
|
||||
genre_id: z.string().optional(),
|
||||
// in older versions, this was a single string. post BFR, you can repeat it multiple times
|
||||
genre_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
has_rating: z.boolean().optional(),
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue