mirror of
https://github.com/jeffvli/feishin.git
synced 2026-08-08 05:12:57 +02:00
feat: adds option to view top rated songs in the Favorite Songs section (#2289)
* Adds Top Rating option songs to Artist's favorite songs section
This commit is contained in:
@@ -535,6 +535,18 @@ export const controller: GeneralController = {
|
|||||||
server.type,
|
server.type,
|
||||||
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
||||||
},
|
},
|
||||||
|
getFavoriteSongs(args) {
|
||||||
|
const server = getServerById(args.apiClientProps.serverId);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
throw new Error(`${i18n.t('error.apiRouteError')}: getFavoriteSongs`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiController(
|
||||||
|
'getFavoriteSongs',
|
||||||
|
server.type,
|
||||||
|
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
||||||
|
},
|
||||||
getFolder(args) {
|
getFolder(args) {
|
||||||
const server = getServerById(args.apiClientProps.serverId);
|
const server = getServerById(args.apiClientProps.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -679,6 +679,43 @@ export const JellyfinController: InternalControllerEndpoint = {
|
|||||||
|
|
||||||
return `${apiClientProps.server?.url}/items/${query.id}/download?apiKey=${apiClientProps.server?.credential}`;
|
return `${apiClientProps.server?.url}/items/${query.id}/download?apiKey=${apiClientProps.server?.credential}`;
|
||||||
},
|
},
|
||||||
|
getFavoriteSongs: async (args) => {
|
||||||
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
|
if (!apiClientProps.server?.userId) {
|
||||||
|
throw new Error('No userId found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets songs sorted by play count and filters favorited songs
|
||||||
|
const res = await jfApiClient(apiClientProps).getTopSongsList({
|
||||||
|
params: {
|
||||||
|
userId: apiClientProps.server?.userId,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
ArtistIds: query.artistId,
|
||||||
|
Fields: JF_FIELDS.SONG,
|
||||||
|
IncludeItemTypes: 'Audio',
|
||||||
|
IsFavorite: true,
|
||||||
|
Limit: query.limit,
|
||||||
|
Recursive: true,
|
||||||
|
SortBy: JFSongListSort.PLAY_COUNT,
|
||||||
|
SortOrder: 'Descending',
|
||||||
|
UserId: apiClientProps.server?.userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error('Failed to get top song list');
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server));
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.body.TotalRecordCount,
|
||||||
|
};
|
||||||
|
},
|
||||||
getFolder: async (args) => {
|
getFolder: async (args) => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
const userId = apiClientProps.server?.userId;
|
const userId = apiClientProps.server?.userId;
|
||||||
|
|||||||
@@ -557,6 +557,57 @@ export const NavidromeController: InternalControllerEndpoint = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
getDownloadUrl: SubsonicController.getDownloadUrl,
|
getDownloadUrl: SubsonicController.getDownloadUrl,
|
||||||
|
getFavoriteSongs: async (args) => {
|
||||||
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
|
// if user selects 'rating'
|
||||||
|
if (query.type === 'rating') {
|
||||||
|
const res = await NavidromeController.getSongList({
|
||||||
|
apiClientProps,
|
||||||
|
query: {
|
||||||
|
artistIds: [query.artistId],
|
||||||
|
sortBy: SongListSort.RATING,
|
||||||
|
sortOrder: SortOrder.DESC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const songsWithHighRating = orderBy(
|
||||||
|
res.items.filter((song) => song.userRating !== null && song.userRating > 2),
|
||||||
|
['userRating', 'userFavorite', 'playCount', 'albumId', 'trackNumber'],
|
||||||
|
['desc', 'desc', 'desc', 'asc', 'asc'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: songsWithHighRating,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.totalRecordCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// else if user selects 'favorite'
|
||||||
|
const res = await NavidromeController.getSongList({
|
||||||
|
apiClientProps,
|
||||||
|
query: {
|
||||||
|
artistIds: [query.artistId],
|
||||||
|
sortBy: SongListSort.FAVORITED,
|
||||||
|
sortOrder: SortOrder.DESC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const songsWithFavorite = orderBy(
|
||||||
|
res.items.filter((song) => song.userFavorite),
|
||||||
|
['userFavorite', 'userRating', 'playCount', 'albumId', 'trackNumber'],
|
||||||
|
['desc', 'desc', 'desc', 'asc', 'asc'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: songsWithFavorite,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.totalRecordCount,
|
||||||
|
};
|
||||||
|
},
|
||||||
getFolder: SubsonicController.getFolder,
|
getFolder: SubsonicController.getFolder,
|
||||||
getGenreList: async (args) => {
|
getGenreList: async (args) => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
AlbumRadioQuery,
|
AlbumRadioQuery,
|
||||||
ArtistListQuery,
|
ArtistListQuery,
|
||||||
ArtistRadioQuery,
|
ArtistRadioQuery,
|
||||||
|
FavoriteSongListQuery,
|
||||||
FolderQuery,
|
FolderQuery,
|
||||||
GenreListQuery,
|
GenreListQuery,
|
||||||
LyricSearchQuery,
|
LyricSearchQuery,
|
||||||
@@ -75,9 +76,9 @@ export const queryKeys: Record<
|
|||||||
|
|
||||||
return [serverId, 'albumArtists', 'detail'] as const;
|
return [serverId, 'albumArtists', 'detail'] as const;
|
||||||
},
|
},
|
||||||
favoriteSongs: (serverId: string, artistId?: string) => {
|
favoriteSongs: (serverId: string, query?: FavoriteSongListQuery) => {
|
||||||
if (artistId) {
|
if (query) {
|
||||||
return [serverId, 'albumArtists', 'favoriteSongs', artistId] as const;
|
return [serverId, 'albumArtists', 'favoriteSongs', query] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [serverId, 'albumArtists', 'favoriteSongs'] as const;
|
return [serverId, 'albumArtists', 'favoriteSongs'] as const;
|
||||||
|
|||||||
@@ -976,6 +976,56 @@ export const SubsonicController: InternalControllerEndpoint = {
|
|||||||
'&c=Feishin'
|
'&c=Feishin'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
getFavoriteSongs: async (args) => {
|
||||||
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
|
// if user selects 'rating'
|
||||||
|
if (query.type === 'rating') {
|
||||||
|
const res = await SubsonicController.getSongList({
|
||||||
|
apiClientProps,
|
||||||
|
query: {
|
||||||
|
artistIds: [query.artistId],
|
||||||
|
sortBy: SongListSort.RATING,
|
||||||
|
sortOrder: SortOrder.DESC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const songsWithHighRating = orderBy(
|
||||||
|
res.items.filter((song) => song.userRating !== null && song.userRating > 2),
|
||||||
|
['userRating', 'userFavorite', 'playCount', 'albumId', 'trackNumber'],
|
||||||
|
['desc', 'desc', 'desc', 'asc', 'asc'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: songsWithHighRating,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.totalRecordCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// else if user selects 'favorites'
|
||||||
|
const res = await SubsonicController.getSongList({
|
||||||
|
apiClientProps,
|
||||||
|
query: {
|
||||||
|
artistIds: [query.artistId],
|
||||||
|
sortBy: SongListSort.FAVORITED,
|
||||||
|
sortOrder: SortOrder.DESC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const songsWithFavorite = orderBy(
|
||||||
|
res.items.filter((song) => song.userFavorite),
|
||||||
|
['userFavorite', 'userRating', 'playCount', 'albumId', 'trackNumber'],
|
||||||
|
['desc', 'desc', 'desc', 'asc', 'asc'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: songsWithFavorite,
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.totalRecordCount,
|
||||||
|
};
|
||||||
|
},
|
||||||
getFolder: async ({ apiClientProps, query }) => {
|
getFolder: async ({ apiClientProps, query }) => {
|
||||||
const sortOrder = (query.sortOrder?.toLowerCase() ?? 'asc') as 'asc' | 'desc';
|
const sortOrder = (query.sortOrder?.toLowerCase() ?? 'asc') as 'asc' | 'desc';
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ import {
|
|||||||
AlbumArtistInfoQuery,
|
AlbumArtistInfoQuery,
|
||||||
AlbumArtistListQuery,
|
AlbumArtistListQuery,
|
||||||
ArtistListQuery,
|
ArtistListQuery,
|
||||||
|
FavoriteSongListQuery,
|
||||||
ListCountQuery,
|
ListCountQuery,
|
||||||
SongListSort,
|
|
||||||
SortOrder,
|
|
||||||
TopSongListQuery,
|
TopSongListQuery,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
@@ -137,22 +136,16 @@ export const artistsQueries = {
|
|||||||
...args.options,
|
...args.options,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
favoriteSongs: (args: QueryHookArgs<{ artistId: string }>) => {
|
favoriteSongs: (args: QueryHookArgs<FavoriteSongListQuery>) => {
|
||||||
return queryOptions({
|
return queryOptions({
|
||||||
queryFn: ({ signal }) => {
|
queryFn: ({ signal }) => {
|
||||||
return api.controller.getSongList({
|
return api.controller.getFavoriteSongs({
|
||||||
apiClientProps: { serverId: args.serverId, signal },
|
apiClientProps: { serverId: args.serverId, signal },
|
||||||
query: {
|
query: args.query,
|
||||||
artistIds: [args.query.artistId],
|
|
||||||
favorite: true,
|
|
||||||
limit: -1,
|
|
||||||
sortBy: SongListSort.RELEASE_DATE,
|
|
||||||
sortOrder: SortOrder.ASC,
|
|
||||||
startIndex: 0,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
queryKey: queryKeys.albumArtists.favoriteSongs(args.serverId, args.query.artistId),
|
queryKey: queryKeys.albumArtists.favoriteSongs(args.serverId, args.query),
|
||||||
|
...args.options,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
topSongs: (args: QueryHookArgs<TopSongListQuery>) => {
|
topSongs: (args: QueryHookArgs<TopSongListQuery>) => {
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ import {
|
|||||||
useCurrentServer,
|
useCurrentServer,
|
||||||
useCurrentServerId,
|
useCurrentServerId,
|
||||||
usePlayerSong,
|
usePlayerSong,
|
||||||
|
useShowFavorites,
|
||||||
|
useShowRatings,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import {
|
import {
|
||||||
useArtistItems,
|
useArtistItems,
|
||||||
@@ -605,6 +607,12 @@ const AlbumArtistMetadataFavoriteSongs = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||||
|
const [favoriteSongsQueryType, setFavoriteSongsQueryType] = useLocalStorage<
|
||||||
|
'favorite' | 'rating'
|
||||||
|
>({
|
||||||
|
defaultValue: 'favorite',
|
||||||
|
key: 'album-artist-favorite-songs-query-type',
|
||||||
|
});
|
||||||
const albumArtistDetailFavoriteSongsSort = useAppStore(
|
const albumArtistDetailFavoriteSongsSort = useAppStore(
|
||||||
(state) => state.albumArtistDetailFavoriteSongsSort,
|
(state) => state.albumArtistDetailFavoriteSongsSort,
|
||||||
);
|
);
|
||||||
@@ -617,11 +625,24 @@ const AlbumArtistMetadataFavoriteSongs = ({
|
|||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const player = usePlayer();
|
const player = usePlayer();
|
||||||
const serverId = useCurrentServerId();
|
const serverId = useCurrentServerId();
|
||||||
|
const server = useCurrentServer();
|
||||||
|
const showRatings = useShowRatings();
|
||||||
|
const showFavorites = useShowFavorites();
|
||||||
|
const showFavoriteAndRatingSegmentControl =
|
||||||
|
server?.type !== ServerType.JELLYFIN && showFavorites && showRatings;
|
||||||
|
|
||||||
|
let favoriteSongsQueryTypeFilter = favoriteSongsQueryType;
|
||||||
|
if (showRatings && !showFavorites) {
|
||||||
|
favoriteSongsQueryTypeFilter = 'rating';
|
||||||
|
} else if (!showRatings && showFavorites) {
|
||||||
|
favoriteSongsQueryTypeFilter = 'favorite';
|
||||||
|
}
|
||||||
|
|
||||||
const favoriteSongsQuery = useQuery({
|
const favoriteSongsQuery = useQuery({
|
||||||
...artistsQueries.favoriteSongs({
|
...artistsQueries.favoriteSongs({
|
||||||
query: {
|
query: {
|
||||||
artistId: routeId,
|
artistId: routeId,
|
||||||
|
type: favoriteSongsQueryTypeFilter,
|
||||||
},
|
},
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
}),
|
}),
|
||||||
@@ -795,6 +816,27 @@ const AlbumArtistMetadataFavoriteSongs = ({
|
|||||||
}}
|
}}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
/>
|
/>
|
||||||
|
{showFavoriteAndRatingSegmentControl && (
|
||||||
|
<SegmentedControl
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
label: t('common.favorite'),
|
||||||
|
value: 'favorite',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.rating'),
|
||||||
|
value: 'rating',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={(value) =>
|
||||||
|
setFavoriteSongsQueryType(
|
||||||
|
value as 'favorite' | 'rating',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size="xs"
|
||||||
|
value={favoriteSongsQueryType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ListSortByDropdownControlled
|
<ListSortByDropdownControlled
|
||||||
filters={CLIENT_SIDE_SONG_FILTERS}
|
filters={CLIENT_SIDE_SONG_FILTERS}
|
||||||
itemType={LibraryItem.SONG}
|
itemType={LibraryItem.SONG}
|
||||||
|
|||||||
+16
-6
@@ -1,4 +1,4 @@
|
|||||||
import { useSuspenseQueries } from '@tanstack/react-query';
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ import { useAppStore } from '/@/renderer/store/app.store';
|
|||||||
import { useCurrentServer } from '/@/renderer/store/auth.store';
|
import { useCurrentServer } from '/@/renderer/store/auth.store';
|
||||||
import { useSettingsStore } from '/@/renderer/store/settings.store';
|
import { useSettingsStore } from '/@/renderer/store/settings.store';
|
||||||
import { sortSongList } from '/@/shared/api/utils';
|
import { sortSongList } from '/@/shared/api/utils';
|
||||||
|
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
|
||||||
import { LibraryItem, Song } from '/@/shared/types/domain-types';
|
import { LibraryItem, Song } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -36,18 +37,27 @@ const AlbumArtistDetailFavoriteSongsListRoute = () => {
|
|||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const pageKey = LibraryItem.SONG;
|
const pageKey = LibraryItem.SONG;
|
||||||
|
|
||||||
const [detailQuery, favoriteSongsQuery] = useSuspenseQueries({
|
const [favoriteSongsQueryType] = useLocalStorage<'favorite' | 'rating'>({
|
||||||
queries: [
|
defaultValue: 'favorite',
|
||||||
|
key: 'album-artist-favorite-songs-query-type',
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailQuery = useSuspenseQuery(
|
||||||
artistsQueries.albumArtistDetail({
|
artistsQueries.albumArtistDetail({
|
||||||
query: { id: routeId },
|
query: { id: routeId },
|
||||||
serverId: server?.id,
|
serverId: server?.id,
|
||||||
}),
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const favoriteSongsQuery = useSuspenseQuery(
|
||||||
artistsQueries.favoriteSongs({
|
artistsQueries.favoriteSongs({
|
||||||
query: { artistId: routeId },
|
query: {
|
||||||
|
artistId: routeId,
|
||||||
|
type: favoriteSongsQueryType,
|
||||||
|
},
|
||||||
serverId: server?.id,
|
serverId: server?.id,
|
||||||
}),
|
}),
|
||||||
],
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const songs = useMemo(
|
const songs = useMemo(
|
||||||
() => favoriteSongsQuery?.data?.items || [],
|
() => favoriteSongsQuery?.data?.items || [],
|
||||||
|
|||||||
@@ -1329,6 +1329,17 @@ export type ArtistInfoQuery = {
|
|||||||
musicFolderId?: string | string[];
|
musicFolderId?: string | string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FavoriteSongListArgs = BaseEndpointArgs & { query: FavoriteSongListQuery };
|
||||||
|
|
||||||
|
export type FavoriteSongListQuery = {
|
||||||
|
artistId: string;
|
||||||
|
limit?: number;
|
||||||
|
type?: 'favorite' | 'rating';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Favorite Songs List
|
||||||
|
export type FavoriteSongListResponse = BasePaginatedResponse<Song[]>;
|
||||||
|
|
||||||
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
|
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
|
||||||
lyrics: LyricsResponse;
|
lyrics: LyricsResponse;
|
||||||
offsetMs?: number;
|
offsetMs?: number;
|
||||||
@@ -1566,6 +1577,7 @@ export type ControllerEndpoint = {
|
|||||||
getArtistListCount: (args: ArtistListCountArgs) => Promise<number>;
|
getArtistListCount: (args: ArtistListCountArgs) => Promise<number>;
|
||||||
getArtistRadio: (args: ArtistRadioArgs) => Promise<Song[]>;
|
getArtistRadio: (args: ArtistRadioArgs) => Promise<Song[]>;
|
||||||
getDownloadUrl: (args: DownloadArgs) => string;
|
getDownloadUrl: (args: DownloadArgs) => string;
|
||||||
|
getFavoriteSongs: (args: FavoriteSongListArgs) => Promise<FavoriteSongListResponse>;
|
||||||
getFolder: (args: FolderArgs) => Promise<FolderResponse>;
|
getFolder: (args: FolderArgs) => Promise<FolderResponse>;
|
||||||
getGenreList: (args: GenreListArgs) => Promise<GenreListResponse>;
|
getGenreList: (args: GenreListArgs) => Promise<GenreListResponse>;
|
||||||
getImageRequest: (args: ImageArgs) => ImageRequest | null;
|
getImageRequest: (args: ImageArgs) => ImageRequest | null;
|
||||||
@@ -1719,6 +1731,9 @@ export type InternalControllerEndpoint = {
|
|||||||
getArtistListCount: (args: ReplaceApiClientProps<ArtistListCountArgs>) => Promise<number>;
|
getArtistListCount: (args: ReplaceApiClientProps<ArtistListCountArgs>) => Promise<number>;
|
||||||
getArtistRadio: (args: ReplaceApiClientProps<ArtistRadioArgs>) => Promise<Song[]>;
|
getArtistRadio: (args: ReplaceApiClientProps<ArtistRadioArgs>) => Promise<Song[]>;
|
||||||
getDownloadUrl: (args: ReplaceApiClientProps<DownloadArgs>) => string;
|
getDownloadUrl: (args: ReplaceApiClientProps<DownloadArgs>) => string;
|
||||||
|
getFavoriteSongs: (
|
||||||
|
args: ReplaceApiClientProps<FavoriteSongListArgs>,
|
||||||
|
) => Promise<FavoriteSongListResponse>;
|
||||||
getFolder: (args: ReplaceApiClientProps<FolderArgs>) => Promise<FolderResponse>;
|
getFolder: (args: ReplaceApiClientProps<FolderArgs>) => Promise<FolderResponse>;
|
||||||
getGenreList: (args: ReplaceApiClientProps<GenreListArgs>) => Promise<GenreListResponse>;
|
getGenreList: (args: ReplaceApiClientProps<GenreListArgs>) => Promise<GenreListResponse>;
|
||||||
getImageRequest: (args: ReplaceApiClientProps<ImageArgs>) => ImageRequest | null;
|
getImageRequest: (args: ReplaceApiClientProps<ImageArgs>) => ImageRequest | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user