mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
support limitPercent for smart playlists
This commit is contained in:
@@ -72,6 +72,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
|
|||||||
? {
|
? {
|
||||||
...convertQueryGroupToNDQuery(smartPlaylist.filters),
|
...convertQueryGroupToNDQuery(smartPlaylist.filters),
|
||||||
limit: smartPlaylist.extraFilters.limit,
|
limit: smartPlaylist.extraFilters.limit,
|
||||||
|
limitPercent: smartPlaylist.extraFilters.limitPercent,
|
||||||
// order field is now optional - sort direction is embedded in sort field
|
// order field is now optional - sort direction is embedded in sort field
|
||||||
sort: sortValue || '+dateAdded',
|
sort: sortValue || '+dateAdded',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { Flex } from '/@/shared/components/flex/flex';
|
|||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||||
|
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
||||||
import { Select } from '/@/shared/components/select/select';
|
import { Select } from '/@/shared/components/select/select';
|
||||||
import { Stack } from '/@/shared/components/stack/stack';
|
import { Stack } from '/@/shared/components/stack/stack';
|
||||||
import { useForm } from '/@/shared/hooks/use-form';
|
import { useForm } from '/@/shared/hooks/use-form';
|
||||||
@@ -51,6 +52,7 @@ type DeleteArgs = {
|
|||||||
|
|
||||||
interface PlaylistQueryBuilderProps {
|
interface PlaylistQueryBuilderProps {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
playlistId?: string;
|
playlistId?: string;
|
||||||
query: any;
|
query: any;
|
||||||
sortBy: SongListSort | SongListSort[];
|
sortBy: SongListSort | SongListSort[];
|
||||||
@@ -155,6 +157,7 @@ export type PlaylistQueryBuilderRef = {
|
|||||||
getFilters: () => {
|
getFilters: () => {
|
||||||
extraFilters: {
|
extraFilters: {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
sortBy?: string[];
|
sortBy?: string[];
|
||||||
sortOrder?: string;
|
sortOrder?: string;
|
||||||
};
|
};
|
||||||
@@ -164,7 +167,7 @@ export type PlaylistQueryBuilderRef = {
|
|||||||
|
|
||||||
export const PlaylistQueryBuilder = forwardRef(
|
export const PlaylistQueryBuilder = forwardRef(
|
||||||
(
|
(
|
||||||
{ limit, playlistId, query, sortBy, sortOrder }: PlaylistQueryBuilderProps,
|
{ limit, limitPercent, playlistId, query, sortBy, sortOrder }: PlaylistQueryBuilderProps,
|
||||||
ref: Ref<PlaylistQueryBuilderRef>,
|
ref: Ref<PlaylistQueryBuilderRef>,
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -213,6 +216,8 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||||||
const extraFiltersForm = useForm({
|
const extraFiltersForm = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
limit,
|
limit,
|
||||||
|
limitMode: limitPercent != null ? 'limitPercent' : 'limit',
|
||||||
|
limitPercent,
|
||||||
sortEntries: initialSortEntries,
|
sortEntries: initialSortEntries,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -224,16 +229,26 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||||||
const sortString = convertSortEntriesToSortString(
|
const sortString = convertSortEntriesToSortString(
|
||||||
extraFiltersForm.values.sortEntries,
|
extraFiltersForm.values.sortEntries,
|
||||||
);
|
);
|
||||||
|
const isLimitPercent = extraFiltersForm.values.limitMode === 'limitPercent';
|
||||||
return {
|
return {
|
||||||
extraFilters: {
|
extraFilters: {
|
||||||
limit: extraFiltersForm.values.limit,
|
limit: isLimitPercent ? undefined : extraFiltersForm.values.limit,
|
||||||
|
limitPercent: isLimitPercent
|
||||||
|
? extraFiltersForm.values.limitPercent
|
||||||
|
: undefined,
|
||||||
sortBy: sortString ? [sortString] : undefined,
|
sortBy: sortString ? [sortString] : undefined,
|
||||||
},
|
},
|
||||||
filters,
|
filters,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[extraFiltersForm.values.sortEntries, extraFiltersForm.values.limit, filters],
|
[
|
||||||
|
extraFiltersForm.values.sortEntries,
|
||||||
|
extraFiltersForm.values.limit,
|
||||||
|
extraFiltersForm.values.limitMode,
|
||||||
|
extraFiltersForm.values.limitPercent,
|
||||||
|
filters,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleResetFilters = useCallback(() => {
|
const handleResetFilters = useCallback(() => {
|
||||||
@@ -608,10 +623,50 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label={t('common.limit', { postProcess: 'titleCase' })}
|
label={
|
||||||
maxWidth="20%"
|
<Group align="center" gap="xs" wrap="nowrap">
|
||||||
|
{t('common.limit', { postProcess: 'titleCase' })}
|
||||||
|
<SegmentedControl
|
||||||
|
data={[
|
||||||
|
{ label: '#', value: 'limit' },
|
||||||
|
{ label: '%', value: 'limitPercent' },
|
||||||
|
]}
|
||||||
|
onChange={(value) =>
|
||||||
|
extraFiltersForm.setFieldValue(
|
||||||
|
'limitMode',
|
||||||
|
value as 'limit' | 'limitPercent',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size="xs"
|
||||||
|
value={extraFiltersForm.values.limitMode}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
max={
|
||||||
|
extraFiltersForm.values.limitMode === 'limitPercent'
|
||||||
|
? 100
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
min={
|
||||||
|
extraFiltersForm.values.limitMode === 'limitPercent'
|
||||||
|
? 0
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={(value) => {
|
||||||
|
const nextValue =
|
||||||
|
value === '' || value == null ? undefined : Number(value);
|
||||||
|
if (extraFiltersForm.values.limitMode === 'limitPercent') {
|
||||||
|
extraFiltersForm.setFieldValue('limitPercent', nextValue);
|
||||||
|
} else {
|
||||||
|
extraFiltersForm.setFieldValue('limit', nextValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={
|
||||||
|
extraFiltersForm.values.limitMode === 'limitPercent'
|
||||||
|
? extraFiltersForm.values.limitPercent
|
||||||
|
: extraFiltersForm.values.limit
|
||||||
|
}
|
||||||
width={75}
|
width={75}
|
||||||
{...extraFiltersForm.getInputProps('limit')}
|
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -28,11 +28,21 @@ export interface PlaylistQueryEditorProps {
|
|||||||
detailQuery: ReturnType<typeof useQuery<any>>;
|
detailQuery: ReturnType<typeof useQuery<any>>;
|
||||||
handleSave: (
|
handleSave: (
|
||||||
filter: Record<string, any>,
|
filter: Record<string, any>,
|
||||||
extraFilters: { limit?: number; sortBy?: string[]; sortOrder?: string },
|
extraFilters: {
|
||||||
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
|
sortBy?: string[];
|
||||||
|
sortOrder?: string;
|
||||||
|
},
|
||||||
) => void;
|
) => void;
|
||||||
handleSaveAs: (
|
handleSaveAs: (
|
||||||
filter: Record<string, any>,
|
filter: Record<string, any>,
|
||||||
extraFilters: { limit?: number; sortBy?: string[]; sortOrder?: string },
|
extraFilters: {
|
||||||
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
|
sortBy?: string[];
|
||||||
|
sortOrder?: string;
|
||||||
|
},
|
||||||
) => void;
|
) => void;
|
||||||
isQueryBuilderExpanded: boolean;
|
isQueryBuilderExpanded: boolean;
|
||||||
onToggleExpand: () => void;
|
onToggleExpand: () => void;
|
||||||
@@ -43,6 +53,7 @@ export interface PlaylistQueryEditorProps {
|
|||||||
|
|
||||||
type AppliedJsonState = {
|
type AppliedJsonState = {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
query: Record<string, any>;
|
query: Record<string, any>;
|
||||||
sort?: string;
|
sort?: string;
|
||||||
};
|
};
|
||||||
@@ -50,7 +61,7 @@ type AppliedJsonState = {
|
|||||||
type EditorMode = 'builder' | 'json';
|
type EditorMode = 'builder' | 'json';
|
||||||
|
|
||||||
const serializeFiltersToRulesJson = (filters: {
|
const serializeFiltersToRulesJson = (filters: {
|
||||||
extraFilters: { limit?: number; sortBy?: string[] };
|
extraFilters: { limit?: number; limitPercent?: number; sortBy?: string[] };
|
||||||
filters: any;
|
filters: any;
|
||||||
}): Record<string, any> => {
|
}): Record<string, any> => {
|
||||||
const queryValue = convertQueryGroupToNDQuery(filters.filters);
|
const queryValue = convertQueryGroupToNDQuery(filters.filters);
|
||||||
@@ -58,18 +69,25 @@ const serializeFiltersToRulesJson = (filters: {
|
|||||||
return {
|
return {
|
||||||
...queryValue,
|
...queryValue,
|
||||||
...(filters.extraFilters.limit != null && { limit: filters.extraFilters.limit }),
|
...(filters.extraFilters.limit != null && { limit: filters.extraFilters.limit }),
|
||||||
|
...(filters.extraFilters.limitPercent != null && {
|
||||||
|
limitPercent: filters.extraFilters.limitPercent,
|
||||||
|
}),
|
||||||
...(sortString && { sort: sortString }),
|
...(sortString && { sort: sortString }),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseRulesJsonToSaveArgs = (
|
const parseRulesJsonToSaveArgs = (
|
||||||
parsed: Record<string, any>,
|
parsed: Record<string, any>,
|
||||||
): { extraFilters: { limit?: number; sortBy?: string[] }; filter: Record<string, any> } => {
|
): {
|
||||||
|
extraFilters: { limit?: number; limitPercent?: number; sortBy?: string[] };
|
||||||
|
filter: Record<string, any>;
|
||||||
|
} => {
|
||||||
const rootKey = parsed.all ? 'all' : 'any';
|
const rootKey = parsed.all ? 'all' : 'any';
|
||||||
const filter = rootKey in parsed ? { [rootKey]: parsed[rootKey] } : { all: [] };
|
const filter = rootKey in parsed ? { [rootKey]: parsed[rootKey] } : { all: [] };
|
||||||
return {
|
return {
|
||||||
extraFilters: {
|
extraFilters: {
|
||||||
...(parsed.limit != null && { limit: parsed.limit }),
|
...(parsed.limit != null && { limit: parsed.limit }),
|
||||||
|
...(parsed.limitPercent != null && { limitPercent: parsed.limitPercent }),
|
||||||
...(parsed.sort != null && { sortBy: [parsed.sort] }),
|
...(parsed.sort != null && { sortBy: [parsed.sort] }),
|
||||||
},
|
},
|
||||||
filter,
|
filter,
|
||||||
@@ -93,7 +111,12 @@ export const PlaylistQueryEditor = ({
|
|||||||
const [appliedJsonState, setAppliedJsonState] = useState<AppliedJsonState | null>(null);
|
const [appliedJsonState, setAppliedJsonState] = useState<AppliedJsonState | null>(null);
|
||||||
|
|
||||||
const getFiltersForSave = useCallback((): null | {
|
const getFiltersForSave = useCallback((): null | {
|
||||||
extraFilters: { limit?: number; sortBy?: string[]; sortOrder?: string };
|
extraFilters: {
|
||||||
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
|
sortBy?: string[];
|
||||||
|
sortOrder?: string;
|
||||||
|
};
|
||||||
filter: Record<string, any>;
|
filter: Record<string, any>;
|
||||||
} => {
|
} => {
|
||||||
if (editorMode === 'json') {
|
if (editorMode === 'json') {
|
||||||
@@ -124,6 +147,9 @@ export const PlaylistQueryEditor = ({
|
|||||||
const previewValue = {
|
const previewValue = {
|
||||||
...payload.filter,
|
...payload.filter,
|
||||||
...(payload.extraFilters.limit != null && { limit: payload.extraFilters.limit }),
|
...(payload.extraFilters.limit != null && { limit: payload.extraFilters.limit }),
|
||||||
|
...(payload.extraFilters.limitPercent != null && {
|
||||||
|
limitPercent: payload.extraFilters.limitPercent,
|
||||||
|
}),
|
||||||
...(payload.extraFilters.sortBy?.[0] && { sort: payload.extraFilters.sortBy[0] }),
|
...(payload.extraFilters.sortBy?.[0] && { sort: payload.extraFilters.sortBy[0] }),
|
||||||
};
|
};
|
||||||
openModal({
|
openModal({
|
||||||
@@ -208,6 +234,8 @@ export const PlaylistQueryEditor = ({
|
|||||||
[appliedJsonState?.query, detailQuery?.data?.rules],
|
[appliedJsonState?.query, detailQuery?.data?.rules],
|
||||||
);
|
);
|
||||||
const effectiveLimit = appliedJsonState?.limit ?? detailQuery?.data?.rules?.limit;
|
const effectiveLimit = appliedJsonState?.limit ?? detailQuery?.data?.rules?.limit;
|
||||||
|
const effectiveLimitPercent =
|
||||||
|
appliedJsonState?.limitPercent ?? detailQuery?.data?.rules?.limitPercent;
|
||||||
const effectiveSortBy = useMemo(
|
const effectiveSortBy = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(appliedJsonState?.sort ? [appliedJsonState.sort] : parseSortBy()) as
|
(appliedJsonState?.sort ? [appliedJsonState.sort] : parseSortBy()) as
|
||||||
@@ -233,6 +261,8 @@ export const PlaylistQueryEditor = ({
|
|||||||
? { ...effectiveQuery }
|
? { ...effectiveQuery }
|
||||||
: { all: [] };
|
: { all: [] };
|
||||||
if (effectiveLimit != null) fallback.limit = effectiveLimit;
|
if (effectiveLimit != null) fallback.limit = effectiveLimit;
|
||||||
|
if (effectiveLimitPercent != null)
|
||||||
|
fallback.limitPercent = effectiveLimitPercent;
|
||||||
if (effectiveSortBy?.[0]) fallback.sort = effectiveSortBy[0];
|
if (effectiveSortBy?.[0]) fallback.sort = effectiveSortBy[0];
|
||||||
if (!fallback.sort) fallback.sort = '+dateAdded';
|
if (!fallback.sort) fallback.sort = '+dateAdded';
|
||||||
setJsonText(JSON.stringify(fallback, null, 2));
|
setJsonText(JSON.stringify(fallback, null, 2));
|
||||||
@@ -248,6 +278,7 @@ export const PlaylistQueryEditor = ({
|
|||||||
}
|
}
|
||||||
setAppliedJsonState({
|
setAppliedJsonState({
|
||||||
limit: parsed.limit,
|
limit: parsed.limit,
|
||||||
|
limitPercent: parsed.limitPercent,
|
||||||
query: { [rootKey]: parsed[rootKey] },
|
query: { [rootKey]: parsed[rootKey] },
|
||||||
sort: parsed.sort,
|
sort: parsed.sort,
|
||||||
});
|
});
|
||||||
@@ -263,7 +294,16 @@ export const PlaylistQueryEditor = ({
|
|||||||
setEditorMode('builder');
|
setEditorMode('builder');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[editorMode, effectiveLimit, effectiveQuery, effectiveSortBy, jsonText, queryBuilderRef, t],
|
[
|
||||||
|
editorMode,
|
||||||
|
effectiveLimit,
|
||||||
|
effectiveLimitPercent,
|
||||||
|
effectiveQuery,
|
||||||
|
effectiveSortBy,
|
||||||
|
jsonText,
|
||||||
|
queryBuilderRef,
|
||||||
|
t,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -367,6 +407,7 @@ export const PlaylistQueryEditor = ({
|
|||||||
<PlaylistQueryBuilder
|
<PlaylistQueryBuilder
|
||||||
key={JSON.stringify(appliedJsonState ?? detailQuery?.data?.rules)}
|
key={JSON.stringify(appliedJsonState ?? detailQuery?.data?.rules)}
|
||||||
limit={effectiveLimit}
|
limit={effectiveLimit}
|
||||||
|
limitPercent={effectiveLimitPercent}
|
||||||
playlistId={playlistId}
|
playlistId={playlistId}
|
||||||
query={effectiveQuery}
|
query={effectiveQuery}
|
||||||
ref={queryBuilderRef}
|
ref={queryBuilderRef}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
|
|
||||||
const handleSave = (
|
const handleSave = (
|
||||||
filter: Record<string, any>,
|
filter: Record<string, any>,
|
||||||
extraFilters: { limit?: number; sortBy?: string[]; sortOrder?: string },
|
extraFilters: { limit?: number; limitPercent?: number; sortBy?: string[]; sortOrder?: string },
|
||||||
) => {
|
) => {
|
||||||
if (!detailQuery?.data) return;
|
if (!detailQuery?.data) return;
|
||||||
|
|
||||||
@@ -96,7 +96,8 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
...filter,
|
...filter,
|
||||||
limit: extraFilters.limit || undefined,
|
limit: extraFilters.limit ?? undefined,
|
||||||
|
limitPercent: extraFilters.limitPercent ?? undefined,
|
||||||
sort: sortValue,
|
sort: sortValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +124,7 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
|
|
||||||
const handleSaveAs = (
|
const handleSaveAs = (
|
||||||
filter: Record<string, any>,
|
filter: Record<string, any>,
|
||||||
extraFilters: { limit?: number; sortBy?: string[]; sortOrder?: string },
|
extraFilters: { limit?: number; limitPercent?: number; sortBy?: string[]; sortOrder?: string },
|
||||||
) => {
|
) => {
|
||||||
if (!detailQuery?.data) return;
|
if (!detailQuery?.data) return;
|
||||||
|
|
||||||
@@ -134,7 +135,8 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
...filter,
|
...filter,
|
||||||
limit: extraFilters.limit || undefined,
|
limit: extraFilters.limit ?? undefined,
|
||||||
|
limitPercent: extraFilters.limitPercent ?? undefined,
|
||||||
sort: sortValue,
|
sort: sortValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -600,6 +600,14 @@ const songListParameters = paginationParameters.extend({
|
|||||||
year: z.number().optional(),
|
year: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const playlistRules = z
|
||||||
|
.object({
|
||||||
|
limit: z.number().optional(),
|
||||||
|
limitPercent: z.number().optional(),
|
||||||
|
sort: z.string().optional(),
|
||||||
|
})
|
||||||
|
.catchall(z.any());
|
||||||
|
|
||||||
const playlist = z.object({
|
const playlist = z.object({
|
||||||
comment: z.string(),
|
comment: z.string(),
|
||||||
createdAt: z.string(),
|
createdAt: z.string(),
|
||||||
@@ -611,7 +619,7 @@ const playlist = z.object({
|
|||||||
ownerName: z.string(),
|
ownerName: z.string(),
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
public: z.boolean(),
|
public: z.boolean(),
|
||||||
rules: z.record(z.string(), z.any()),
|
rules: playlistRules,
|
||||||
size: z.number(),
|
size: z.number(),
|
||||||
songCount: z.number(),
|
songCount: z.number(),
|
||||||
sync: z.boolean(),
|
sync: z.boolean(),
|
||||||
@@ -643,7 +651,7 @@ const createPlaylistParameters = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
ownerId: z.string().optional(),
|
ownerId: z.string().optional(),
|
||||||
public: z.boolean().optional(),
|
public: z.boolean().optional(),
|
||||||
rules: z.record(z.any()).optional(),
|
rules: playlistRules.optional(),
|
||||||
sync: z.boolean().optional(),
|
sync: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ export type Playlist = {
|
|||||||
owner: null | string;
|
owner: null | string;
|
||||||
ownerId: null | string;
|
ownerId: null | string;
|
||||||
public: boolean | null;
|
public: boolean | null;
|
||||||
rules?: null | Record<string, any>;
|
rules?: null | PlaylistRules;
|
||||||
size: null | number;
|
size: null | number;
|
||||||
songCount: null | number;
|
songCount: null | number;
|
||||||
sync?: boolean | null;
|
sync?: boolean | null;
|
||||||
@@ -947,7 +947,7 @@ export type CreatePlaylistBody = {
|
|||||||
name: string;
|
name: string;
|
||||||
ownerId?: string;
|
ownerId?: string;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
queryBuilderRules?: Record<string, any>;
|
queryBuilderRules?: PlaylistRules;
|
||||||
sync?: boolean;
|
sync?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1009,6 +1009,12 @@ export interface PlaylistListQuery extends BaseQuery<PlaylistListSort> {
|
|||||||
// Playlist List
|
// Playlist List
|
||||||
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]>;
|
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]>;
|
||||||
|
|
||||||
|
export type PlaylistRules = Record<string, any> & {
|
||||||
|
limit?: number;
|
||||||
|
limitPercent?: number;
|
||||||
|
sort?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type RatingQuery = {
|
export type RatingQuery = {
|
||||||
id: string[];
|
id: string[];
|
||||||
rating: number;
|
rating: number;
|
||||||
@@ -1089,7 +1095,7 @@ export type UpdatePlaylistBody = {
|
|||||||
name: string;
|
name: string;
|
||||||
ownerId?: string;
|
ownerId?: string;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
queryBuilderRules?: Record<string, any>;
|
queryBuilderRules?: PlaylistRules;
|
||||||
sync?: boolean;
|
sync?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user