Pass full server to controller

This commit is contained in:
jeffvli 2023-04-25 16:25:26 -07:00
parent 1cbd61888f
commit 8f042ad448
6 changed files with 144 additions and 181 deletions

View file

@ -1,5 +1,7 @@
import { initClient, initContract } from '@ts-rest/core'; import { initClient, initContract } from '@ts-rest/core';
import axios, { Method, AxiosError, AxiosResponse, isAxiosError } from 'axios'; import axios, { Method, AxiosError, AxiosResponse, isAxiosError } from 'axios';
import omitBy from 'lodash/omitBy';
import qs from 'qs';
import { ndType } from './navidrome-types'; import { ndType } from './navidrome-types';
import { resultWithHeaders } from '/@/renderer/api/utils'; import { resultWithHeaders } from '/@/renderer/api/utils';
import { toast } from '/@/renderer/components'; import { toast } from '/@/renderer/components';
@ -15,6 +17,7 @@ export const contract = c.router({
path: 'playlist/:id/tracks', path: 'playlist/:id/tracks',
responses: { responses: {
200: resultWithHeaders(ndType._response.addToPlaylist), 200: resultWithHeaders(ndType._response.addToPlaylist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
authenticate: { authenticate: {
@ -23,6 +26,7 @@ export const contract = c.router({
path: 'auth/login', path: 'auth/login',
responses: { responses: {
200: resultWithHeaders(ndType._response.authenticate), 200: resultWithHeaders(ndType._response.authenticate),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
createPlaylist: { createPlaylist: {
@ -31,6 +35,7 @@ export const contract = c.router({
path: 'playlist', path: 'playlist',
responses: { responses: {
200: resultWithHeaders(ndType._response.createPlaylist), 200: resultWithHeaders(ndType._response.createPlaylist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
deletePlaylist: { deletePlaylist: {
@ -39,6 +44,7 @@ export const contract = c.router({
path: 'playlist/:id', path: 'playlist/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.deletePlaylist), 200: resultWithHeaders(ndType._response.deletePlaylist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getAlbumArtistDetail: { getAlbumArtistDetail: {
@ -46,6 +52,7 @@ export const contract = c.router({
path: 'artist/:id', path: 'artist/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.albumArtist), 200: resultWithHeaders(ndType._response.albumArtist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getAlbumArtistList: { getAlbumArtistList: {
@ -54,6 +61,7 @@ export const contract = c.router({
query: ndType._parameters.albumArtistList, query: ndType._parameters.albumArtistList,
responses: { responses: {
200: resultWithHeaders(ndType._response.albumArtistList), 200: resultWithHeaders(ndType._response.albumArtistList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getAlbumDetail: { getAlbumDetail: {
@ -61,6 +69,7 @@ export const contract = c.router({
path: 'album/:id', path: 'album/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.album), 200: resultWithHeaders(ndType._response.album),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getAlbumList: { getAlbumList: {
@ -69,6 +78,7 @@ export const contract = c.router({
query: ndType._parameters.albumList, query: ndType._parameters.albumList,
responses: { responses: {
200: resultWithHeaders(ndType._response.albumList), 200: resultWithHeaders(ndType._response.albumList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getGenreList: { getGenreList: {
@ -76,6 +86,7 @@ export const contract = c.router({
path: 'genre', path: 'genre',
responses: { responses: {
200: resultWithHeaders(ndType._response.genreList), 200: resultWithHeaders(ndType._response.genreList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getPlaylistDetail: { getPlaylistDetail: {
@ -83,6 +94,7 @@ export const contract = c.router({
path: 'playlist/:id', path: 'playlist/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.playlist), 200: resultWithHeaders(ndType._response.playlist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getPlaylistList: { getPlaylistList: {
@ -91,6 +103,7 @@ export const contract = c.router({
query: ndType._parameters.playlistList, query: ndType._parameters.playlistList,
responses: { responses: {
200: resultWithHeaders(ndType._response.playlistList), 200: resultWithHeaders(ndType._response.playlistList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getPlaylistSongList: { getPlaylistSongList: {
@ -99,6 +112,7 @@ export const contract = c.router({
query: ndType._parameters.songList, query: ndType._parameters.songList,
responses: { responses: {
200: resultWithHeaders(ndType._response.playlistSongList), 200: resultWithHeaders(ndType._response.playlistSongList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getSongDetail: { getSongDetail: {
@ -106,6 +120,7 @@ export const contract = c.router({
path: 'song/:id', path: 'song/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.song), 200: resultWithHeaders(ndType._response.song),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getSongList: { getSongList: {
@ -114,6 +129,7 @@ export const contract = c.router({
query: ndType._parameters.songList, query: ndType._parameters.songList,
responses: { responses: {
200: resultWithHeaders(ndType._response.songList), 200: resultWithHeaders(ndType._response.songList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
getUserList: { getUserList: {
@ -122,6 +138,7 @@ export const contract = c.router({
query: ndType._parameters.userList, query: ndType._parameters.userList,
responses: { responses: {
200: resultWithHeaders(ndType._response.userList), 200: resultWithHeaders(ndType._response.userList),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
removeFromPlaylist: { removeFromPlaylist: {
@ -131,6 +148,7 @@ export const contract = c.router({
query: ndType._parameters.removeFromPlaylist, query: ndType._parameters.removeFromPlaylist,
responses: { responses: {
200: resultWithHeaders(ndType._response.removeFromPlaylist), 200: resultWithHeaders(ndType._response.removeFromPlaylist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
updatePlaylist: { updatePlaylist: {
@ -139,12 +157,17 @@ export const contract = c.router({
path: 'playlist/:id', path: 'playlist/:id',
responses: { responses: {
200: resultWithHeaders(ndType._response.updatePlaylist), 200: resultWithHeaders(ndType._response.updatePlaylist),
500: resultWithHeaders(ndType._response.error),
}, },
}, },
}); });
const axiosClient = axios.create({}); const axiosClient = axios.create({});
axiosClient.defaults.paramsSerializer = (params) => {
return qs.stringify(params, { arrayFormat: 'repeat' });
};
axiosClient.interceptors.response.use( axiosClient.interceptors.response.use(
(response) => { (response) => {
const serverId = useAuthStore.getState().currentServer?.id; const serverId = useAuthStore.getState().currentServer?.id;
@ -175,20 +198,37 @@ axiosClient.interceptors.response.use(
}, },
); );
export const ndApiClient = (args: { server: ServerListItem | string; signal?: AbortSignal }) => { const parsePath = (fullPath: string) => {
const { server, signal } = args; const [path, params] = fullPath.split('?');
const parsedParams = qs.parse(params);
const notNilParams = omitBy(parsedParams, (value) => value === 'undefined' || value === 'null');
return {
params: notNilParams,
path,
};
};
export const ndApiClient = (args: {
server?: ServerListItem;
signal?: AbortSignal;
url?: string;
}) => {
const { server, url, signal } = args;
return initClient(contract, { return initClient(contract, {
api: async ({ path, method, headers, body }) => { api: async ({ path, method, headers, body }) => {
let baseUrl: string | undefined; let baseUrl: string | undefined;
let token: string | undefined; let token: string | undefined;
if (typeof server === 'object') { const { params, path: api } = parsePath(path);
const selectedServer = useAuthStore.getState().actions.getServer(server.id);
baseUrl = `${selectedServer?.url}/api`; if (server) {
token = selectedServer?.ndCredential; baseUrl = `${server?.url}/api`;
token = server?.ndCredential;
} else { } else {
baseUrl = server; baseUrl = url;
} }
try { try {
@ -199,8 +239,9 @@ export const ndApiClient = (args: { server: ServerListItem | string; signal?: Ab
...(token && { 'x-nd-authorization': `Bearer ${token}` }), ...(token && { 'x-nd-authorization': `Bearer ${token}` }),
}, },
method: method as Method, method: method as Method,
params,
signal, signal,
url: `${baseUrl}/${path}`, url: `${baseUrl}/${api}`,
}); });
return { return {
body: { data: result.data, headers: result.headers }, body: { data: result.data, headers: result.headers },
@ -222,5 +263,6 @@ export const ndApiClient = (args: { server: ServerListItem | string; signal?: Ab
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
baseUrl: '', baseUrl: '',
jsonQuery: false,
}); });
}; };

View file

@ -49,7 +49,7 @@ const authenticate = async (
): Promise<AuthenticationResponse> => { ): Promise<AuthenticationResponse> => {
const cleanServerUrl = url.replace(/\/$/, ''); const cleanServerUrl = url.replace(/\/$/, '');
const res = await ndApiClient({ server: cleanServerUrl }).authenticate({ const res = await ndApiClient({ url: cleanServerUrl }).authenticate({
body: { body: {
password: body.password, password: body.password,
username: body.username, username: body.username,
@ -69,19 +69,15 @@ const authenticate = async (
}; };
const getUserList = async (args: UserListArgs): Promise<UserListResponse> => { const getUserList = async (args: UserListArgs): Promise<UserListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getUserList({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getUserList({
query: { query: {
_end: query.startIndex + (query.limit || 0), _end: query.startIndex + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder], _order: sortOrderMap.navidrome[query.sortOrder],
_sort: userListSortMap.navidrome[query.sortBy], _sort: userListSortMap.navidrome[query.sortBy],
_start: query.startIndex, _start: query.startIndex,
...query.ndParams, ...query._custom?.navidrome,
}, },
}); });
@ -97,13 +93,9 @@ const getUserList = async (args: UserListArgs): Promise<UserListResponse> => {
}; };
const getGenreList = async (args: GenreListArgs): Promise<GenreListResponse> => { const getGenreList = async (args: GenreListArgs): Promise<GenreListResponse> => {
const { server, signal } = args; const { apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getGenreList({});
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getGenreList({});
if (res.status !== 200) { if (res.status !== 200) {
throw new Error('Failed to get genre list'); throw new Error('Failed to get genre list');
@ -119,13 +111,9 @@ const getGenreList = async (args: GenreListArgs): Promise<GenreListResponse> =>
const getAlbumArtistDetail = async ( const getAlbumArtistDetail = async (
args: AlbumArtistDetailArgs, args: AlbumArtistDetailArgs,
): Promise<AlbumArtistDetailResponse> => { ): Promise<AlbumArtistDetailResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getAlbumArtistDetail({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getAlbumArtistDetail({
params: { params: {
id: query.id, id: query.id,
}, },
@ -135,17 +123,17 @@ const getAlbumArtistDetail = async (
throw new Error('Failed to get album artist detail'); throw new Error('Failed to get album artist detail');
} }
return ndNormalize.albumArtist(res.body.data, server); if (!apiClientProps.server) {
throw new Error('Server is required');
}
return ndNormalize.albumArtist(res.body.data, apiClientProps.server);
}; };
const getAlbumArtistList = async (args: AlbumArtistListArgs): Promise<AlbumArtistListResponse> => { const getAlbumArtistList = async (args: AlbumArtistListArgs): Promise<AlbumArtistListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getAlbumArtistList({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getAlbumArtistList({
query: { query: {
_end: query.startIndex + (query.limit || 0), _end: query.startIndex + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder], _order: sortOrderMap.navidrome[query.sortOrder],
@ -160,27 +148,29 @@ const getAlbumArtistList = async (args: AlbumArtistListArgs): Promise<AlbumArtis
throw new Error('Failed to get album artist list'); throw new Error('Failed to get album artist list');
} }
if (!apiClientProps.server) {
throw new Error('Server is required');
}
return { return {
items: res.body.data.map((albumArtist) => ndNormalize.albumArtist(albumArtist, server)), items: res.body.data.map((albumArtist) =>
ndNormalize.albumArtist(albumArtist, apiClientProps.server),
),
startIndex: query.startIndex, startIndex: query.startIndex,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
}; };
const getAlbumDetail = async (args: AlbumDetailArgs): Promise<AlbumDetailResponse> => { const getAlbumDetail = async (args: AlbumDetailArgs): Promise<AlbumDetailResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const albumRes = await ndApiClient(apiClientProps).getAlbumDetail({
throw new Error('No server');
}
const albumRes = await ndApiClient({ server, signal }).getAlbumDetail({
params: { params: {
id: query.id, id: query.id,
}, },
}); });
const songsData = await ndApiClient({ server, signal }).getSongList({ const songsData = await ndApiClient(apiClientProps).getSongList({
query: { query: {
_end: 0, _end: 0,
_order: 'ASC', _order: 'ASC',
@ -194,17 +184,16 @@ const getAlbumDetail = async (args: AlbumDetailArgs): Promise<AlbumDetailRespons
throw new Error('Failed to get album detail'); throw new Error('Failed to get album detail');
} }
return ndNormalize.album({ ...albumRes.body.data, songs: songsData.body.data }, server); return ndNormalize.album(
{ ...albumRes.body.data, songs: songsData.body.data },
apiClientProps.server,
);
}; };
const getAlbumList = async (args: AlbumListArgs): Promise<AlbumListResponse> => { const getAlbumList = async (args: AlbumListArgs): Promise<AlbumListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getAlbumList({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getAlbumList({
query: { query: {
_end: query.startIndex + (query.limit || 0), _end: query.startIndex + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder], _order: sortOrderMap.navidrome[query.sortOrder],
@ -221,20 +210,18 @@ const getAlbumList = async (args: AlbumListArgs): Promise<AlbumListResponse> =>
} }
return { return {
items: res.body.data.map((album) => ndNormalize.album(album, server)), items: res.body.data.map((album) => ndNormalize.album(album, apiClientProps.server)),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
}; };
const getSongList = async (args: SongListArgs): Promise<SongListResponse> => { const getSongList = async (args: SongListArgs): Promise<SongListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { console.log('query :>> ', query);
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getSongList({ const res = await ndApiClient(apiClientProps).getSongList({
query: { query: {
_end: query.startIndex + (query.limit || -1), _end: query.startIndex + (query.limit || -1),
_order: sortOrderMap.navidrome[query.sortOrder], _order: sortOrderMap.navidrome[query.sortOrder],
@ -252,20 +239,16 @@ const getSongList = async (args: SongListArgs): Promise<SongListResponse> => {
} }
return { return {
items: res.body.data.map((song) => ndNormalize.song(song, server, '')), items: res.body.data.map((song) => ndNormalize.song(song, apiClientProps.server, '')),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
}; };
const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse> => { const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getSongDetail({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getSongDetail({
params: { params: {
id: query.id, id: query.id,
}, },
@ -275,23 +258,19 @@ const getSongDetail = async (args: SongDetailArgs): Promise<SongDetailResponse>
throw new Error('Failed to get song detail'); throw new Error('Failed to get song detail');
} }
return ndNormalize.song(res.body.data, server, ''); return ndNormalize.song(res.body.data, apiClientProps.server, '');
}; };
const createPlaylist = async (args: CreatePlaylistArgs): Promise<CreatePlaylistResponse> => { const createPlaylist = async (args: CreatePlaylistArgs): Promise<CreatePlaylistResponse> => {
const { body, server } = args; const { body, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).createPlaylist({
throw new Error('No server');
}
const res = await ndApiClient({ server }).createPlaylist({
body: { body: {
comment: body.comment, comment: body.comment,
name: body.name, name: body.name,
public: body._custom.navidrome?.public, public: body._custom?.navidrome?.public,
rules: body._custom.navidrome?.rules, rules: body._custom?.navidrome?.rules,
sync: body._custom.navidrome?.sync, sync: body._custom?.navidrome?.sync,
}, },
}); });
@ -306,19 +285,15 @@ const createPlaylist = async (args: CreatePlaylistArgs): Promise<CreatePlaylistR
}; };
const updatePlaylist = async (args: UpdatePlaylistArgs): Promise<UpdatePlaylistResponse> => { const updatePlaylist = async (args: UpdatePlaylistArgs): Promise<UpdatePlaylistResponse> => {
const { query, body, server, signal } = args; const { query, body, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).updatePlaylist({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).updatePlaylist({
body: { body: {
comment: body.comment || '', comment: body.comment || '',
name: body.name, name: body.name,
public: body.ndParams?.public || false, public: body._custom?.navidrome?.public || false,
rules: body.ndParams?.rules ? body.ndParams?.rules : undefined, rules: body._custom?.navidrome?.rules ? body._custom.navidrome.rules : undefined,
sync: body.ndParams?.sync || undefined, sync: body._custom?.navidrome?.sync || undefined,
}, },
params: { params: {
id: query.id, id: query.id,
@ -335,13 +310,9 @@ const updatePlaylist = async (args: UpdatePlaylistArgs): Promise<UpdatePlaylistR
}; };
const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistResponse> => { const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistResponse> => {
const { query, server } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).deletePlaylist({
throw new Error('No server');
}
const res = await ndApiClient({ server }).deletePlaylist({
body: null, body: null,
params: { params: {
id: query.id, id: query.id,
@ -356,13 +327,9 @@ const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistR
}; };
const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResponse> => { const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getPlaylistList({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getPlaylistList({
query: { query: {
_end: query.startIndex + (query.limit || 0), _end: query.startIndex + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder], _order: sortOrderMap.navidrome[query.sortOrder],
@ -377,20 +344,16 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResp
} }
return { return {
items: res.body.data.map((item) => ndNormalize.playlist(item, server)), items: res.body.data.map((item) => ndNormalize.playlist(item, apiClientProps.server)),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
}; };
const getPlaylistDetail = async (args: PlaylistDetailArgs): Promise<PlaylistDetailResponse> => { const getPlaylistDetail = async (args: PlaylistDetailArgs): Promise<PlaylistDetailResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getPlaylistDetail({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getPlaylistDetail({
params: { params: {
id: query.id, id: query.id,
}, },
@ -400,19 +363,15 @@ const getPlaylistDetail = async (args: PlaylistDetailArgs): Promise<PlaylistDeta
throw new Error('Failed to get playlist detail'); throw new Error('Failed to get playlist detail');
} }
return ndNormalize.playlist(res.body.data, server); return ndNormalize.playlist(res.body.data, apiClientProps.server);
}; };
const getPlaylistSongList = async ( const getPlaylistSongList = async (
args: PlaylistSongListArgs, args: PlaylistSongListArgs,
): Promise<PlaylistSongListResponse> => { ): Promise<PlaylistSongListResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).getPlaylistSongList({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).getPlaylistSongList({
params: { params: {
id: query.id, id: query.id,
}, },
@ -429,20 +388,16 @@ const getPlaylistSongList = async (
} }
return { return {
items: res.body.data.map((item) => ndNormalize.song(item, server, '')), items: res.body.data.map((item) => ndNormalize.song(item, apiClientProps.server, '')),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
}; };
const addToPlaylist = async (args: AddToPlaylistArgs): Promise<AddToPlaylistResponse> => { const addToPlaylist = async (args: AddToPlaylistArgs): Promise<AddToPlaylistResponse> => {
const { body, query, server } = args; const { body, query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).addToPlaylist({
throw new Error('No server');
}
const res = await ndApiClient({ server }).addToPlaylist({
body: { body: {
ids: body.songId, ids: body.songId,
}, },
@ -461,13 +416,9 @@ const addToPlaylist = async (args: AddToPlaylistArgs): Promise<AddToPlaylistResp
const removeFromPlaylist = async ( const removeFromPlaylist = async (
args: RemoveFromPlaylistArgs, args: RemoveFromPlaylistArgs,
): Promise<RemoveFromPlaylistResponse> => { ): Promise<RemoveFromPlaylistResponse> => {
const { query, server, signal } = args; const { query, apiClientProps } = args;
if (!server) { const res = await ndApiClient(apiClientProps).removeFromPlaylist({
throw new Error('No server');
}
const res = await ndApiClient({ server, signal }).removeFromPlaylist({
body: null, body: null,
params: { params: {
id: query.id, id: query.id,

View file

@ -157,7 +157,7 @@ const normalizeAlbumArtist = (
lastPlayedAt: item.playDate.includes('0001-') ? null : item.playDate, lastPlayedAt: item.playDate.includes('0001-') ? null : item.playDate,
name: item.name, name: item.name,
playCount: item.playCount, playCount: item.playCount,
serverId: server?.id || '', serverId: server.id,
serverType: ServerType.NAVIDROME, serverType: ServerType.NAVIDROME,
similarArtists: null, similarArtists: null,
// similarArtists: // similarArtists:

View file

@ -2,6 +2,8 @@ import { z } from 'zod';
const sortOrderValues = ['ASC', 'DESC'] as const; const sortOrderValues = ['ASC', 'DESC'] as const;
const error = z.string();
const paginationParameters = z.object({ const paginationParameters = z.object({
_end: z.number().optional(), _end: z.number().optional(),
_order: z.enum(sortOrderValues), _order: z.enum(sortOrderValues),
@ -339,6 +341,7 @@ export const ndType = {
authenticate, authenticate,
createPlaylist, createPlaylist,
deletePlaylist, deletePlaylist,
error,
genre, genre,
genreList, genreList,
playlist, playlist,

View file

@ -1,8 +1,8 @@
import { initClient, initContract } from '@ts-rest/core'; import { initClient, initContract } from '@ts-rest/core';
import axios, { Method, AxiosError, isAxiosError, AxiosResponse } from 'axios'; import axios, { Method, AxiosError, isAxiosError, AxiosResponse } from 'axios';
import { ssType } from '/@/renderer/api/subsonic/subsonic-types'; import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
import { ServerListItem } from '/@/renderer/api/types';
import { toast } from '/@/renderer/components'; import { toast } from '/@/renderer/components';
import { useAuthStore } from '/@/renderer/store';
const c = initContract(); const c = initContract();
@ -95,29 +95,24 @@ axiosClient.interceptors.response.use(
}, },
); );
export const ssApiClient = (args: { serverId?: string; signal?: AbortSignal; url?: string }) => { export const ssApiClient = (args: {
const { serverId, url, signal } = args; server?: ServerListItem;
signal?: AbortSignal;
url?: string;
}) => {
const { server, url, signal } = args;
return initClient(contract, { return initClient(contract, {
api: async ({ path, method, headers, body }) => { api: async ({ path, method, headers, body }) => {
let baseUrl: string | undefined; let baseUrl: string | undefined;
const authParams: Record<string, any> = {}; const authParams: Record<string, any> = {};
if (serverId) { if (server) {
const selectedServer = useAuthStore.getState().actions.getServer(serverId); baseUrl = `${server.url}/rest`;
const token = server.credential;
if (!selectedServer) {
return {
body: { data: null, headers: null },
status: 500,
};
}
baseUrl = `${selectedServer?.url}/rest`;
const token = selectedServer.credential;
const params = token.split(/&?\w=/gm); const params = token.split(/&?\w=/gm);
authParams.u = selectedServer.username; authParams.u = server.username;
if (params?.length === 4) { if (params?.length === 4) {
authParams.s = params[2]; authParams.s = params[2];
authParams.t = params[3]; authParams.t = params[3];

View file

@ -72,13 +72,9 @@ const authenticate = async (
}; };
const getMusicFolderList = async (args: MusicFolderListArgs): Promise<MusicFolderListResponse> => { const getMusicFolderList = async (args: MusicFolderListArgs): Promise<MusicFolderListResponse> => {
const { signal, serverId } = args; const { apiClientProps } = args;
if (!serverId) { const res = await ssApiClient(apiClientProps).getMusicFolderList({});
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).getMusicFolderList({});
if (res.status !== 200) { if (res.status !== 200) {
throw new Error('Failed to get music folder list'); throw new Error('Failed to get music folder list');
@ -198,13 +194,9 @@ const getMusicFolderList = async (args: MusicFolderListArgs): Promise<MusicFolde
// }; // };
const createFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => { const createFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => {
const { serverId, query, signal } = args; const { query, apiClientProps } = args;
if (!serverId) { const res = await ssApiClient(apiClientProps).createFavorite({
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).createFavorite({
query: { query: {
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined, albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined, artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
@ -223,13 +215,9 @@ const createFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> =>
}; };
const removeFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => { const removeFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => {
const { serverId, query, signal } = args; const { query, apiClientProps } = args;
if (!serverId) { const res = await ssApiClient(apiClientProps).removeFavorite({
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).removeFavorite({
query: { query: {
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined, albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined, artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
@ -248,16 +236,12 @@ const removeFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> =>
}; };
const setRating = async (args: RatingArgs): Promise<RatingResponse> => { const setRating = async (args: RatingArgs): Promise<RatingResponse> => {
const { serverId, query, signal } = args; const { query, apiClientProps } = args;
if (!serverId) {
throw new Error('No server id');
}
const itemIds = query.item.map((item) => item.id); const itemIds = query.item.map((item) => item.id);
for (const id of itemIds) { for (const id of itemIds) {
await ssApiClient({ serverId, signal }).setRating({ await ssApiClient(apiClientProps).setRating({
query: { query: {
id, id,
rating: query.rating, rating: query.rating,
@ -269,13 +253,9 @@ const setRating = async (args: RatingArgs): Promise<RatingResponse> => {
}; };
const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse> => { const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse> => {
const { signal, serverId, query, server } = args; const { query, apiClientProps } = args;
if (!serverId || !server) { const res = await ssApiClient(apiClientProps).getTopSongsList({
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).getTopSongsList({
query: { query: {
artist: query.artist, artist: query.artist,
count: query.limit, count: query.limit,
@ -287,7 +267,7 @@ const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse>
} }
return { return {
items: res.body.topSongs.song.map((song) => ssNormalize.song(song, server, '')), items: res.body.topSongs.song.map((song) => ssNormalize.song(song, apiClientProps.server, '')),
startIndex: 0, startIndex: 0,
totalRecordCount: res.body.topSongs.song.length || 0, totalRecordCount: res.body.topSongs.song.length || 0,
}; };
@ -296,13 +276,9 @@ const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse>
const getArtistInfo = async ( const getArtistInfo = async (
args: ArtistInfoArgs, args: ArtistInfoArgs,
): Promise<z.infer<typeof ssType._response.artistInfo>> => { ): Promise<z.infer<typeof ssType._response.artistInfo>> => {
const { signal, serverId, query } = args; const { query, apiClientProps } = args;
if (!serverId) { const res = await ssApiClient(apiClientProps).getArtistInfo({
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).getArtistInfo({
query: { query: {
count: query.limit, count: query.limit,
id: query.artistId, id: query.artistId,
@ -317,13 +293,9 @@ const getArtistInfo = async (
}; };
const scrobble = async (args: ScrobbleArgs): Promise<ScrobbleResponse> => { const scrobble = async (args: ScrobbleArgs): Promise<ScrobbleResponse> => {
const { signal, serverId, query } = args; const { query, apiClientProps } = args;
if (!serverId) { const res = await ssApiClient(apiClientProps).scrobble({
throw new Error('No server id');
}
const res = await ssApiClient({ serverId, signal }).scrobble({
query: { query: {
id: query.id, id: query.id,
submission: query.submission, submission: query.submission,