mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
add has_rating filter for Navidrome song list
This commit is contained in:
@@ -30,6 +30,7 @@ import { ServerFeature } from '/@/shared/types/features-types';
|
||||
const VERSION_INFO: VersionInfo = [
|
||||
// Why 2? Subsonic controller will return 1 for its own implementation
|
||||
// Use 2 to denote that Navidrome's own API has a different endpoint
|
||||
['0.60.4', { [ServerFeature.TRACK_YES_NO_RATING_FILTER]: [1] }],
|
||||
['0.57.0', { [ServerFeature.SERVER_PLAY_QUEUE]: [2] }],
|
||||
['0.56.0', { [ServerFeature.TRACK_ALBUM_ARTIST_SEARCH]: [1] }],
|
||||
['0.55.0', { [ServerFeature.BFR]: [1], [ServerFeature.TAGS]: [1] }],
|
||||
@@ -669,6 +670,7 @@ export const NavidromeController: InternalControllerEndpoint = {
|
||||
...subsonicArgs.features,
|
||||
...navidromeFeatures,
|
||||
publicPlaylist: [1],
|
||||
[ServerFeature.ALBUM_YES_NO_RATING_FILTER]: [1],
|
||||
[ServerFeature.MUSIC_FOLDER_MULTISELECT]: [1],
|
||||
};
|
||||
|
||||
@@ -741,6 +743,10 @@ export const NavidromeController: InternalControllerEndpoint = {
|
||||
album_id: query.albumIds,
|
||||
genre_id: query.genreIds,
|
||||
[getArtistSongKey(apiClientProps.server)]: query.artistIds ?? query.albumArtistIds,
|
||||
...(hasFeature(apiClientProps.server, ServerFeature.TRACK_YES_NO_RATING_FILTER) &&
|
||||
query.hasRating !== undefined
|
||||
? { has_rating: query.hasRating }
|
||||
: {}),
|
||||
library_id: getLibraryId(query.musicFolderId),
|
||||
starred: query.favorite,
|
||||
title: query.searchTerm,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { TagFilters } from '/@/renderer/features/shared/components/tag-filter';
|
||||
import { useSongListFilters } from '/@/renderer/features/songs/hooks/use-song-list-filters';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { useAppStore, useAppStoreActions } from '/@/renderer/store/app.store';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { VirtualMultiSelect } from '/@/shared/components/multi-select/virtual-multi-select';
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ServerFeature } from '/@/shared/types/features-types';
|
||||
|
||||
interface NavidromeSongFiltersProps {
|
||||
disableArtistFilter?: boolean;
|
||||
@@ -40,8 +42,18 @@ export const NavidromeSongFilters = ({
|
||||
const { t } = useTranslation();
|
||||
const server = useCurrentServer();
|
||||
const serverId = server.id;
|
||||
const { query, setArtistIds, setCustom, setFavorite, setGenreId, setMaxYear, setMinYear } =
|
||||
useSongListFilters();
|
||||
const {
|
||||
query,
|
||||
setArtistIds,
|
||||
setCustom,
|
||||
setFavorite,
|
||||
setGenreId,
|
||||
setHasRating,
|
||||
setMaxYear,
|
||||
setMinYear,
|
||||
} = useSongListFilters();
|
||||
|
||||
const showRatingFilter = hasFeature(server, ServerFeature.TRACK_YES_NO_RATING_FILTER);
|
||||
|
||||
const genreListQuery = useQuery(
|
||||
genresQueries.list({
|
||||
@@ -278,6 +290,25 @@ export const NavidromeSongFilters = ({
|
||||
w="100%"
|
||||
/>
|
||||
</Stack>
|
||||
{showRatingFilter && (
|
||||
<>
|
||||
<Divider my="md" />
|
||||
<Stack gap="xs">
|
||||
<Text size="sm" weight={500}>
|
||||
{t('filter.isRated', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
<SegmentedControl
|
||||
data={segmentedControlData}
|
||||
onChange={(value) => {
|
||||
setHasRating(segmentValueToBoolean(value));
|
||||
}}
|
||||
size="sm"
|
||||
value={booleanToSegmentValue(query.hasRating)}
|
||||
w="100%"
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
{!disableArtistFilter && (
|
||||
<>
|
||||
<Divider my="md" />
|
||||
|
||||
@@ -53,6 +53,11 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
||||
[searchParams],
|
||||
);
|
||||
|
||||
const hasRating = useMemo(
|
||||
() => parseBooleanParam(searchParams, FILTER_KEYS.SONG.HAS_RATING),
|
||||
[searchParams],
|
||||
);
|
||||
|
||||
const custom = useMemo(
|
||||
() => parseCustomFiltersParam(searchParams, FILTER_KEYS.SONG._CUSTOM),
|
||||
[searchParams],
|
||||
@@ -103,6 +108,15 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
const setHasRating = useCallback(
|
||||
(value: boolean | null) => {
|
||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.HAS_RATING, value), {
|
||||
replace: true,
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
const setCustom = useCallback(
|
||||
(
|
||||
value:
|
||||
@@ -142,6 +156,7 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
||||
[FILTER_KEYS.SONG.ARTIST_IDS]: null,
|
||||
[FILTER_KEYS.SONG.FAVORITE]: null,
|
||||
[FILTER_KEYS.SONG.GENRE_ID]: null,
|
||||
[FILTER_KEYS.SONG.HAS_RATING]: null,
|
||||
[FILTER_KEYS.SONG.MAX_YEAR]: null,
|
||||
[FILTER_KEYS.SONG.MIN_YEAR]: null,
|
||||
},
|
||||
@@ -160,10 +175,22 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
||||
[FILTER_KEYS.SONG.ARTIST_IDS]: artistIds ?? undefined,
|
||||
[FILTER_KEYS.SONG.FAVORITE]: favorite ?? undefined,
|
||||
[FILTER_KEYS.SONG.GENRE_ID]: genreId ?? undefined,
|
||||
[FILTER_KEYS.SONG.HAS_RATING]: hasRating ?? undefined,
|
||||
[FILTER_KEYS.SONG.MAX_YEAR]: maxYear ?? undefined,
|
||||
[FILTER_KEYS.SONG.MIN_YEAR]: minYear ?? undefined,
|
||||
}),
|
||||
[searchTerm, sortBy, sortOrder, custom, artistIds, favorite, genreId, maxYear, minYear],
|
||||
[
|
||||
searchTerm,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
custom,
|
||||
artistIds,
|
||||
favorite,
|
||||
genreId,
|
||||
hasRating,
|
||||
maxYear,
|
||||
minYear,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -173,6 +200,7 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
||||
setCustom,
|
||||
setFavorite,
|
||||
setGenreId,
|
||||
setHasRating,
|
||||
setMaxYear,
|
||||
setMinYear,
|
||||
setSearchTerm,
|
||||
|
||||
@@ -580,6 +580,7 @@ const songListParameters = paginationParameters.extend({
|
||||
artist_id: z.array(z.string()).optional(),
|
||||
artists_id: z.array(z.string()).optional(),
|
||||
genre_id: z.array(z.string()).optional(),
|
||||
has_rating: z.boolean().optional(),
|
||||
library_id: z.array(z.string()).optional(),
|
||||
path: z.string().optional(),
|
||||
starred: z.boolean().optional(),
|
||||
|
||||
@@ -623,6 +623,7 @@ export interface SongListQuery extends BaseQuery<SongListSort> {
|
||||
artistIds?: string[];
|
||||
favorite?: boolean;
|
||||
genreIds?: string[];
|
||||
hasRating?: boolean;
|
||||
imageSize?: number;
|
||||
limit?: number;
|
||||
maxYear?: number;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Should follow a strict naming convention: "<FEATURE GROUP>_<FEATURE NAME>"
|
||||
// For example: <FEATURE GROUP>: "Playlists", <FEATURE NAME>: "Smart" = "PLAYLISTS_SMART"
|
||||
export enum ServerFeature {
|
||||
ALBUM_YES_NO_RATING_FILTER = 'albumYesNoRatingFilter',
|
||||
BFR = 'bfr',
|
||||
LYRICS_MULTIPLE_STRUCTURED = 'lyricsMultipleStructured',
|
||||
LYRICS_SINGLE_STRUCTURED = 'lyricsSingleStructured',
|
||||
@@ -13,6 +14,7 @@ export enum ServerFeature {
|
||||
SIMILAR_SONGS_MUSIC_FOLDER = 'similarSongsMusicFolder',
|
||||
TAGS = 'tags',
|
||||
TRACK_ALBUM_ARTIST_SEARCH = 'trackAlbumArtistSearch',
|
||||
TRACK_YES_NO_RATING_FILTER = 'trackYesNoRatingFilter',
|
||||
}
|
||||
|
||||
export type ServerFeatures = Partial<Record<ServerFeature, number[]>>;
|
||||
|
||||
Reference in New Issue
Block a user