refactor api controller to internalize server fetch

This commit is contained in:
jeffvli 2025-11-02 21:56:35 -08:00
parent 8dbaec3943
commit c7a473d864
79 changed files with 904 additions and 399 deletions

View file

@ -2,18 +2,19 @@ import i18n from '/@/i18n/i18n';
import { JellyfinController } from '/@/renderer/api/jellyfin/jellyfin-controller';
import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-controller';
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
import { useAuthStore } from '/@/renderer/store';
import { getServerById, useAuthStore } from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast';
import {
AuthenticationResponse,
ControllerEndpoint,
InternalControllerEndpoint,
ServerType,
} from '/@/shared/types/domain-types';
type ApiController = {
jellyfin: ControllerEndpoint;
navidrome: ControllerEndpoint;
subsonic: ControllerEndpoint;
jellyfin: InternalControllerEndpoint;
navidrome: InternalControllerEndpoint;
subsonic: InternalControllerEndpoint;
};
const endpoints: ApiController = {
@ -25,7 +26,7 @@ const endpoints: ApiController = {
const apiController = <K extends keyof ControllerEndpoint>(
endpoint: K,
type?: ServerType,
): NonNullable<ControllerEndpoint[K]> => {
): NonNullable<InternalControllerEndpoint[K]> => {
const serverType = type || useAuthStore.getState().currentServer?.type;
if (!serverType) {
@ -68,129 +69,580 @@ export interface GeneralController extends Omit<Required<ControllerEndpoint>, 'a
export const controller: GeneralController = {
addToPlaylist(args) {
return apiController('addToPlaylist', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: addToPlaylist`,
);
}
return apiController(
'addToPlaylist',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
authenticate(url, body, type) {
return apiController('authenticate', type)(url, body);
},
createFavorite(args) {
return apiController('createFavorite', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: createFavorite`,
);
}
return apiController(
'createFavorite',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
createPlaylist(args) {
return apiController('createPlaylist', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: createPlaylist`,
);
}
return apiController(
'createPlaylist',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
deleteFavorite(args) {
return apiController('deleteFavorite', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: deleteFavorite`,
);
}
return apiController(
'deleteFavorite',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
deletePlaylist(args) {
return apiController('deletePlaylist', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: deletePlaylist`,
);
}
return apiController(
'deletePlaylist',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumArtistDetail(args) {
return apiController('getAlbumArtistDetail', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumArtistDetail`,
);
}
return apiController(
'getAlbumArtistDetail',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumArtistList(args) {
return apiController('getAlbumArtistList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumArtistList`,
);
}
return apiController(
'getAlbumArtistList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumArtistListCount(args) {
return apiController('getAlbumArtistListCount', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumArtistListCount`,
);
}
return apiController(
'getAlbumArtistListCount',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumDetail(args) {
return apiController('getAlbumDetail', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumDetail`,
);
}
return apiController(
'getAlbumDetail',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumInfo(args) {
return apiController('getAlbumInfo', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumInfo`,
);
}
return apiController(
'getAlbumInfo',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumList(args) {
return apiController('getAlbumList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumList`,
);
}
return apiController(
'getAlbumList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getAlbumListCount(args) {
return apiController('getAlbumListCount', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getAlbumListCount`,
);
}
return apiController(
'getAlbumListCount',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getArtistList(args) {
return apiController('getArtistList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getArtistList`,
);
}
return apiController(
'getArtistList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getArtistListCount(args) {
return apiController('getArtistListCount', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getArtistListCount`,
);
}
return apiController(
'getArtistListCount',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getDownloadUrl(args) {
return apiController('getDownloadUrl', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getDownloadUrl`,
);
}
return apiController(
'getDownloadUrl',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getGenreList(args) {
return apiController('getGenreList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getGenreList`,
);
}
return apiController(
'getGenreList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getLyrics(args) {
return apiController('getLyrics', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getLyrics`,
);
}
return apiController(
'getLyrics',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getMusicFolderList(args) {
return apiController('getMusicFolderList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getMusicFolderList`,
);
}
return apiController(
'getMusicFolderList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getPlaylistDetail(args) {
return apiController('getPlaylistDetail', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getPlaylistDetail`,
);
}
return apiController(
'getPlaylistDetail',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getPlaylistList(args) {
return apiController('getPlaylistList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getPlaylistList`,
);
}
return apiController(
'getPlaylistList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getPlaylistListCount(args) {
return apiController('getPlaylistListCount', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getPlaylistListCount`,
);
}
return apiController(
'getPlaylistListCount',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getPlaylistSongList(args) {
return apiController('getPlaylistSongList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getPlaylistSongList`,
);
}
return apiController(
'getPlaylistSongList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getRandomSongList(args) {
return apiController('getRandomSongList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getRandomSongList`,
);
}
return apiController(
'getRandomSongList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getRoles(args) {
return apiController('getRoles', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getRoles`,
);
}
return apiController(
'getRoles',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getServerInfo(args) {
return apiController('getServerInfo', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getServerInfo`,
);
}
return apiController(
'getServerInfo',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getSimilarSongs(args) {
return apiController('getSimilarSongs', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getSimilarSongs`,
);
}
return apiController(
'getSimilarSongs',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getSongDetail(args) {
return apiController('getSongDetail', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getSongDetail`,
);
}
return apiController(
'getSongDetail',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getSongList(args) {
return apiController('getSongList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getSongList`,
);
}
return apiController(
'getSongList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getSongListCount(args) {
return apiController('getSongListCount', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getSongListCount`,
);
}
return apiController(
'getSongListCount',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getStructuredLyrics(args) {
return apiController('getStructuredLyrics', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getStructuredLyrics`,
);
}
return apiController(
'getStructuredLyrics',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getTags(args) {
return apiController('getTags', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getTags`,
);
}
return apiController(
'getTags',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getTopSongs(args) {
return apiController('getTopSongs', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getTopSongs`,
);
}
return apiController(
'getTopSongs',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getTranscodingUrl(args) {
return apiController('getTranscodingUrl', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getTranscodingUrl`,
);
}
return apiController(
'getTranscodingUrl',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getUserList(args) {
return apiController('getUserList', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getUserList`,
);
}
return apiController(
'getUserList',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
movePlaylistItem(args) {
return apiController('movePlaylistItem', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: movePlaylistItem`,
);
}
return apiController(
'movePlaylistItem',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
removeFromPlaylist(args) {
return apiController('removeFromPlaylist', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: removeFromPlaylist`,
);
}
return apiController(
'removeFromPlaylist',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
scrobble(args) {
return apiController('scrobble', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: scrobble`,
);
}
return apiController(
'scrobble',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
search(args) {
return apiController('search', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: search`,
);
}
return apiController(
'search',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
setRating(args) {
return apiController('setRating', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: setRating`,
);
}
return apiController(
'setRating',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
shareItem(args) {
return apiController('shareItem', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: shareItem`,
);
}
return apiController(
'shareItem',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
updatePlaylist(args) {
return apiController('updatePlaylist', args.apiClientProps.server?.type)?.(args);
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: updatePlaylist`,
);
}
return apiController(
'updatePlaylist',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
};

View file

@ -11,7 +11,7 @@ import { authenticationFailure } from '/@/renderer/api/utils';
import { useAuthStore } from '/@/renderer/store';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { getClientType } from '/@/shared/api/utils';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItemWithCredential } from '/@/shared/types/domain-types';
const c = initContract();
@ -359,7 +359,7 @@ export const createAuthHeader = (): string => {
};
export const jfApiClient = (args: {
server: null | ServerListItem;
server: null | ServerListItemWithCredential;
signal?: AbortSignal;
url?: string;
}) => {

View file

@ -9,8 +9,8 @@ import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
import {
albumArtistListSortMap,
albumListSortMap,
ControllerEndpoint,
genreListSortMap,
InternalControllerEndpoint,
LibraryItem,
Played,
playlistListSortMap,
@ -40,7 +40,7 @@ const VERSION_INFO: VersionInfo = [
['10.0.0', { [ServerFeature.TAGS]: [1] }],
];
export const JellyfinController: ControllerEndpoint = {
export const JellyfinController: InternalControllerEndpoint = {
addToPlaylist: async (args) => {
const { apiClientProps, body, query } = args;

View file

@ -11,7 +11,7 @@ import { useAuthStore } from '/@/renderer/store';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import { resultWithHeaders } from '/@/shared/api/utils';
import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItemWithCredential } from '/@/shared/types/domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
@ -379,7 +379,7 @@ axiosClient.interceptors.response.use(
);
export const ndApiClient = (args: {
server: null | ServerListItem;
server: null | ServerListItemWithCredential;
signal?: AbortSignal;
url?: string;
}) => {

View file

@ -9,12 +9,12 @@ import {
albumArtistListSortMap,
albumListSortMap,
AuthenticationResponse,
ControllerEndpoint,
genreListSortMap,
InternalControllerEndpoint,
playlistListSortMap,
PlaylistSongListArgs,
PlaylistSongListResponse,
ServerListItem,
ServerListItemWithCredential,
Song,
songListSortMap,
sortOrderMap,
@ -47,7 +47,11 @@ const NAVIDROME_ROLES: Array<string | { label: string; value: string }> = [
const EXCLUDED_TAGS = new Set<string>(['disctotal', 'genre', 'tracktotal']);
const excludeMissing = (server: null | ServerListItem) => {
const excludeMissing = (server?: null | ServerListItemWithCredential) => {
if (!server) {
return undefined;
}
if (hasFeature(server, ServerFeature.BFR)) {
return { missing: false };
}
@ -55,10 +59,10 @@ const excludeMissing = (server: null | ServerListItem) => {
return undefined;
};
const getArtistSongKey = (server: null | ServerListItem) =>
const getArtistSongKey = (server: null | ServerListItemWithCredential) =>
hasFeature(server, ServerFeature.TRACK_ALBUM_ARTIST_SEARCH) ? 'artists_id' : 'album_artist_id';
export const NavidromeController: ControllerEndpoint = {
export const NavidromeController: InternalControllerEndpoint = {
addToPlaylist: async (args) => {
const { apiClientProps, body, query } = args;
@ -156,7 +160,7 @@ export const NavidromeController: ControllerEndpoint = {
throw new Error('Failed to get album artist detail');
}
if (!apiClientProps.server) {
if (!apiClientProps.serverId) {
throw new Error('Server is required');
}
@ -431,7 +435,7 @@ export const NavidromeController: ControllerEndpoint = {
getPlaylistSongList: async (args: PlaylistSongListArgs): Promise<PlaylistSongListResponse> => {
const { apiClientProps, query } = args;
const res = await ndApiClient(apiClientProps).getPlaylistSongList({
const res = await ndApiClient(apiClientProps as any).getPlaylistSongList({
params: {
id: query.id,
},
@ -481,7 +485,7 @@ export const NavidromeController: ControllerEndpoint = {
return {
features,
id: apiClientProps.server?.id,
id: apiClientProps.serverId,
version: ping.body.serverVersion!,
};
},

View file

@ -7,7 +7,7 @@ import { z } from 'zod';
import i18n from '/@/i18n/i18n';
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItemWithCredential } from '/@/shared/types/domain-types';
const c = initContract();
@ -288,7 +288,7 @@ const silentlyTransformResponse = (data: any) => {
};
export const ssApiClient = (args: {
server: null | ServerListItem;
server: null | ServerListItemWithCredential;
signal?: AbortSignal;
silent?: boolean;
url?: string;

View file

@ -16,8 +16,8 @@ import {
} from '/@/shared/api/subsonic/subsonic-types';
import {
AlbumListSort,
ControllerEndpoint,
GenreListSort,
InternalControllerEndpoint,
LibraryItem,
PlaylistListSort,
Song,
@ -51,7 +51,7 @@ const MAX_SUBSONIC_ITEMS = 500;
// A trick to skip ahead 10x
const SUBSONIC_FAST_BATCH_SIZE = MAX_SUBSONIC_ITEMS * 10;
export const SubsonicController: ControllerEndpoint = {
export const SubsonicController: InternalControllerEndpoint = {
addToPlaylist: async ({ apiClientProps, body, query }) => {
const res = await ssApiClient(apiClientProps).updatePlaylist({
query: {

View file

@ -20,7 +20,7 @@ import {
gaplessHandler,
} from '/@/renderer/components/audio-player/utils/list-handlers';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { getServerById, TranscodingConfig, usePlaybackSettings, useSpeed } from '/@/renderer/store';
import { TranscodingConfig, usePlaybackSettings, useSpeed } from '/@/renderer/store';
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { toast } from '/@/shared/components/toast/toast';
import { PlaybackStyle, PlayerStatus } from '/@/shared/types/types';
@ -76,7 +76,7 @@ const useSongUrl = (transcode: TranscodingConfig, current: boolean, song?: Song)
const result = api.controller.getTranscodingUrl({
apiClientProps: {
server: getServerById(song.serverId),
serverId: song.serverId,
},
query: {
base: song.streamUrl,

View file

@ -142,19 +142,19 @@ export const SwiperGridCarousel = ({
const { id, isFavorite, itemType, serverId } = options;
if (isFavorite) {
deleteFavoriteMutation.mutate({
apiClientProps: { serverId },
query: {
id,
type: itemType,
},
serverId,
});
} else {
createFavoriteMutation.mutate({
apiClientProps: { serverId },
query: {
id,
type: itemType,
},
serverId,
});
}
},

View file

@ -15,11 +15,11 @@ export const FavoriteCell = ({ data, node, value }: ICellRendererParams) => {
if (newFavoriteValue) {
createMutation.mutate(
{
apiClientProps: { serverId: data.serverId },
query: {
id: [data.id],
type: data.itemType,
},
serverId: data.serverId,
},
{
onSuccess: () => {
@ -30,11 +30,11 @@ export const FavoriteCell = ({ data, node, value }: ICellRendererParams) => {
} else {
deleteMutation.mutate(
{
apiClientProps: { serverId: data.serverId },
query: {
id: [data.id],
type: data.itemType,
},
serverId: data.serverId,
},
{
onSuccess: () => {

View file

@ -10,11 +10,11 @@ export const RatingCell = ({ node, value }: ICellRendererParams) => {
const handleUpdateRating = (rating: number) => {
updateRatingMutation.mutate(
{
apiClientProps: { serverId: value?.serverId || '' },
query: {
item: [value],
rating,
},
serverId: value?.serverId,
},
{
onSuccess: () => {

View file

@ -168,7 +168,7 @@ export const useVirtualTable = <TFilter extends BaseQuery<any>>({
queryFn: async ({ signal }) => {
const res = await queryFn!({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {

View file

@ -18,7 +18,11 @@ import { Icon } from '/@/shared/components/icon/icon';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { ServerListItem, ServerType } from '/@/shared/types/domain-types';
import {
ServerListItem,
ServerListItemWithCredential,
ServerType,
} from '/@/shared/types/domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
@ -69,7 +73,7 @@ function ServerSelector() {
const currentServer = useCurrentServer();
const { setCurrentServer } = useAuthStoreActions();
const handleSetCurrentServer = (server: ServerListItem) => {
const handleSetCurrentServer = (server: ServerListItemWithCredential) => {
navigate(AppRoute.HOME);
setCurrentServer(server);
};

View file

@ -9,7 +9,7 @@ import { ServerRequired } from '/@/renderer/features/action-required/components/
import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store';
import { useCurrentServerWithCredential } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group';
@ -18,7 +18,7 @@ import { Stack } from '/@/shared/components/stack/stack';
const ActionRequiredRoute = () => {
const { t } = useTranslation();
const currentServer = useCurrentServer();
const currentServer = useCurrentServerWithCredential();
const isServerRequired = !currentServer;
const isCredentialRequired = currentServer && !currentServer.credential;

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AlbumDetailQuery, AlbumListQuery, ListCountQuery } from '/@/shared/types/domain-types';
export const albumQueries = {
@ -11,7 +10,7 @@ export const albumQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumDetail({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
@ -23,7 +22,7 @@ export const albumQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumList({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
@ -39,7 +38,7 @@ export const albumQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumListCount({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},

View file

@ -290,19 +290,19 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
if (detailQuery.data.userFavorite) {
deleteFavoriteMutation.mutate({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM,
},
serverId: detailQuery.data.serverId,
});
} else {
createFavoriteMutation.mutate({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM,
},
serverId: detailQuery.data.serverId,
});
}
};

View file

@ -118,11 +118,11 @@ export const AlbumDetailHeader = forwardRef(
if (!detailQuery?.data) return;
updateRatingMutation.mutate({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
item: [detailQuery.data],
rating,
},
serverId: detailQuery.data.serverId,
});
};

View file

@ -35,7 +35,7 @@ export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
const scrollOffset = searchParams.get('scrollOffset');
const initialScrollOffset = Number(id ? scrollOffset : grid?.scrollOffset) || 0;
const handleFavorite = useHandleFavorite({ gridRef, server });
const handleFavorite = useHandleFavorite({ gridRef });
const cardRows = useMemo(() => {
const rows: CardRow<Album>[] = [ALBUM_CARD_ROWS.name];
@ -177,7 +177,7 @@ export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
queryFn: async ({ signal }) =>
controller.getAlbumList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -12,7 +12,7 @@ import { genresQueries } from '/@/renderer/features/genres/api/genres-api';
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
import {
AlbumListFilter,
getServerById,
useCurrentServer,
useListStoreActions,
useListStoreByKey,
} from '/@/renderer/store';
@ -53,7 +53,7 @@ export const NavidromeAlbumFilters = ({
const { t } = useTranslation();
const { filter } = useListStoreByKey<AlbumListQuery>({ key: pageKey });
const { setFilter } = useListStoreActions();
const server = getServerById(serverId);
const server = useCurrentServer();
const genreListQuery = useQuery(
genresQueries.list({

View file

@ -107,7 +107,7 @@ const AlbumListRoute = () => {
const albumListRes = await queryClient.fetchQuery({
queryFn: ({ signal }) => {
return api.controller.getAlbumList({
apiClientProps: { server, signal },
apiClientProps: { serverId: server?.id || '', signal },
query,
});
},

View file

@ -42,9 +42,8 @@ const DummyAlbumDetailRoute = () => {
const queryKey = queryKeys.songs.detail(server?.id || '', albumId);
const detailQuery = useQuery({
queryFn: ({ signal }) => {
if (!server) throw new Error('Server not found');
return api.controller.getSongDetail({
apiClientProps: { server, signal },
apiClientProps: { serverId: server?.id || '', signal },
query: { id: albumId },
});
},
@ -70,19 +69,19 @@ const DummyAlbumDetailRoute = () => {
try {
if (wasFavorite) {
await deleteFavoriteMutation.mutateAsync({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.SONG,
},
serverId: detailQuery.data.serverId,
});
} else {
await createFavoriteMutation.mutateAsync({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.SONG,
},
serverId: detailQuery.data.serverId,
});
}

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import {
AlbumArtistDetailQuery,
AlbumArtistListQuery,
@ -17,11 +16,11 @@ export const artistsQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumArtistDetail({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.albumArtists.detail(args.serverId || '', args.query),
queryKey: queryKeys.albumArtists.detail(args.serverId, args.query),
...args.options,
});
},
@ -29,11 +28,11 @@ export const artistsQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumArtistList({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.albumArtists.list(args.serverId || '', args.query),
queryKey: queryKeys.albumArtists.list(args.serverId, args.query),
...args.options,
});
},
@ -41,12 +40,12 @@ export const artistsQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getAlbumArtistListCount({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.albumArtists.count(
args.serverId || '',
args.serverId,
Object.keys(args.query).length === 0 ? undefined : args.query,
),
...args.options,
@ -56,12 +55,12 @@ export const artistsQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getArtistListCount({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.albumArtists.count(
args.serverId || '',
args.serverId,
Object.keys(args.query).length === 0 ? undefined : args.query,
),
...args.options,
@ -71,11 +70,11 @@ export const artistsQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.getTopSongs({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.albumArtists.topSongs(args.serverId || '', args.query),
queryKey: queryKeys.albumArtists.topSongs(args.serverId, args.query),
...args.options,
});
},

View file

@ -311,19 +311,19 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
if (detailQuery.data.userFavorite) {
deleteFavoriteMutation.mutate({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM_ARTIST,
},
serverId: detailQuery.data.serverId,
});
} else {
createFavoriteMutation.mutate({
apiClientProps: { serverId: detailQuery.data.serverId },
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM_ARTIST,
},
serverId: detailQuery.data.serverId,
});
}
};

View file

@ -71,11 +71,11 @@ export const AlbumArtistDetailHeader = forwardRef(
if (!detailQuery?.data) return;
updateRatingMutation.mutate({
apiClientProps: { serverId: detailQuery?.data.serverId },
query: {
item: [detailQuery.data],
rating,
},
serverId: detailQuery?.data.serverId,
});
};

View file

@ -38,7 +38,7 @@ export const AlbumArtistListGridView = ({ gridRef, itemCount }: AlbumArtistListG
const { pageKey } = useListContext();
const { display, filter, grid } = useListStoreByKey<AlbumArtistListQuery>({ key: pageKey });
const { setGrid } = useListStoreActions();
const handleFavorite = useHandleFavorite({ gridRef, server });
const handleFavorite = useHandleFavorite({ gridRef });
const fetchInitialData = useCallback(() => {
const query: Omit<AlbumArtistListQuery, 'limit' | 'startIndex'> = {
@ -89,7 +89,7 @@ export const AlbumArtistListGridView = ({ gridRef, itemCount }: AlbumArtistListG
queryFn: async ({ signal }) =>
api.controller.getAlbumArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -184,7 +184,7 @@ export const AlbumArtistListHeaderFilters = ({
queryFn: async ({ signal }) =>
api.controller.getAlbumArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {
@ -220,7 +220,7 @@ export const AlbumArtistListHeaderFilters = ({
queryFn: async ({ signal }) =>
api.controller.getAlbumArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {

View file

@ -39,7 +39,7 @@ export const ArtistListGridView = ({ gridRef, itemCount }: ArtistListGridViewPro
const { pageKey } = useListContext();
const { display, filter, grid } = useListStoreByKey<ArtistListQuery>({ key: pageKey });
const { setGrid } = useListStoreActions();
const handleFavorite = useHandleFavorite({ gridRef, server });
const handleFavorite = useHandleFavorite({ gridRef });
const fetchInitialData = useCallback(() => {
const query: Omit<ArtistListQuery, 'limit' | 'startIndex'> = {
@ -90,7 +90,7 @@ export const ArtistListGridView = ({ gridRef, itemCount }: ArtistListGridViewPro
queryFn: async ({ signal }) =>
api.controller.getArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -191,7 +191,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
queryFn: async ({ signal }) =>
api.controller.getArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {
@ -227,7 +227,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
queryFn: async ({ signal }) =>
api.controller.getArtistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {

View file

@ -39,7 +39,6 @@ import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-
import { useSetRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
import { AppRoute } from '/@/renderer/router/routes';
import {
getServerById,
useAuthStore,
useCurrentServer,
usePlayerStore,
@ -260,7 +259,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const handleDeletePlaylist = useCallback(() => {
for (const item of ctx.data) {
deletePlaylistMutation?.mutate(
{ query: { id: item.id }, serverId: item.serverId },
{ apiClientProps: { serverId: item.serverId }, query: { id: item.id } },
{
onError: (err) => {
toast.error({
@ -330,11 +329,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
createFavoriteMutation.mutate(
{
apiClientProps: { serverId },
query: {
id: items.map((item) => item.id),
type: ctx.type,
},
serverId,
},
{
onError: (err) => {
@ -369,11 +368,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
createFavoriteMutation.mutate(
{
apiClientProps: { serverId },
query: {
id: items.map((item: AnyLibraryItem) => item.id),
type: ctx.type,
},
serverId,
},
{
onError: (err) => {
@ -408,11 +407,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const idsToUnfavorite = nodesByServerId[serverId].map((node) => node.data.id);
deleteFavoriteMutation.mutate(
{
apiClientProps: { serverId },
query: {
id: idsToUnfavorite,
type: ctx.type,
},
serverId,
},
{
onSuccess: () => {
@ -441,11 +440,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
(item: AnyLibraryItem) => item.id,
);
deleteFavoriteMutation.mutate({
apiClientProps: { serverId },
query: {
id: idsToUnfavorite,
type: ctx.type,
},
serverId,
});
}
}
@ -529,11 +528,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const confirm = () => {
removeFromPlaylistMutation.mutate(
{
apiClientProps: { serverId: ctx.data?.[0]?.serverId },
query: {
id: ctx.context.playlistId,
songId: songId || [],
},
serverId: ctx.data?.[0]?.serverId,
},
{
onError: (err) => {
@ -604,11 +603,11 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
updateRatingMutation.mutate(
{
apiClientProps: { serverId },
query: {
item: items,
rating: ratingToSet,
},
serverId,
},
{
onSuccess: () => {
@ -719,7 +718,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const item = ctx.data[0];
const songs = await controller.getSimilarSongs({
apiClientProps: {
server: getServerById(item.serverId),
serverId: item.serverId,
signal: undefined,
},
query: { albumArtistIds: item.albumArtistIds, songId: item.id },
@ -732,7 +731,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const handleDownload = useCallback(() => {
const item = ctx.data[0];
const url = api.controller.getDownloadUrl({
apiClientProps: { server },
apiClientProps: { serverId: item.serverId },
query: { id: item.id },
});
@ -741,7 +740,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
} else {
window.open(url, '_blank');
}
}, [ctx.data, server]);
}, [ctx.data]);
const handleGoToAlbum = useCallback(() => {
const item = ctx.data[0];

View file

@ -6,7 +6,6 @@ import { controller } from '/@/renderer/api/controller';
import {
DiscordDisplayType,
DiscordLinkType,
getServerById,
useAppStore,
useDiscordSettings,
useGeneralSettings,
@ -123,11 +122,9 @@ export const useDiscordRpc = () => {
if (song.serverType === ServerType.JELLYFIN && song.imageUrl) {
activity.largeImageKey = song.imageUrl;
} else if (song.serverType === ServerType.NAVIDROME) {
const server = getServerById(song.serverId);
try {
const info = await controller.getAlbumInfo({
apiClientProps: { server },
apiClientProps: { serverId: song.serverId },
query: { id: song.albumId },
});

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { GenreListQuery } from '/@/shared/types/domain-types';
export const genresQueries = {
@ -11,14 +10,12 @@ export const genresQueries = {
return queryOptions({
gcTime: 1000 * 5,
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getGenreList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.genres.list(args.serverId || '', args.query),
queryKey: queryKeys.genres.list(args.serverId, args.query),
...args.options,
});
},

View file

@ -109,7 +109,7 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
queryFn: async ({ signal }) => {
return api.controller.getGenreList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AlbumListQuery, AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
export const homeQueries = {
@ -18,14 +17,12 @@ export const homeQueries = {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getAlbumList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: requestQuery,
});
},
queryKey: queryKeys.albums.list(args.serverId || '', requestQuery),
queryKey: queryKeys.albums.list(args.serverId, requestQuery),
...args.options,
});
},

View file

@ -87,11 +87,11 @@ export const lyricsQueries = {
// This should only be called for Jellyfin. Return null to ignore errors
if (server.type !== ServerType.JELLYFIN) return null;
return api.controller.getLyrics({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.songs.lyrics(args.serverId || '', args.query),
queryKey: queryKeys.songs.lyrics(args.serverId, args.query),
...args.options,
});
},
@ -111,7 +111,7 @@ export const lyricsQueries = {
if (hasFeature(server, ServerFeature.LYRICS_MULTIPLE_STRUCTURED)) {
const subsonicLyrics = await api.controller
.getStructuredLyrics({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: { songId: song.id },
})
.catch(console.error);
@ -122,7 +122,7 @@ export const lyricsQueries = {
} else if (hasFeature(server, ServerFeature.LYRICS_SINGLE_STRUCTURED)) {
const jfLyrics = await api.controller
.getLyrics({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: { songId: song.id },
})
.catch((err) => console.error(err));
@ -175,7 +175,7 @@ export const lyricsQueries = {
return null;
},
queryKey: queryKeys.songs.lyrics(args.serverId || '', args.query),
queryKey: queryKeys.songs.lyrics(args.serverId, args.query),
staleTime: Infinity,
...args.options,
});

View file

@ -62,11 +62,11 @@ export const RightControls = () => {
if (!song?.id) return;
addToFavoritesMutation.mutate({
apiClientProps: { serverId: song?.serverId || '' },
query: {
id: [song.id],
type: LibraryItem.SONG,
},
serverId: song?.serverId,
});
};
@ -74,11 +74,11 @@ export const RightControls = () => {
if (!currentSong) return;
updateRatingMutation.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
item: [currentSong],
rating,
},
serverId: currentSong?.serverId,
});
};
@ -86,11 +86,11 @@ export const RightControls = () => {
if (!song?.id) return;
removeFromFavoritesMutation.mutate({
apiClientProps: { serverId: song?.serverId || '' },
query: {
id: [song.id],
type: LibraryItem.SONG,
},
serverId: song?.serverId,
});
};
@ -163,16 +163,17 @@ export const RightControls = () => {
remote.requestFavorite((_event, { favorite, id, serverId }) => {
const mutator = favorite ? addToFavoritesMutation : removeFromFavoritesMutation;
mutator.mutate({
apiClientProps: { serverId },
query: {
id: [id],
type: LibraryItem.SONG,
},
serverId,
});
});
remote.requestRating((_event, { id, rating, serverId }) => {
updateRatingMutation.mutate({
apiClientProps: { serverId },
query: {
item: [
{
@ -183,7 +184,6 @@ export const RightControls = () => {
],
rating,
},
serverId,
});
});

View file

@ -98,7 +98,7 @@ export const ShuffleAllModal = ({
queryFn: ({ signal }) =>
api.controller.getRandomSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {
@ -257,7 +257,7 @@ export const openShuffleAllModal = async (
queryFn: ({ signal }) =>
api.controller.getGenreList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {
@ -275,7 +275,7 @@ export const openShuffleAllModal = async (
queryFn: ({ signal }) =>
api.controller.getMusicFolderList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
}),

View file

@ -39,22 +39,18 @@ export const useMediaSession = ({
}
mediaSession.setActionHandler('nexttrack', () => {
console.log('nexttrack');
handleNextTrack();
});
mediaSession.setActionHandler('pause', () => {
console.log('pause');
handlePause();
});
mediaSession.setActionHandler('play', () => {
console.log('play');
handlePlay();
});
mediaSession.setActionHandler('previoustrack', () => {
console.log('previoustrack');
handlePrevTrack();
});

View file

@ -75,13 +75,13 @@ export const useScrobble = () => {
currentSong?.serverType === ServerType.JELLYFIN ? currentTime * 1e7 : undefined;
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
event: 'timeupdate',
id: currentSong.id,
position,
submission: false,
},
serverId: currentSong?.serverId,
});
},
[isScrobbleEnabled, isPrivateModeEnabled, sendScrobble],
@ -149,12 +149,12 @@ export const useScrobble = () => {
: undefined;
sendScrobble.mutate({
apiClientProps: { serverId: previousSong?.serverId || '' },
query: {
id: previousSong.id,
position,
submission: true,
},
serverId: previousSong?.serverId,
});
}
}
@ -173,13 +173,13 @@ export const useScrobble = () => {
// Send start scrobble when song changes and the new song is playing
if (currentStatus === PlayerStatus.PLAYING && currentSong?.id) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
event: 'start',
id: currentSong.id,
position: 0,
submission: false,
},
serverId: currentSong?.serverId,
});
if (currentSong?.serverType === ServerType.JELLYFIN) {
@ -228,13 +228,13 @@ export const useScrobble = () => {
// Whenever the player is restarted, send a 'start' scrobble
if (currentStatus === PlayerStatus.PLAYING) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
event: 'unpause',
id: currentSong.id,
position,
submission: false,
},
serverId: currentSong?.serverId,
});
if (currentSong?.serverType === ServerType.JELLYFIN) {
@ -253,13 +253,13 @@ export const useScrobble = () => {
// Jellyfin is the only one that needs to send a 'pause' event to the server
} else if (currentSong?.serverType === ServerType.JELLYFIN) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
event: 'pause',
id: currentSong.id,
position,
submission: false,
},
serverId: currentSong?.serverId,
});
if (progressIntervalId.current) {
@ -287,11 +287,11 @@ export const useScrobble = () => {
if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
id: currentSong.id,
submission: true,
},
serverId: currentSong?.serverId,
});
setIsCurrentSongScrobbled(true);
@ -332,24 +332,24 @@ export const useScrobble = () => {
if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
id: currentSong.id,
position,
submission: true,
},
serverId: currentSong?.serverId,
});
}
if (currentSong?.serverType === ServerType.JELLYFIN) {
sendScrobble.mutate({
apiClientProps: { serverId: currentSong?.serverId || '' },
query: {
event: 'start',
id: currentSong.id,
position: 0,
submission: false,
},
serverId: currentSong?.serverId,
});
}

View file

@ -3,7 +3,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { MutationOptions } from '/@/renderer/lib/react-query';
import { getServerById, useIncrementQueuePlayCount } from '/@/renderer/store';
import { useIncrementQueuePlayCount } from '/@/renderer/store';
import { usePlayEvent } from '/@/renderer/store/event.store';
import { ScrobbleArgs, ScrobbleResponse } from '/@/shared/types/domain-types';
@ -11,16 +11,12 @@ export const useSendScrobble = (options?: MutationOptions) => {
const incrementPlayCount = useIncrementQueuePlayCount();
const sendPlayEvent = usePlayEvent();
return useMutation<
ScrobbleResponse,
AxiosError,
Omit<ScrobbleArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<ScrobbleResponse, AxiosError, ScrobbleArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.scrobble({ ...args, apiClientProps: { server } });
return api.controller.scrobble({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
// Manually increment the play count for the song in the queue if scrobble was submitted

View file

@ -33,7 +33,7 @@ export const getPlaylistSongsById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getPlaylistSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -77,7 +77,7 @@ export const getAlbumSongsById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -119,7 +119,7 @@ export const getGenreSongsById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -161,7 +161,7 @@ export const getAlbumArtistSongsById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -196,7 +196,7 @@ export const getArtistSongsById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -229,7 +229,7 @@ export const getSongsByQuery = async (args: {
queryFn: async ({ signal }) => {
return api.controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,
@ -258,7 +258,7 @@ export const getSongById = async (args: {
queryFn: async ({ signal }) =>
api.controller.getSongDetail({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: queryFilter,

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import {
PlaylistDetailQuery,
PlaylistListQuery,
@ -14,14 +13,12 @@ export const playlistsQueries = {
detail: (args: QueryHookArgs<PlaylistDetailQuery>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getPlaylistDetail({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.playlists.detail(args.serverId || '', args.query.id, args.query),
queryKey: queryKeys.playlists.detail(args.serverId, args.query.id, args.query),
...args.options,
});
},
@ -29,10 +26,8 @@ export const playlistsQueries = {
return queryOptions({
gcTime: 1000 * 60 * 60,
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getPlaylistList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
@ -43,10 +38,8 @@ export const playlistsQueries = {
songList: (args: QueryHookArgs<PlaylistSongListQuery>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getPlaylistSongList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},

View file

@ -120,7 +120,7 @@ export const AddToPlaylistContextModal = ({
queryFn: ({ signal }) => {
if (!server) throw new Error('No server');
return api.controller.getSongList({
apiClientProps: { server, signal },
apiClientProps: { serverId: server?.id || '', signal },
query,
});
},
@ -147,7 +147,7 @@ export const AddToPlaylistContextModal = ({
queryFn: ({ signal }) => {
if (!server) throw new Error('No server');
return api.controller.getSongList({
apiClientProps: { server, signal },
apiClientProps: { serverId: server?.id || '', signal },
query,
});
},
@ -203,7 +203,7 @@ export const AddToPlaylistContextModal = ({
for (const playlist of values.newPlaylists) {
try {
const response = await api.controller.createPlaylist({
apiClientProps: { server },
apiClientProps: { serverId: server?.id || '' },
body: {
name: playlist,
public: false,
@ -238,7 +238,7 @@ export const AddToPlaylistContextModal = ({
);
return api.controller.getPlaylistSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {
@ -266,9 +266,9 @@ export const AddToPlaylistContextModal = ({
}
addToPlaylistMutation.mutate(
{
apiClientProps: { serverId: server.id },
body: { songId: values.skipDuplicates ? uniqueSongIds : allSongIds },
query: { id: playlistId },
serverId: server?.id,
},
{
onError: (err) => {

View file

@ -58,6 +58,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
mutation.mutate(
{
apiClientProps: { serverId: server.id },
body: {
...values,
_custom: {
@ -75,7 +76,6 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
},
},
},
serverId: server.id,
},
{
onError: (err) => {

View file

@ -111,7 +111,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
try {
await api.controller.movePlaylistItem({
apiClientProps: {
server,
serverId: server?.id || '',
},
query: {
endingIndex: e.overIndex,

View file

@ -376,7 +376,10 @@ export const PlaylistDetailSongListHeaderFilters = ({
const handleDeletePlaylist = useCallback(() => {
if (!detailQuery.data) return;
deletePlaylistMutation?.mutate(
{ query: { id: detailQuery.data.id }, serverId: detailQuery.data.serverId },
{
apiClientProps: { serverId: detailQuery.data.serverId },
query: { id: detailQuery.data.id },
},
{
onError: (err) => {
toast.error({

View file

@ -38,7 +38,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
const handlePlayQueueAdd = usePlayQueueAdd();
const { display, filter, grid } = useListStoreByKey<PlaylistListQuery>({ key: pageKey });
const { setGrid } = useListStoreActions();
const handleFavorite = useHandleFavorite({ gridRef, server });
const handleFavorite = useHandleFavorite({ gridRef });
const cardRows = useMemo(() => {
const rows: CardRow<Playlist>[] = [PLAYLIST_CARD_ROWS.nameFull];
@ -126,7 +126,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
queryFn: async ({ signal }) =>
controller.getPlaylistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -174,7 +174,7 @@ export const PlaylistListHeaderFilters = ({
queryFn: async ({ signal }) =>
api.controller.getPlaylistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,
@ -213,7 +213,7 @@ export const PlaylistListHeaderFilters = ({
queryFn: async ({ signal }) =>
api.controller.getPlaylistList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query: {

View file

@ -21,7 +21,7 @@ interface SaveAsPlaylistFormProps {
body: Partial<CreatePlaylistBody>;
onCancel: () => void;
onSuccess: (data: CreatePlaylistResponse) => void;
serverId: string | undefined;
serverId?: string;
}
export const SaveAsPlaylistForm = ({
@ -50,7 +50,7 @@ export const SaveAsPlaylistForm = ({
const handleSubmit = form.onSubmit((values) => {
mutation.mutate(
{ body: values, serverId },
{ apiClientProps: { serverId: serverId || '' }, body: values },
{
onError: (err) => {
toast.error({

View file

@ -65,9 +65,9 @@ export const UpdatePlaylistForm = ({ body, onCancel, query, users }: UpdatePlayl
const handleSubmit = form.onSubmit((values) => {
mutation.mutate(
{
apiClientProps: { serverId: server?.id || '' },
body: values,
query,
serverId: server?.id,
},
{
onError: (err) => {
@ -174,7 +174,10 @@ export const openUpdatePlaylistModal = async (args: {
? await queryClient
.fetchQuery({
queryFn: ({ signal }) =>
api.controller.getUserList({ apiClientProps: { server, signal }, query }),
api.controller.getUserList({
apiClientProps: { serverId: server?.id || '', signal },
query,
}),
queryKey: queryKeys.users.list(server?.id || '', query),
})
.catch((error) => {

View file

@ -4,26 +4,22 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AddToPlaylistArgs, AddToPlaylistResponse } from '/@/shared/types/domain-types';
export const useAddToPlaylist = (args: MutationHookArgs) => {
const { options } = args || {};
const queryClient = useQueryClient();
return useMutation<
AddToPlaylistResponse,
AxiosError,
Omit<AddToPlaylistArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<AddToPlaylistResponse, AxiosError, AddToPlaylistArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.addToPlaylist({ ...args, apiClientProps: { server } });
return api.controller.addToPlaylist({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
const { serverId } = variables;
const { apiClientProps } = variables;
const serverId = apiClientProps.serverId;
if (!serverId) return;

View file

@ -4,32 +4,24 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { CreatePlaylistArgs, CreatePlaylistResponse } from '/@/shared/types/domain-types';
export const useCreatePlaylist = (args: MutationHookArgs) => {
const { options } = args || {};
const queryClient = useQueryClient();
return useMutation<
CreatePlaylistResponse,
AxiosError,
Omit<CreatePlaylistArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<CreatePlaylistResponse, AxiosError, CreatePlaylistArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.createPlaylist({ ...args, apiClientProps: { server } });
return api.controller.createPlaylist({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_args, variables) => {
const server = getServerById(variables.serverId);
if (server) {
queryClient.invalidateQueries({
exact: false,
queryKey: queryKeys.playlists.list(server.id),
queryKey: queryKeys.playlists.list(variables.apiClientProps.serverId),
});
}
},
...options,
});

View file

@ -4,33 +4,29 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useCurrentServer } from '/@/renderer/store';
import { DeletePlaylistArgs, DeletePlaylistResponse } from '/@/shared/types/domain-types';
export const useDeletePlaylist = (args: MutationHookArgs) => {
const { options } = args || {};
const queryClient = useQueryClient();
const server = useCurrentServer();
return useMutation<
DeletePlaylistResponse,
AxiosError,
Omit<DeletePlaylistArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<DeletePlaylistResponse, AxiosError, DeletePlaylistArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.deletePlaylist({ ...args, apiClientProps: { server } });
return api.controller.deletePlaylist({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onMutate: () => {
queryClient.cancelQueries({ queryKey: queryKeys.playlists.list(server?.id || '') });
onMutate: (variables) => {
queryClient.cancelQueries({
queryKey: queryKeys.playlists.list(variables.apiClientProps.serverId),
});
return null;
},
onSuccess: () => {
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({
exact: false,
queryKey: queryKeys.playlists.list(server?.id || ''),
queryKey: queryKeys.playlists.list(variables.apiClientProps.serverId),
});
},
...options,

View file

@ -4,25 +4,21 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationOptions } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { RemoveFromPlaylistArgs, RemoveFromPlaylistResponse } from '/@/shared/types/domain-types';
export const useRemoveFromPlaylist = (options?: MutationOptions) => {
const queryClient = useQueryClient();
return useMutation<
RemoveFromPlaylistResponse,
AxiosError,
Omit<RemoveFromPlaylistArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<RemoveFromPlaylistResponse, AxiosError, RemoveFromPlaylistArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.removeFromPlaylist({ ...args, apiClientProps: { server } });
return api.controller.removeFromPlaylist({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
const { serverId } = variables;
const { apiClientProps } = variables;
const serverId = apiClientProps.serverId;
if (!serverId) return;

View file

@ -4,26 +4,22 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { UpdatePlaylistArgs, UpdatePlaylistResponse } from '/@/shared/types/domain-types';
export const useUpdatePlaylist = (args: MutationHookArgs) => {
const { options } = args || {};
const queryClient = useQueryClient();
return useMutation<
UpdatePlaylistResponse,
AxiosError,
Omit<UpdatePlaylistArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<UpdatePlaylistResponse, AxiosError, UpdatePlaylistArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.updatePlaylist({ ...args, apiClientProps: { server } });
return api.controller.updatePlaylist({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
const { query, serverId } = variables;
const { apiClientProps, query } = variables;
const serverId = apiClientProps.serverId;
if (!serverId) return;

View file

@ -56,6 +56,7 @@ const PlaylistDetailSongListRoute = () => {
createPlaylistMutation.mutate(
{
apiClientProps: { serverId: detailQuery?.data?.serverId },
body: {
_custom: {
navidrome: {
@ -69,7 +70,6 @@ const PlaylistDetailSongListRoute = () => {
name: detailQuery?.data?.name,
public: detailQuery?.data?.public || false,
},
serverId: detailQuery?.data?.serverId,
},
{
onSuccess: (data) => {
@ -83,8 +83,8 @@ const PlaylistDetailSongListRoute = () => {
},
);
deletePlaylistMutation.mutate({
apiClientProps: { serverId: detailQuery?.data?.serverId },
query: { id: playlistId },
serverId: detailQuery?.data?.serverId,
});
},
},
@ -124,7 +124,7 @@ const PlaylistDetailSongListRoute = () => {
}),
)
}
serverId={detailQuery?.data?.serverId}
serverId={detailQuery?.data?.serverId || ''}
/>
),
title: t('common.saveAs', { postProcess: 'sentenceCase' }),

View file

@ -3,7 +3,6 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { SearchQuery } from '/@/shared/types/domain-types';
export const searchQueries = {
@ -11,11 +10,11 @@ export const searchQueries = {
return queryOptions({
queryFn: ({ signal }) => {
return api.controller.search({
apiClientProps: { server: getServerById(args.serverId), signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.search.list(args.serverId || '', args.query),
queryKey: queryKeys.search.list(args.serverId, args.query),
...args.options,
});
},

View file

@ -7,7 +7,7 @@ import { Command, CommandPalettePages } from '/@/renderer/features/search/compon
import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { AppRoute } from '/@/renderer/router/routes';
import { useAuthStoreActions, useServerList } from '/@/renderer/store';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItemWithCredential } from '/@/shared/types/domain-types';
interface ServerCommandsProps {
handleClose: () => void;
@ -32,7 +32,7 @@ export const ServerCommands = ({ handleClose, setPages, setQuery }: ServerComman
}, [handleClose, setPages, setQuery, t]);
const handleSelectServer = useCallback(
(server: ServerListItem) => {
(server: ServerListItemWithCredential) => {
navigate(AppRoute.HOME);
setCurrentServer(server);
handleClose();

View file

@ -21,7 +21,7 @@ import { Stack } from '/@/shared/components/stack/stack';
import { TextInput } from '/@/shared/components/text-input/text-input';
import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast';
import { AuthenticationResponse, ServerListItem } from '/@/shared/types/domain-types';
import { AuthenticationResponse, ServerListItemWithCredential } from '/@/shared/types/domain-types';
import { DiscoveredServerItem, ServerType, toServerType } from '/@/shared/types/types';
const autodiscover = isElectron() ? window.api.autodiscover : null;
@ -152,7 +152,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
});
}
const serverItem: ServerListItem = {
const serverItem: ServerListItemWithCredential = {
credential: data.credential,
id: nanoid(),
name: values.name,

View file

@ -18,7 +18,12 @@ import { Stack } from '/@/shared/components/stack/stack';
import { TextInput } from '/@/shared/components/text-input/text-input';
import { toast } from '/@/shared/components/toast/toast';
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
import { AuthenticationResponse, ServerListItem, ServerType } from '/@/shared/types/domain-types';
import {
AuthenticationResponse,
ServerListItem,
ServerListItemWithCredential,
ServerType,
} from '/@/shared/types/domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
@ -86,7 +91,7 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
});
}
const serverItem: ServerListItem = {
const serverItem: ServerListItemWithCredential = {
credential: data.credential,
id: server.id,
name: values.name,

View file

@ -3,28 +3,25 @@ import { queryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { MusicFolderListQuery, TagQuery, UserListQuery } from '/@/shared/types/domain-types';
export const sharedQueries = {
musicFolders: (args: QueryHookArgs<MusicFolderListQuery>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getMusicFolderList({ apiClientProps: { server, signal } });
return api.controller.getMusicFolderList({
apiClientProps: { serverId: args.serverId, signal },
});
},
queryKey: queryKeys.musicFolders.list(args.serverId || ''),
queryKey: queryKeys.musicFolders.list(args.serverId),
...args.options,
});
},
roles: (args: QueryHookArgs<object>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getRoles({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
});
},
queryKey: queryKeys.roles.list(args.serverId || ''),
@ -35,10 +32,8 @@ export const sharedQueries = {
return queryOptions({
gcTime: 1000 * 60,
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getTags({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
@ -49,10 +44,8 @@ export const sharedQueries = {
users: (args: QueryHookArgs<UserListQuery>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getUserList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},

View file

@ -3,17 +3,17 @@ import { MutableRefObject, useCallback } from 'react';
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid/virtual-infinite-grid';
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { useCurrentServerId } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { ServerListItem } from '/@/shared/types/types';
interface HandleFavoriteProps {
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
server: null | ServerListItem;
}
export const useHandleFavorite = ({ gridRef, server }: HandleFavoriteProps) => {
export const useHandleFavorite = ({ gridRef }: HandleFavoriteProps) => {
const createFavoriteMutation = useCreateFavorite({});
const deleteFavoriteMutation = useDeleteFavorite({});
const serverId = useCurrentServerId();
const handleFavorite = useCallback(
async (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => {
@ -21,19 +21,19 @@ export const useHandleFavorite = ({ gridRef, server }: HandleFavoriteProps) => {
try {
if (isFavorite) {
await deleteFavoriteMutation.mutateAsync({
apiClientProps: { serverId },
query: {
id,
type: itemType,
},
serverId: server?.id,
});
} else {
await createFavoriteMutation.mutateAsync({
apiClientProps: { serverId },
query: {
id,
type: itemType,
},
serverId: server?.id,
});
}
@ -50,7 +50,7 @@ export const useHandleFavorite = ({ gridRef, server }: HandleFavoriteProps) => {
console.error(error);
}
},
[createFavoriteMutation, deleteFavoriteMutation, gridRef, server?.id],
[createFavoriteMutation, deleteFavoriteMutation, gridRef, serverId],
);
return handleFavorite;

View file

@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
import { useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
import { useFavoriteEvent } from '/@/renderer/store/event.store';
import {
AlbumArtistDetailResponse,
@ -24,19 +24,16 @@ export const useCreateFavorite = (args: MutationHookArgs) => {
const setQueueFavorite = useSetQueueFavorite();
const setFavoriteEvent = useFavoriteEvent();
return useMutation<
FavoriteResponse,
AxiosError,
Omit<FavoriteArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.createFavorite({ ...args, apiClientProps: { server } });
return api.controller.createFavorite({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
const { serverId } = variables;
const { apiClientProps } = variables;
const serverId = apiClientProps.serverId;
if (!serverId) return;

View file

@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
import { useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
import { useFavoriteEvent } from '/@/renderer/store/event.store';
import {
AlbumArtistDetailResponse,
@ -24,21 +24,16 @@ export const useDeleteFavorite = (args: MutationHookArgs) => {
const setQueueFavorite = useSetQueueFavorite();
const setFavoriteEvent = useFavoriteEvent();
return useMutation<
FavoriteResponse,
AxiosError,
Omit<FavoriteArgs, 'apiClientProps' | 'server'>,
null
>({
return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.deleteFavorite({ ...args, apiClientProps: { server } });
return api.controller.deleteFavorite({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onSuccess: (_data, variables) => {
const { serverId } = variables;
if (!serverId) return;
const { apiClientProps } = variables;
const serverId = apiClientProps.serverId;
for (const id of variables.query.id) {
// Set the userFavorite property to false for the album in the album list data store
@ -55,7 +50,9 @@ export const useDeleteFavorite = (args: MutationHookArgs) => {
// We only need to set if we're already on the album detail page
if (variables.query.type === LibraryItem.ALBUM && variables.query.id.length === 1) {
const queryKey = queryKeys.albums.detail(serverId, { id: variables.query.id[0] });
const queryKey = queryKeys.albums.detail(serverId, {
id: variables.query.id[0],
});
const previous = queryClient.getQueryData<AlbumDetailResponse>(queryKey);
if (previous) {

View file

@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useSetAlbumListItemDataById, useSetQueueRating } from '/@/renderer/store';
import { useSetAlbumListItemDataById, useSetQueueRating } from '/@/renderer/store';
import { useRatingEvent } from '/@/renderer/store/event.store';
import {
Album,
@ -30,13 +30,14 @@ export const useSetRating = (args: MutationHookArgs) => {
return useMutation<
RatingResponse,
AxiosError,
Omit<SetRatingArgs, 'apiClientProps' | 'server'>,
SetRatingArgs,
{ previous: undefined | { items: AnyLibraryItems } }
>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.setRating({ ...args, apiClientProps: { server } });
return api.controller.setRating({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
onError: (_error, _variables, context) => {
for (const item of context?.previous?.items || []) {

View file

@ -48,6 +48,7 @@ export const ShareItemContextModal = ({
const handleSubmit = form.onSubmit(async (values) => {
shareItemMutation.mutate(
{
apiClientProps: { serverId: server?.id || '' },
body: {
description: values.description,
downloadable: values.allowDownloading,
@ -55,7 +56,6 @@ export const ShareItemContextModal = ({
resourceIds: itemIds.join(),
resourceType,
},
serverId: server?.id,
},
{
onError: () => {

View file

@ -3,7 +3,6 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api';
import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AnyLibraryItems, ShareItemArgs, ShareItemResponse } from '/@/shared/types/domain-types';
export const useShareItem = (args: MutationHookArgs) => {
@ -12,13 +11,14 @@ export const useShareItem = (args: MutationHookArgs) => {
return useMutation<
ShareItemResponse,
AxiosError,
Omit<ShareItemArgs, 'apiClientProps' | 'server'>,
ShareItemArgs,
{ previous: undefined | { items: AnyLibraryItems } }
>({
mutationFn: (args) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.shareItem({ ...args, apiClientProps: { server } });
return api.controller.shareItem({
...args,
apiClientProps: { serverId: args.apiClientProps.serverId },
});
},
...options,
});

View file

@ -4,36 +4,31 @@ import { api } from '/@/renderer/api';
import { controller } from '/@/renderer/api/controller';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { ListCountQuery, SimilarSongsQuery, SongListQuery } from '/@/shared/types/domain-types';
export const songsQueries = {
list: (args: QueryHookArgs<SongListQuery>, imageSize?: number) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return controller.getSongList({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: { ...args.query, imageSize },
});
},
queryKey: queryKeys.songs.list(args.serverId || '', { ...args.query, imageSize }),
queryKey: queryKeys.songs.list(args.serverId, { ...args.query, imageSize }),
...args.options,
});
},
listCount: (args: QueryHookArgs<ListCountQuery<SongListQuery>>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getSongListCount({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: args.query,
});
},
queryKey: queryKeys.songs.count(
args.serverId || '',
args.serverId,
Object.keys(args.query).length === 0 ? undefined : args.query,
),
...args.options,
@ -42,10 +37,8 @@ export const songsQueries = {
similar: (args: QueryHookArgs<SimilarSongsQuery>) => {
return queryOptions({
queryFn: ({ signal }) => {
const server = getServerById(args.serverId);
if (!server) throw new Error('Server not found');
return api.controller.getSimilarSongs({
apiClientProps: { server, signal },
apiClientProps: { serverId: args.serverId, signal },
query: {
albumArtistIds: args.query.albumArtistIds,
count: args.query.count ?? 50,
@ -53,7 +46,7 @@ export const songsQueries = {
},
});
},
queryKey: queryKeys.songs.similar(args.serverId || '', args.query),
queryKey: queryKeys.songs.similar(args.serverId, args.query),
...args.options,
});
},

View file

@ -10,8 +10,8 @@ import {
import { genresQueries } from '/@/renderer/features/genres/api/genres-api';
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
import {
getServerById,
SongListFilter,
useCurrentServer,
useListFilterByKey,
useListStoreActions,
} from '/@/renderer/store';
@ -42,7 +42,7 @@ export const NavidromeSongFilters = ({
const { t } = useTranslation();
const { setFilter } = useListStoreActions();
const filter = useListFilterByKey<SongListQuery>({ key: pageKey });
const server = getServerById(serverId);
const server = useCurrentServer();
const isGenrePage = customFilters?.genreIds !== undefined;

View file

@ -43,7 +43,7 @@ export const SongListGridView = ({ gridRef, itemCount }: SongListGridViewProps)
const scrollOffset = searchParams.get('scrollOffset');
const initialScrollOffset = Number(id ? scrollOffset : grid?.scrollOffset) || 0;
const handleFavorite = useHandleFavorite({ gridRef, server });
const handleFavorite = useHandleFavorite({ gridRef });
useEffect(() => {
const unSub = useEventStore.subscribe((state) => {
@ -189,7 +189,7 @@ export const SongListGridView = ({ gridRef, itemCount }: SongListGridViewProps)
queryFn: async ({ signal }) =>
controller.getSongList({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -20,7 +20,7 @@ import {
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
import { Icon } from '/@/shared/components/icon/icon';
import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem, ServerType } from '/@/shared/types/domain-types';
import { ServerListItemWithCredential, ServerType } from '/@/shared/types/domain-types';
const browser = isElectron() ? window.api.browser : null;
const localSettings = isElectron() ? window.api.localSettings : null;
@ -35,12 +35,12 @@ export const AppMenu = () => {
const { privateMode } = useAppStore();
const { setPrivateMode, setSideBar } = useAppStoreActions();
const handleSetCurrentServer = (server: ServerListItem) => {
const handleSetCurrentServer = (server: ServerListItemWithCredential) => {
navigate(AppRoute.HOME);
setCurrentServer(server);
};
const handleCredentialsModal = async (server: ServerListItem) => {
const handleCredentialsModal = async (server: ServerListItemWithCredential) => {
let password: null | string = null;
try {

View file

@ -86,7 +86,7 @@ export const useListFilterRefresh = ({
queryFn: async ({ signal }) => {
return queryFn({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
query,

View file

@ -3,7 +3,7 @@ import { debounce } from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react';
import { api } from '/@/renderer/api';
import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { getServerById, useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast';
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
import { AuthState, ServerListItem, ServerType } from '/@/shared/types/types';
@ -26,7 +26,7 @@ export const useServerAuthenticated = () => {
// making one request first
try {
await api.controller.getSongList({
apiClientProps: { server },
apiClientProps: { serverId: server?.id || '' },
query: {
limit: 1,
sortBy: SongListSort.NAME,
@ -59,11 +59,12 @@ export const useServerAuthenticated = () => {
useEffect(() => {
if (priorServerId.current !== server?.id) {
const serverWithAuth = getServerById(server!.id);
priorServerId.current = server?.id || '';
if (server?.type === ServerType.NAVIDROME) {
setReady(AuthState.LOADING);
debouncedAuth(server);
debouncedAuth(serverWithAuth!);
} else {
setReady(AuthState.VALID);
}

View file

@ -15,7 +15,7 @@ export const useServerVersion = () => {
queryFn: async ({ signal }) => {
return controller.getServerInfo({
apiClientProps: {
server,
serverId: server?.id || '',
signal,
},
});

View file

@ -2,27 +2,28 @@ import merge from 'lodash/merge';
import { nanoid } from 'nanoid/non-secure';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { shallow } from 'zustand/shallow';
import { createWithEqualityFn } from 'zustand/traditional';
import { useAlbumArtistListDataStore } from '/@/renderer/store/album-artist-list-data.store';
import { useAlbumListDataStore } from '/@/renderer/store/album-list-data.store';
import { useListStore } from '/@/renderer/store/list.store';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItem, ServerListItemWithCredential } from '/@/shared/types/domain-types';
export interface AuthSlice extends AuthState {
actions: {
addServer: (args: ServerListItem) => void;
addServer: (args: ServerListItemWithCredential) => void;
deleteServer: (id: string) => void;
getServer: (id: string) => null | ServerListItem;
setCurrentServer: (server: null | ServerListItem) => void;
updateServer: (id: string, args: Partial<ServerListItem>) => void;
getServer: (id: string) => null | ServerListItemWithCredential;
setCurrentServer: (server: null | ServerListItemWithCredential) => void;
updateServer: (id: string, args: Partial<ServerListItemWithCredential>) => void;
};
}
export interface AuthState {
currentServer: null | ServerListItem;
currentServer: null | ServerListItemWithCredential;
deviceId: string;
serverList: Record<string, ServerListItem>;
serverList: Record<string, ServerListItemWithCredential>;
}
export const useAuthStore = createWithEqualityFn<AuthSlice>()(
@ -92,7 +93,23 @@ export const useAuthStore = createWithEqualityFn<AuthSlice>()(
export const useCurrentServerId = () => useAuthStore((state) => state.currentServer)?.id || '';
export const useCurrentServer = () =>
useAuthStore((state) => state.currentServer) as ServerListItem;
useAuthStore((state) => {
return {
features: state.currentServer?.features,
id: state.currentServer?.id,
name: state.currentServer?.name,
preferInstantMix: state.currentServer?.preferInstantMix,
savePassword: state.currentServer?.savePassword,
type: state.currentServer?.type,
url: state.currentServer?.url,
userId: state.currentServer?.userId,
username: state.currentServer?.username,
version: state.currentServer?.version,
};
}, shallow) as ServerListItem;
export const useCurrentServerWithCredential = () =>
useAuthStore((state) => state.currentServer) as ServerListItemWithCredential;
export const useServerList = () => useAuthStore((state) => state.serverList);

View file

@ -914,7 +914,6 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
merge: mergeOverridingColumns,
migrate(persistedState, version) {
const state = persistedState as SettingsSlice;
console.log('migrate: ', version);
if (version === 8) {
state.general.sidebarItems = state.general.sidebarItems.filter(

View file

@ -3,7 +3,7 @@ import type { PlayerData, QueueSong } from '/@/shared/types/domain-types';
import isElectron from 'is-electron';
import { api } from '/@/renderer/api';
import { getServerById, useSettingsStore } from '/@/renderer/store';
import { useSettingsStore } from '/@/renderer/store';
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
@ -12,7 +12,7 @@ const modifyUrl = (song: QueueSong): string => {
if (transcode.enabled) {
const streamUrl = api.controller.getTranscodingUrl({
apiClientProps: {
server: getServerById(song.serverId),
serverId: song.serverId,
},
query: {
base: song.streamUrl,

View file

@ -126,7 +126,7 @@ const getArtists = (
const normalizeSong = (
item: z.infer<typeof ndType._response.playlistSong> | z.infer<typeof ndType._response.song>,
server: null | ServerListItem,
server?: null | ServerListItem,
imageSize?: number,
): Song => {
let id;
@ -222,7 +222,7 @@ const normalizeAlbum = (
item: z.infer<typeof ndType._response.album> & {
songs?: z.infer<typeof ndType._response.songList>;
},
server: null | ServerListItem,
server?: null | ServerListItem,
imageSize?: number,
): Album => {
const imageUrl = getCoverArtUrl({
@ -293,7 +293,7 @@ const normalizeAlbumArtist = (
item: z.infer<typeof ndType._response.albumArtist> & {
similarArtists?: z.infer<typeof ssType._response.artistInfo>['artistInfo']['similarArtist'];
},
server: null | ServerListItem,
server?: null | ServerListItem,
): AlbumArtist => {
let imageUrl = getImageUrl({ url: item?.largeImageUrl || null });
@ -358,7 +358,7 @@ const normalizeAlbumArtist = (
const normalizePlaylist = (
item: z.infer<typeof ndType._response.playlist>,
server: null | ServerListItem,
server?: null | ServerListItem,
imageSize?: number,
): Playlist => {
const imageUrl = getCoverArtUrl({

View file

@ -11,7 +11,7 @@ import {
Playlist,
QueueSong,
RelatedArtist,
ServerListItem,
ServerListItemWithCredential,
ServerType,
} from '/@/shared/types/domain-types';
@ -117,7 +117,7 @@ const getGenres = (
const normalizeSong = (
item: z.infer<typeof ssType._response.song>,
server: null | ServerListItem,
server?: null | ServerListItemWithCredential,
size?: number,
): QueueSong => {
const imageUrl =
@ -200,7 +200,7 @@ const normalizeAlbumArtist = (
item:
| z.infer<typeof ssType._response.albumArtist>
| z.infer<typeof ssType._response.artistListEntry>,
server: null | ServerListItem,
server?: null | ServerListItemWithCredential,
imageSize?: number,
): AlbumArtist => {
const imageUrl =
@ -235,7 +235,7 @@ const normalizeAlbumArtist = (
const normalizeAlbum = (
item: z.infer<typeof ssType._response.album> | z.infer<typeof ssType._response.albumListEntry>,
server: null | ServerListItem,
server?: null | ServerListItemWithCredential,
imageSize?: number,
): Album => {
const imageUrl =
@ -294,7 +294,7 @@ const normalizePlaylist = (
item:
| z.infer<typeof ssType._response.playlist>
| z.infer<typeof ssType._response.playlistListEntry>,
server: null | ServerListItem,
server?: null | ServerListItemWithCredential,
): Playlist => {
return {
description: item.comment || null,

View file

@ -1,3 +1,4 @@
import { Omit } from 'lodash';
import orderBy from 'lodash/orderBy';
import reverse from 'lodash/reverse';
import shuffle from 'lodash/shuffle';
@ -84,11 +85,9 @@ export type QueueSong = Song & {
};
export type ServerListItem = {
credential: string;
features?: ServerFeatures;
id: string;
name: string;
ndCredential?: string;
preferInstantMix?: boolean;
savePassword?: boolean;
type: ServerType;
@ -98,6 +97,11 @@ export type ServerListItem = {
version?: string;
};
export type ServerListItemWithCredential = ServerListItem & {
credential: string;
ndCredential?: string;
};
export type User = {
createdAt: null | string;
email: null | string;
@ -372,7 +376,8 @@ export type Song = {
type BaseEndpointArgs = {
apiClientProps: {
server: null | ServerListItem;
server?: null | ServerListItemWithCredential;
serverId: string;
signal?: AbortSignal;
};
};
@ -829,7 +834,6 @@ export enum PlaylistListSort {
export type AddToPlaylistArgs = BaseEndpointArgs & {
body: AddToPlaylistBody;
query: AddToPlaylistQuery;
serverId?: string;
};
export type AddToPlaylistBody = {
@ -843,7 +847,7 @@ export type AddToPlaylistQuery = {
// Add to playlist
export type AddToPlaylistResponse = null | undefined;
export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody; serverId?: string };
export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody };
export type CreatePlaylistBody = {
_custom?: {
@ -864,7 +868,6 @@ export type CreatePlaylistResponse = undefined | { id: string };
export type DeletePlaylistArgs = BaseEndpointArgs & {
query: DeletePlaylistQuery;
serverId?: string;
};
export type DeletePlaylistQuery = { id: string };
@ -872,7 +875,7 @@ export type DeletePlaylistQuery = { id: string };
// Delete Playlist
export type DeletePlaylistResponse = null | undefined;
export type FavoriteArgs = BaseEndpointArgs & { query: FavoriteQuery; serverId?: string };
export type FavoriteArgs = BaseEndpointArgs & { query: FavoriteQuery };
export type FavoriteQuery = {
id: string[];
@ -909,7 +912,6 @@ export type RatingResponse = null | undefined;
export type RemoveFromPlaylistArgs = BaseEndpointArgs & {
query: RemoveFromPlaylistQuery;
serverId?: string;
};
export type RemoveFromPlaylistQuery = {
@ -920,9 +922,9 @@ export type RemoveFromPlaylistQuery = {
// Remove from playlist
export type RemoveFromPlaylistResponse = null | undefined;
export type SetRatingArgs = BaseEndpointArgs & { query: RatingQuery; serverId?: string };
export type SetRatingArgs = BaseEndpointArgs & { query: RatingQuery };
export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody; serverId?: string };
export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody };
export type ShareItemBody = {
description: string;
@ -938,7 +940,6 @@ export type ShareItemResponse = undefined | { id: string };
export type UpdatePlaylistArgs = BaseEndpointArgs & {
body: UpdatePlaylistBody;
query: UpdatePlaylistQuery;
serverId?: string;
};
export type UpdatePlaylistBody = {
@ -1135,7 +1136,6 @@ export type RandomSongListResponse = SongListResponse;
export type ScrobbleArgs = BaseEndpointArgs & {
query: ScrobbleQuery;
serverId?: string;
};
export type ScrobbleQuery = {
@ -1279,6 +1279,85 @@ export type FontData = {
style: string;
};
export type InternalControllerEndpoint = {
addToPlaylist: (
args: ReplaceApiClientProps<AddToPlaylistArgs>,
) => Promise<AddToPlaylistResponse>;
authenticate: (
url: string,
body: { legacy?: boolean; password: string; username: string },
) => Promise<AuthenticationResponse>;
createFavorite: (args: ReplaceApiClientProps<FavoriteArgs>) => Promise<FavoriteResponse>;
createPlaylist: (
args: ReplaceApiClientProps<CreatePlaylistArgs>,
) => Promise<CreatePlaylistResponse>;
deleteFavorite: (args: ReplaceApiClientProps<FavoriteArgs>) => Promise<FavoriteResponse>;
deletePlaylist: (
args: ReplaceApiClientProps<DeletePlaylistArgs>,
) => Promise<DeletePlaylistResponse>;
getAlbumArtistDetail: (
args: ReplaceApiClientProps<AlbumArtistDetailArgs>,
) => Promise<AlbumArtistDetailResponse>;
getAlbumArtistList: (
args: ReplaceApiClientProps<AlbumArtistListArgs>,
) => Promise<AlbumArtistListResponse>;
getAlbumArtistListCount: (
args: ReplaceApiClientProps<AlbumArtistListCountArgs>,
) => Promise<number>;
getAlbumDetail: (args: ReplaceApiClientProps<AlbumDetailArgs>) => Promise<AlbumDetailResponse>;
getAlbumInfo?: (args: ReplaceApiClientProps<AlbumDetailArgs>) => Promise<AlbumInfo>;
getAlbumList: (args: ReplaceApiClientProps<AlbumListArgs>) => Promise<AlbumListResponse>;
getAlbumListCount: (args: ReplaceApiClientProps<AlbumListCountArgs>) => Promise<number>;
// getArtistInfo?: (args: any) => void;
getArtistList: (args: ReplaceApiClientProps<ArtistListArgs>) => Promise<ArtistListResponse>;
getArtistListCount: (args: ReplaceApiClientProps<ArtistListCountArgs>) => Promise<number>;
getDownloadUrl: (args: ReplaceApiClientProps<DownloadArgs>) => string;
getGenreList: (args: ReplaceApiClientProps<GenreListArgs>) => Promise<GenreListResponse>;
getLyrics?: (args: ReplaceApiClientProps<LyricsArgs>) => Promise<LyricsResponse>;
getMusicFolderList: (
args: ReplaceApiClientProps<MusicFolderListArgs>,
) => Promise<MusicFolderListResponse>;
getPlaylistDetail: (
args: ReplaceApiClientProps<PlaylistDetailArgs>,
) => Promise<PlaylistDetailResponse>;
getPlaylistList: (
args: ReplaceApiClientProps<PlaylistListArgs>,
) => Promise<PlaylistListResponse>;
getPlaylistListCount: (args: ReplaceApiClientProps<PlaylistListCountArgs>) => Promise<number>;
getPlaylistSongList: (
args: ReplaceApiClientProps<PlaylistSongListArgs>,
) => Promise<SongListResponse>;
getRandomSongList: (
args: ReplaceApiClientProps<RandomSongListArgs>,
) => Promise<SongListResponse>;
getRoles: (
args: ReplaceApiClientProps<BaseEndpointArgs>,
) => Promise<Array<string | { label: string; value: string }>>;
getServerInfo: (args: ReplaceApiClientProps<ServerInfoArgs>) => Promise<ServerInfo>;
getSimilarSongs: (args: ReplaceApiClientProps<SimilarSongsArgs>) => Promise<Song[]>;
getSongDetail: (args: ReplaceApiClientProps<SongDetailArgs>) => Promise<SongDetailResponse>;
getSongList: (args: ReplaceApiClientProps<SongListArgs>) => Promise<SongListResponse>;
getSongListCount: (args: ReplaceApiClientProps<SongListCountArgs>) => Promise<number>;
getStructuredLyrics?: (
args: ReplaceApiClientProps<StructuredLyricsArgs>,
) => Promise<StructuredLyric[]>;
getTags?: (args: ReplaceApiClientProps<TagArgs>) => Promise<TagResponses>;
getTopSongs: (args: ReplaceApiClientProps<TopSongListArgs>) => Promise<TopSongListResponse>;
getTranscodingUrl: (args: ReplaceApiClientProps<TranscodingArgs>) => string;
getUserList?: (args: ReplaceApiClientProps<UserListArgs>) => Promise<UserListResponse>;
movePlaylistItem?: (args: ReplaceApiClientProps<MoveItemArgs>) => Promise<void>;
removeFromPlaylist: (
args: ReplaceApiClientProps<RemoveFromPlaylistArgs>,
) => Promise<RemoveFromPlaylistResponse>;
scrobble: (args: ReplaceApiClientProps<ScrobbleArgs>) => Promise<ScrobbleResponse>;
search: (args: ReplaceApiClientProps<SearchArgs>) => Promise<SearchResponse>;
setRating?: (args: ReplaceApiClientProps<SetRatingArgs>) => Promise<RatingResponse>;
shareItem?: (args: ReplaceApiClientProps<ShareItemArgs>) => Promise<ShareItemResponse>;
updatePlaylist: (
args: ReplaceApiClientProps<UpdatePlaylistArgs>,
) => Promise<UpdatePlaylistResponse>;
};
export type LyricGetQuery = {
remoteSongId: string;
remoteSource: LyricSource;
@ -1305,6 +1384,8 @@ export type MoveItemQuery = {
trackId: string;
};
export type ReplaceApiClientProps<T> = BaseEndpointArgsWithServer & Omit<T, 'apiClientProps'>;
export type ServerInfo = {
features: ServerFeatures;
id?: string;
@ -1370,6 +1451,14 @@ export type TranscodingQuery = {
format?: string;
};
type BaseEndpointArgsWithServer = {
apiClientProps: {
server: null | ServerListItemWithCredential;
serverId: string;
signal?: AbortSignal;
};
};
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: SortOrder) => {
let results = albums;