mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 10:23:33 +00:00
Lint all files
This commit is contained in:
parent
22af76b4d6
commit
30e52ebb54
334 changed files with 76519 additions and 75932 deletions
|
|
@ -10,196 +10,198 @@ import { toast } from '/@/renderer/components/toast/index';
|
|||
const c = initContract();
|
||||
|
||||
export const contract = c.router({
|
||||
authenticate: {
|
||||
method: 'GET',
|
||||
path: 'ping.view',
|
||||
query: ssType._parameters.authenticate,
|
||||
responses: {
|
||||
200: ssType._response.authenticate,
|
||||
authenticate: {
|
||||
method: 'GET',
|
||||
path: 'ping.view',
|
||||
query: ssType._parameters.authenticate,
|
||||
responses: {
|
||||
200: ssType._response.authenticate,
|
||||
},
|
||||
},
|
||||
},
|
||||
createFavorite: {
|
||||
method: 'GET',
|
||||
path: 'star.view',
|
||||
query: ssType._parameters.createFavorite,
|
||||
responses: {
|
||||
200: ssType._response.createFavorite,
|
||||
createFavorite: {
|
||||
method: 'GET',
|
||||
path: 'star.view',
|
||||
query: ssType._parameters.createFavorite,
|
||||
responses: {
|
||||
200: ssType._response.createFavorite,
|
||||
},
|
||||
},
|
||||
},
|
||||
getArtistInfo: {
|
||||
method: 'GET',
|
||||
path: 'getArtistInfo.view',
|
||||
query: ssType._parameters.artistInfo,
|
||||
responses: {
|
||||
200: ssType._response.artistInfo,
|
||||
getArtistInfo: {
|
||||
method: 'GET',
|
||||
path: 'getArtistInfo.view',
|
||||
query: ssType._parameters.artistInfo,
|
||||
responses: {
|
||||
200: ssType._response.artistInfo,
|
||||
},
|
||||
},
|
||||
},
|
||||
getMusicFolderList: {
|
||||
method: 'GET',
|
||||
path: 'getMusicFolders.view',
|
||||
responses: {
|
||||
200: ssType._response.musicFolderList,
|
||||
getMusicFolderList: {
|
||||
method: 'GET',
|
||||
path: 'getMusicFolders.view',
|
||||
responses: {
|
||||
200: ssType._response.musicFolderList,
|
||||
},
|
||||
},
|
||||
},
|
||||
getRandomSongList: {
|
||||
method: 'GET',
|
||||
path: 'getRandomSongs.view',
|
||||
query: ssType._parameters.randomSongList,
|
||||
responses: {
|
||||
200: ssType._response.randomSongList,
|
||||
getRandomSongList: {
|
||||
method: 'GET',
|
||||
path: 'getRandomSongs.view',
|
||||
query: ssType._parameters.randomSongList,
|
||||
responses: {
|
||||
200: ssType._response.randomSongList,
|
||||
},
|
||||
},
|
||||
},
|
||||
getTopSongsList: {
|
||||
method: 'GET',
|
||||
path: 'getTopSongs.view',
|
||||
query: ssType._parameters.topSongsList,
|
||||
responses: {
|
||||
200: ssType._response.topSongsList,
|
||||
getTopSongsList: {
|
||||
method: 'GET',
|
||||
path: 'getTopSongs.view',
|
||||
query: ssType._parameters.topSongsList,
|
||||
responses: {
|
||||
200: ssType._response.topSongsList,
|
||||
},
|
||||
},
|
||||
},
|
||||
removeFavorite: {
|
||||
method: 'GET',
|
||||
path: 'unstar.view',
|
||||
query: ssType._parameters.removeFavorite,
|
||||
responses: {
|
||||
200: ssType._response.removeFavorite,
|
||||
removeFavorite: {
|
||||
method: 'GET',
|
||||
path: 'unstar.view',
|
||||
query: ssType._parameters.removeFavorite,
|
||||
responses: {
|
||||
200: ssType._response.removeFavorite,
|
||||
},
|
||||
},
|
||||
},
|
||||
scrobble: {
|
||||
method: 'GET',
|
||||
path: 'scrobble.view',
|
||||
query: ssType._parameters.scrobble,
|
||||
responses: {
|
||||
200: ssType._response.scrobble,
|
||||
scrobble: {
|
||||
method: 'GET',
|
||||
path: 'scrobble.view',
|
||||
query: ssType._parameters.scrobble,
|
||||
responses: {
|
||||
200: ssType._response.scrobble,
|
||||
},
|
||||
},
|
||||
},
|
||||
search3: {
|
||||
method: 'GET',
|
||||
path: 'search3.view',
|
||||
query: ssType._parameters.search3,
|
||||
responses: {
|
||||
200: ssType._response.search3,
|
||||
search3: {
|
||||
method: 'GET',
|
||||
path: 'search3.view',
|
||||
query: ssType._parameters.search3,
|
||||
responses: {
|
||||
200: ssType._response.search3,
|
||||
},
|
||||
},
|
||||
},
|
||||
setRating: {
|
||||
method: 'GET',
|
||||
path: 'setRating.view',
|
||||
query: ssType._parameters.setRating,
|
||||
responses: {
|
||||
200: ssType._response.setRating,
|
||||
setRating: {
|
||||
method: 'GET',
|
||||
path: 'setRating.view',
|
||||
query: ssType._parameters.setRating,
|
||||
responses: {
|
||||
200: ssType._response.setRating,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const axiosClient = axios.create({});
|
||||
|
||||
axiosClient.defaults.paramsSerializer = (params) => {
|
||||
return qs.stringify(params, { arrayFormat: 'repeat' });
|
||||
return qs.stringify(params, { arrayFormat: 'repeat' });
|
||||
};
|
||||
|
||||
axiosClient.interceptors.response.use(
|
||||
(response) => {
|
||||
const data = response.data;
|
||||
(response) => {
|
||||
const data = response.data;
|
||||
|
||||
if (data['subsonic-response'].status !== 'ok') {
|
||||
// Suppress code related to non-linked lastfm or spotify from Navidrome
|
||||
if (data['subsonic-response'].error.code !== 0) {
|
||||
toast.error({
|
||||
message: data['subsonic-response'].error.message,
|
||||
title: 'Issue from Subsonic API',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (data['subsonic-response'].status !== 'ok') {
|
||||
// Suppress code related to non-linked lastfm or spotify from Navidrome
|
||||
if (data['subsonic-response'].error.code !== 0) {
|
||||
toast.error({
|
||||
message: data['subsonic-response'].error.message,
|
||||
title: 'Issue from Subsonic API',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const parsePath = (fullPath: string) => {
|
||||
const [path, params] = fullPath.split('?');
|
||||
const [path, params] = fullPath.split('?');
|
||||
|
||||
const parsedParams = qs.parse(params);
|
||||
const notNilParams = omitBy(parsedParams, (value) => value === 'undefined' || value === 'null');
|
||||
const parsedParams = qs.parse(params);
|
||||
const notNilParams = omitBy(parsedParams, (value) => value === 'undefined' || value === 'null');
|
||||
|
||||
return {
|
||||
params: notNilParams,
|
||||
path,
|
||||
};
|
||||
return {
|
||||
params: notNilParams,
|
||||
path,
|
||||
};
|
||||
};
|
||||
|
||||
export const ssApiClient = (args: {
|
||||
server: ServerListItem | null;
|
||||
signal?: AbortSignal;
|
||||
url?: string;
|
||||
server: ServerListItem | null;
|
||||
signal?: AbortSignal;
|
||||
url?: string;
|
||||
}) => {
|
||||
const { server, url, signal } = args;
|
||||
const { server, url, signal } = args;
|
||||
|
||||
return initClient(contract, {
|
||||
api: async ({ path, method, headers, body }) => {
|
||||
let baseUrl: string | undefined;
|
||||
const authParams: Record<string, any> = {};
|
||||
return initClient(contract, {
|
||||
api: async ({ path, method, headers, body }) => {
|
||||
let baseUrl: string | undefined;
|
||||
const authParams: Record<string, any> = {};
|
||||
|
||||
const { params, path: api } = parsePath(path);
|
||||
const { params, path: api } = parsePath(path);
|
||||
|
||||
if (server) {
|
||||
baseUrl = `${server.url}/rest`;
|
||||
const token = server.credential;
|
||||
const params = token.split(/&?\w=/gm);
|
||||
if (server) {
|
||||
baseUrl = `${server.url}/rest`;
|
||||
const token = server.credential;
|
||||
const params = token.split(/&?\w=/gm);
|
||||
|
||||
authParams.u = server.username;
|
||||
if (params?.length === 4) {
|
||||
authParams.s = params[2];
|
||||
authParams.t = params[3];
|
||||
} else if (params?.length === 3) {
|
||||
authParams.p = params[2];
|
||||
}
|
||||
} else {
|
||||
baseUrl = url;
|
||||
}
|
||||
authParams.u = server.username;
|
||||
if (params?.length === 4) {
|
||||
authParams.s = params[2];
|
||||
authParams.t = params[3];
|
||||
} else if (params?.length === 3) {
|
||||
authParams.p = params[2];
|
||||
}
|
||||
} else {
|
||||
baseUrl = url;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await axiosClient.request<z.infer<typeof ssType._response.baseResponse>>({
|
||||
data: body,
|
||||
headers,
|
||||
method: method as Method,
|
||||
params: {
|
||||
c: 'Feishin',
|
||||
f: 'json',
|
||||
v: '1.13.0',
|
||||
...authParams,
|
||||
...params,
|
||||
},
|
||||
signal,
|
||||
url: `${baseUrl}/${api}`,
|
||||
});
|
||||
try {
|
||||
const result = await axiosClient.request<
|
||||
z.infer<typeof ssType._response.baseResponse>
|
||||
>({
|
||||
data: body,
|
||||
headers,
|
||||
method: method as Method,
|
||||
params: {
|
||||
c: 'Feishin',
|
||||
f: 'json',
|
||||
v: '1.13.0',
|
||||
...authParams,
|
||||
...params,
|
||||
},
|
||||
signal,
|
||||
url: `${baseUrl}/${api}`,
|
||||
});
|
||||
|
||||
return {
|
||||
body: result.data['subsonic-response'],
|
||||
headers: result.headers as any,
|
||||
status: result.status,
|
||||
};
|
||||
} catch (e: Error | AxiosError | any) {
|
||||
console.log('CATCH ERR');
|
||||
return {
|
||||
body: result.data['subsonic-response'],
|
||||
headers: result.headers as any,
|
||||
status: result.status,
|
||||
};
|
||||
} catch (e: Error | AxiosError | any) {
|
||||
console.log('CATCH ERR');
|
||||
|
||||
if (isAxiosError(e)) {
|
||||
const error = e as AxiosError;
|
||||
const response = error.response as AxiosResponse;
|
||||
if (isAxiosError(e)) {
|
||||
const error = e as AxiosError;
|
||||
const response = error.response as AxiosResponse;
|
||||
|
||||
return {
|
||||
body: response?.data,
|
||||
headers: response.headers as any,
|
||||
status: response?.status,
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
baseHeaders: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
baseUrl: '',
|
||||
});
|
||||
return {
|
||||
body: response?.data,
|
||||
headers: response.headers as any,
|
||||
status: response?.status,
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
baseHeaders: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
baseUrl: '',
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,91 +4,91 @@ import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
|
|||
import { ssNormalize } from '/@/renderer/api/subsonic/subsonic-normalize';
|
||||
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
|
||||
import {
|
||||
ArtistInfoArgs,
|
||||
AuthenticationResponse,
|
||||
FavoriteArgs,
|
||||
FavoriteResponse,
|
||||
LibraryItem,
|
||||
MusicFolderListArgs,
|
||||
MusicFolderListResponse,
|
||||
SetRatingArgs,
|
||||
RatingResponse,
|
||||
ScrobbleArgs,
|
||||
ScrobbleResponse,
|
||||
SongListResponse,
|
||||
TopSongListArgs,
|
||||
SearchArgs,
|
||||
SearchResponse,
|
||||
RandomSongListResponse,
|
||||
RandomSongListArgs,
|
||||
ArtistInfoArgs,
|
||||
AuthenticationResponse,
|
||||
FavoriteArgs,
|
||||
FavoriteResponse,
|
||||
LibraryItem,
|
||||
MusicFolderListArgs,
|
||||
MusicFolderListResponse,
|
||||
SetRatingArgs,
|
||||
RatingResponse,
|
||||
ScrobbleArgs,
|
||||
ScrobbleResponse,
|
||||
SongListResponse,
|
||||
TopSongListArgs,
|
||||
SearchArgs,
|
||||
SearchResponse,
|
||||
RandomSongListResponse,
|
||||
RandomSongListArgs,
|
||||
} from '/@/renderer/api/types';
|
||||
import { randomString } from '/@/renderer/utils';
|
||||
|
||||
const authenticate = async (
|
||||
url: string,
|
||||
body: {
|
||||
legacy?: boolean;
|
||||
password: string;
|
||||
username: string;
|
||||
},
|
||||
): Promise<AuthenticationResponse> => {
|
||||
let credential: string;
|
||||
let credentialParams: {
|
||||
p?: string;
|
||||
s?: string;
|
||||
t?: string;
|
||||
u: string;
|
||||
};
|
||||
|
||||
const cleanServerUrl = url.replace(/\/$/, '');
|
||||
|
||||
if (body.legacy) {
|
||||
credential = `u=${body.username}&p=${body.password}`;
|
||||
credentialParams = {
|
||||
p: body.password,
|
||||
u: body.username,
|
||||
};
|
||||
} else {
|
||||
const salt = randomString(12);
|
||||
const hash = md5(body.password + salt);
|
||||
credential = `u=${body.username}&s=${salt}&t=${hash}`;
|
||||
credentialParams = {
|
||||
s: salt,
|
||||
t: hash,
|
||||
u: body.username,
|
||||
};
|
||||
}
|
||||
|
||||
await ssApiClient({ server: null, url: cleanServerUrl }).authenticate({
|
||||
query: {
|
||||
c: 'Feishin',
|
||||
f: 'json',
|
||||
v: '1.13.0',
|
||||
...credentialParams,
|
||||
url: string,
|
||||
body: {
|
||||
legacy?: boolean;
|
||||
password: string;
|
||||
username: string;
|
||||
},
|
||||
});
|
||||
): Promise<AuthenticationResponse> => {
|
||||
let credential: string;
|
||||
let credentialParams: {
|
||||
p?: string;
|
||||
s?: string;
|
||||
t?: string;
|
||||
u: string;
|
||||
};
|
||||
|
||||
return {
|
||||
credential,
|
||||
userId: null,
|
||||
username: body.username,
|
||||
};
|
||||
const cleanServerUrl = url.replace(/\/$/, '');
|
||||
|
||||
if (body.legacy) {
|
||||
credential = `u=${body.username}&p=${body.password}`;
|
||||
credentialParams = {
|
||||
p: body.password,
|
||||
u: body.username,
|
||||
};
|
||||
} else {
|
||||
const salt = randomString(12);
|
||||
const hash = md5(body.password + salt);
|
||||
credential = `u=${body.username}&s=${salt}&t=${hash}`;
|
||||
credentialParams = {
|
||||
s: salt,
|
||||
t: hash,
|
||||
u: body.username,
|
||||
};
|
||||
}
|
||||
|
||||
await ssApiClient({ server: null, url: cleanServerUrl }).authenticate({
|
||||
query: {
|
||||
c: 'Feishin',
|
||||
f: 'json',
|
||||
v: '1.13.0',
|
||||
...credentialParams,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
credential,
|
||||
userId: null,
|
||||
username: body.username,
|
||||
};
|
||||
};
|
||||
|
||||
const getMusicFolderList = async (args: MusicFolderListArgs): Promise<MusicFolderListResponse> => {
|
||||
const { apiClientProps } = args;
|
||||
const { apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).getMusicFolderList({});
|
||||
const res = await ssApiClient(apiClientProps).getMusicFolderList({});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get music folder list');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get music folder list');
|
||||
}
|
||||
|
||||
return {
|
||||
items: res.body.musicFolders.musicFolder,
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.musicFolders.musicFolder.length,
|
||||
};
|
||||
return {
|
||||
items: res.body.musicFolders.musicFolder,
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.musicFolders.musicFolder.length,
|
||||
};
|
||||
};
|
||||
|
||||
// export const getAlbumArtistDetail = async (
|
||||
|
|
@ -198,184 +198,185 @@ const getMusicFolderList = async (args: MusicFolderListArgs): Promise<MusicFolde
|
|||
// };
|
||||
|
||||
const createFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).createFavorite({
|
||||
query: {
|
||||
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
|
||||
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
|
||||
id: query.type === LibraryItem.SONG ? query.id : undefined,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).createFavorite({
|
||||
query: {
|
||||
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
|
||||
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
|
||||
id: query.type === LibraryItem.SONG ? query.id : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to create favorite');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to create favorite');
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
const removeFavorite = async (args: FavoriteArgs): Promise<FavoriteResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).removeFavorite({
|
||||
query: {
|
||||
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
|
||||
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
|
||||
id: query.type === LibraryItem.SONG ? query.id : undefined,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).removeFavorite({
|
||||
query: {
|
||||
albumId: query.type === LibraryItem.ALBUM ? query.id : undefined,
|
||||
artistId: query.type === LibraryItem.ALBUM_ARTIST ? query.id : undefined,
|
||||
id: query.type === LibraryItem.SONG ? query.id : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to delete favorite');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to delete favorite');
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
const setRating = async (args: SetRatingArgs): Promise<RatingResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const itemIds = query.item.map((item) => item.id);
|
||||
const itemIds = query.item.map((item) => item.id);
|
||||
|
||||
for (const id of itemIds) {
|
||||
await ssApiClient(apiClientProps).setRating({
|
||||
query: {
|
||||
id,
|
||||
rating: query.rating,
|
||||
},
|
||||
});
|
||||
}
|
||||
for (const id of itemIds) {
|
||||
await ssApiClient(apiClientProps).setRating({
|
||||
query: {
|
||||
id,
|
||||
rating: query.rating,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
const getTopSongList = async (args: TopSongListArgs): Promise<SongListResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).getTopSongsList({
|
||||
query: {
|
||||
artist: query.artist,
|
||||
count: query.limit,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).getTopSongsList({
|
||||
query: {
|
||||
artist: query.artist,
|
||||
count: query.limit,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get top songs');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get top songs');
|
||||
}
|
||||
|
||||
return {
|
||||
items:
|
||||
res.body.topSongs?.song?.map((song) => ssNormalize.song(song, apiClientProps.server, '')) ||
|
||||
[],
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.topSongs?.song?.length || 0,
|
||||
};
|
||||
return {
|
||||
items:
|
||||
res.body.topSongs?.song?.map((song) =>
|
||||
ssNormalize.song(song, apiClientProps.server, ''),
|
||||
) || [],
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.topSongs?.song?.length || 0,
|
||||
};
|
||||
};
|
||||
|
||||
const getArtistInfo = async (
|
||||
args: ArtistInfoArgs,
|
||||
args: ArtistInfoArgs,
|
||||
): Promise<z.infer<typeof ssType._response.artistInfo>> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).getArtistInfo({
|
||||
query: {
|
||||
count: query.limit,
|
||||
id: query.artistId,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).getArtistInfo({
|
||||
query: {
|
||||
count: query.limit,
|
||||
id: query.artistId,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get artist info');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get artist info');
|
||||
}
|
||||
|
||||
return res.body;
|
||||
return res.body;
|
||||
};
|
||||
|
||||
const scrobble = async (args: ScrobbleArgs): Promise<ScrobbleResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).scrobble({
|
||||
query: {
|
||||
id: query.id,
|
||||
submission: query.submission,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).scrobble({
|
||||
query: {
|
||||
id: query.id,
|
||||
submission: query.submission,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to scrobble');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to scrobble');
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
const search3 = async (args: SearchArgs): Promise<SearchResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).search3({
|
||||
query: {
|
||||
albumCount: query.albumLimit,
|
||||
albumOffset: query.albumStartIndex,
|
||||
artistCount: query.albumArtistLimit,
|
||||
artistOffset: query.albumArtistStartIndex,
|
||||
query: query.query,
|
||||
songCount: query.songLimit,
|
||||
songOffset: query.songStartIndex,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).search3({
|
||||
query: {
|
||||
albumCount: query.albumLimit,
|
||||
albumOffset: query.albumStartIndex,
|
||||
artistCount: query.albumArtistLimit,
|
||||
artistOffset: query.albumArtistStartIndex,
|
||||
query: query.query,
|
||||
songCount: query.songLimit,
|
||||
songOffset: query.songStartIndex,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to search');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to search');
|
||||
}
|
||||
|
||||
return {
|
||||
albumArtists: res.body.searchResult3?.artist?.map((artist) =>
|
||||
ssNormalize.albumArtist(artist, apiClientProps.server),
|
||||
),
|
||||
albums: res.body.searchResult3?.album?.map((album) =>
|
||||
ssNormalize.album(album, apiClientProps.server),
|
||||
),
|
||||
songs: res.body.searchResult3?.song?.map((song) =>
|
||||
ssNormalize.song(song, apiClientProps.server, ''),
|
||||
),
|
||||
};
|
||||
return {
|
||||
albumArtists: res.body.searchResult3?.artist?.map((artist) =>
|
||||
ssNormalize.albumArtist(artist, apiClientProps.server),
|
||||
),
|
||||
albums: res.body.searchResult3?.album?.map((album) =>
|
||||
ssNormalize.album(album, apiClientProps.server),
|
||||
),
|
||||
songs: res.body.searchResult3?.song?.map((song) =>
|
||||
ssNormalize.song(song, apiClientProps.server, ''),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const getRandomSongList = async (args: RandomSongListArgs): Promise<RandomSongListResponse> => {
|
||||
const { query, apiClientProps } = args;
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const res = await ssApiClient(apiClientProps).getRandomSongList({
|
||||
query: {
|
||||
fromYear: query.minYear,
|
||||
genre: query.genre,
|
||||
musicFolderId: query.musicFolderId,
|
||||
size: query.limit,
|
||||
toYear: query.maxYear,
|
||||
},
|
||||
});
|
||||
const res = await ssApiClient(apiClientProps).getRandomSongList({
|
||||
query: {
|
||||
fromYear: query.minYear,
|
||||
genre: query.genre,
|
||||
musicFolderId: query.musicFolderId,
|
||||
size: query.limit,
|
||||
toYear: query.maxYear,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get random songs');
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get random songs');
|
||||
}
|
||||
|
||||
return {
|
||||
items: res.body.randomSongs?.song?.map((song) =>
|
||||
ssNormalize.song(song, apiClientProps.server, ''),
|
||||
),
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.randomSongs?.song?.length || 0,
|
||||
};
|
||||
return {
|
||||
items: res.body.randomSongs?.song?.map((song) =>
|
||||
ssNormalize.song(song, apiClientProps.server, ''),
|
||||
),
|
||||
startIndex: 0,
|
||||
totalRecordCount: res.body.randomSongs?.song?.length || 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const ssController = {
|
||||
authenticate,
|
||||
createFavorite,
|
||||
getArtistInfo,
|
||||
getMusicFolderList,
|
||||
getRandomSongList,
|
||||
getTopSongList,
|
||||
removeFavorite,
|
||||
scrobble,
|
||||
search3,
|
||||
setRating,
|
||||
authenticate,
|
||||
createFavorite,
|
||||
getArtistInfo,
|
||||
getMusicFolderList,
|
||||
getRandomSongList,
|
||||
getTopSongList,
|
||||
removeFavorite,
|
||||
scrobble,
|
||||
search3,
|
||||
setRating,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,176 +5,178 @@ import { QueueSong, LibraryItem, AlbumArtist, Album } from '/@/renderer/api/type
|
|||
import { ServerListItem, ServerType } from '/@/renderer/types';
|
||||
|
||||
const getCoverArtUrl = (args: {
|
||||
baseUrl: string | undefined;
|
||||
coverArtId?: string;
|
||||
credential: string | undefined;
|
||||
size: number;
|
||||
baseUrl: string | undefined;
|
||||
coverArtId?: string;
|
||||
credential: string | undefined;
|
||||
size: number;
|
||||
}) => {
|
||||
const size = args.size ? args.size : 250;
|
||||
const size = args.size ? args.size : 250;
|
||||
|
||||
if (!args.coverArtId || args.coverArtId.match('2a96cbd8b46e442fc41c2b86b821562f')) {
|
||||
return null;
|
||||
}
|
||||
if (!args.coverArtId || args.coverArtId.match('2a96cbd8b46e442fc41c2b86b821562f')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
`${args.baseUrl}/rest/getCoverArt.view` +
|
||||
`?id=${args.coverArtId}` +
|
||||
`&${args.credential}` +
|
||||
'&v=1.13.0' +
|
||||
'&c=feishin' +
|
||||
`&size=${size}`
|
||||
);
|
||||
return (
|
||||
`${args.baseUrl}/rest/getCoverArt.view` +
|
||||
`?id=${args.coverArtId}` +
|
||||
`&${args.credential}` +
|
||||
'&v=1.13.0' +
|
||||
'&c=feishin' +
|
||||
`&size=${size}`
|
||||
);
|
||||
};
|
||||
|
||||
const normalizeSong = (
|
||||
item: z.infer<typeof ssType._response.song>,
|
||||
server: ServerListItem | null,
|
||||
deviceId: string,
|
||||
item: z.infer<typeof ssType._response.song>,
|
||||
server: ServerListItem | null,
|
||||
deviceId: string,
|
||||
): QueueSong => {
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 100,
|
||||
}) || null;
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 100,
|
||||
}) || null;
|
||||
|
||||
const streamUrl = `${server?.url}/rest/stream.view?id=${item.id}&v=1.13.0&c=feishin_${deviceId}&${server?.credential}`;
|
||||
const streamUrl = `${server?.url}/rest/stream.view?id=${item.id}&v=1.13.0&c=feishin_${deviceId}&${server?.credential}`;
|
||||
|
||||
return {
|
||||
album: item.album || '',
|
||||
albumArtists: [
|
||||
{
|
||||
id: item.artistId || '',
|
||||
imageUrl: null,
|
||||
name: item.artist || '',
|
||||
},
|
||||
],
|
||||
albumId: item.albumId || '',
|
||||
artistName: item.artist || '',
|
||||
artists: [
|
||||
{
|
||||
id: item.artistId || '',
|
||||
imageUrl: null,
|
||||
name: item.artist || '',
|
||||
},
|
||||
],
|
||||
bitRate: item.bitRate || 0,
|
||||
bpm: null,
|
||||
channels: null,
|
||||
comment: null,
|
||||
compilation: null,
|
||||
container: item.contentType,
|
||||
createdAt: item.created,
|
||||
discNumber: item.discNumber || 1,
|
||||
duration: item.duration || 0,
|
||||
genres: item.genre
|
||||
? [
|
||||
{
|
||||
id: item.genre,
|
||||
name: item.genre,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
id: item.id,
|
||||
imagePlaceholderUrl: null,
|
||||
imageUrl,
|
||||
itemType: LibraryItem.SONG,
|
||||
lastPlayedAt: null,
|
||||
lyrics: null,
|
||||
name: item.title,
|
||||
path: item.path,
|
||||
playCount: item?.playCount || 0,
|
||||
releaseDate: null,
|
||||
releaseYear: item.year ? String(item.year) : null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
size: item.size,
|
||||
streamUrl,
|
||||
trackNumber: item.track || 1,
|
||||
uniqueId: nanoid(),
|
||||
updatedAt: '',
|
||||
userFavorite: item.starred || false,
|
||||
userRating: item.userRating || null,
|
||||
};
|
||||
return {
|
||||
album: item.album || '',
|
||||
albumArtists: [
|
||||
{
|
||||
id: item.artistId || '',
|
||||
imageUrl: null,
|
||||
name: item.artist || '',
|
||||
},
|
||||
],
|
||||
albumId: item.albumId || '',
|
||||
artistName: item.artist || '',
|
||||
artists: [
|
||||
{
|
||||
id: item.artistId || '',
|
||||
imageUrl: null,
|
||||
name: item.artist || '',
|
||||
},
|
||||
],
|
||||
bitRate: item.bitRate || 0,
|
||||
bpm: null,
|
||||
channels: null,
|
||||
comment: null,
|
||||
compilation: null,
|
||||
container: item.contentType,
|
||||
createdAt: item.created,
|
||||
discNumber: item.discNumber || 1,
|
||||
duration: item.duration || 0,
|
||||
genres: item.genre
|
||||
? [
|
||||
{
|
||||
id: item.genre,
|
||||
name: item.genre,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
id: item.id,
|
||||
imagePlaceholderUrl: null,
|
||||
imageUrl,
|
||||
itemType: LibraryItem.SONG,
|
||||
lastPlayedAt: null,
|
||||
lyrics: null,
|
||||
name: item.title,
|
||||
path: item.path,
|
||||
playCount: item?.playCount || 0,
|
||||
releaseDate: null,
|
||||
releaseYear: item.year ? String(item.year) : null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
size: item.size,
|
||||
streamUrl,
|
||||
trackNumber: item.track || 1,
|
||||
uniqueId: nanoid(),
|
||||
updatedAt: '',
|
||||
userFavorite: item.starred || false,
|
||||
userRating: item.userRating || null,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeAlbumArtist = (
|
||||
item: z.infer<typeof ssType._response.albumArtist>,
|
||||
server: ServerListItem | null,
|
||||
item: z.infer<typeof ssType._response.albumArtist>,
|
||||
server: ServerListItem | null,
|
||||
): AlbumArtist => {
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 100,
|
||||
}) || null;
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 100,
|
||||
}) || null;
|
||||
|
||||
return {
|
||||
albumCount: item.albumCount ? Number(item.albumCount) : 0,
|
||||
backgroundImageUrl: null,
|
||||
biography: null,
|
||||
duration: null,
|
||||
genres: [],
|
||||
id: item.id,
|
||||
imageUrl,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
lastPlayedAt: null,
|
||||
name: item.name,
|
||||
playCount: null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
similarArtists: [],
|
||||
songCount: null,
|
||||
userFavorite: false,
|
||||
userRating: null,
|
||||
};
|
||||
return {
|
||||
albumCount: item.albumCount ? Number(item.albumCount) : 0,
|
||||
backgroundImageUrl: null,
|
||||
biography: null,
|
||||
duration: null,
|
||||
genres: [],
|
||||
id: item.id,
|
||||
imageUrl,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
lastPlayedAt: null,
|
||||
name: item.name,
|
||||
playCount: null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
similarArtists: [],
|
||||
songCount: null,
|
||||
userFavorite: false,
|
||||
userRating: null,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeAlbum = (
|
||||
item: z.infer<typeof ssType._response.album>,
|
||||
server: ServerListItem | null,
|
||||
item: z.infer<typeof ssType._response.album>,
|
||||
server: ServerListItem | null,
|
||||
): Album => {
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 300,
|
||||
}) || null;
|
||||
const imageUrl =
|
||||
getCoverArtUrl({
|
||||
baseUrl: server?.url,
|
||||
coverArtId: item.coverArt,
|
||||
credential: server?.credential,
|
||||
size: 300,
|
||||
}) || null;
|
||||
|
||||
return {
|
||||
albumArtists: item.artistId ? [{ id: item.artistId, imageUrl: null, name: item.artist }] : [],
|
||||
artists: item.artistId ? [{ id: item.artistId, imageUrl: null, name: item.artist }] : [],
|
||||
backdropImageUrl: null,
|
||||
createdAt: item.created,
|
||||
duration: item.duration,
|
||||
genres: item.genre ? [{ id: item.genre, name: item.genre }] : [],
|
||||
id: item.id,
|
||||
imagePlaceholderUrl: null,
|
||||
imageUrl,
|
||||
isCompilation: null,
|
||||
itemType: LibraryItem.ALBUM,
|
||||
lastPlayedAt: null,
|
||||
name: item.name,
|
||||
playCount: null,
|
||||
releaseDate: item.year ? new Date(item.year, 0, 1).toISOString() : null,
|
||||
releaseYear: item.year ? Number(item.year) : null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
size: null,
|
||||
songCount: item.songCount,
|
||||
songs: [],
|
||||
uniqueId: nanoid(),
|
||||
updatedAt: item.created,
|
||||
userFavorite: item.starred || false,
|
||||
userRating: item.userRating || null,
|
||||
};
|
||||
return {
|
||||
albumArtists: item.artistId
|
||||
? [{ id: item.artistId, imageUrl: null, name: item.artist }]
|
||||
: [],
|
||||
artists: item.artistId ? [{ id: item.artistId, imageUrl: null, name: item.artist }] : [],
|
||||
backdropImageUrl: null,
|
||||
createdAt: item.created,
|
||||
duration: item.duration,
|
||||
genres: item.genre ? [{ id: item.genre, name: item.genre }] : [],
|
||||
id: item.id,
|
||||
imagePlaceholderUrl: null,
|
||||
imageUrl,
|
||||
isCompilation: null,
|
||||
itemType: LibraryItem.ALBUM,
|
||||
lastPlayedAt: null,
|
||||
name: item.name,
|
||||
playCount: null,
|
||||
releaseDate: item.year ? new Date(item.year, 0, 1).toISOString() : null,
|
||||
releaseYear: item.year ? Number(item.year) : null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
size: null,
|
||||
songCount: item.songCount,
|
||||
songs: [],
|
||||
uniqueId: nanoid(),
|
||||
updatedAt: item.created,
|
||||
userFavorite: item.starred || false,
|
||||
userRating: item.userRating || null,
|
||||
};
|
||||
};
|
||||
|
||||
export const ssNormalize = {
|
||||
album: normalizeAlbum,
|
||||
albumArtist: normalizeAlbumArtist,
|
||||
song: normalizeSong,
|
||||
album: normalizeAlbum,
|
||||
albumArtist: normalizeAlbumArtist,
|
||||
song: normalizeSong,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,240 +1,240 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
const baseResponse = z.object({
|
||||
'subsonic-response': z.object({
|
||||
status: z.string(),
|
||||
version: z.string(),
|
||||
}),
|
||||
'subsonic-response': z.object({
|
||||
status: z.string(),
|
||||
version: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
const authenticate = z.null();
|
||||
|
||||
const authenticateParameters = z.object({
|
||||
c: z.string(),
|
||||
f: z.string(),
|
||||
p: z.string().optional(),
|
||||
s: z.string().optional(),
|
||||
t: z.string().optional(),
|
||||
u: z.string(),
|
||||
v: z.string(),
|
||||
c: z.string(),
|
||||
f: z.string(),
|
||||
p: z.string().optional(),
|
||||
s: z.string().optional(),
|
||||
t: z.string().optional(),
|
||||
u: z.string(),
|
||||
v: z.string(),
|
||||
});
|
||||
|
||||
const createFavoriteParameters = z.object({
|
||||
albumId: z.array(z.string()).optional(),
|
||||
artistId: z.array(z.string()).optional(),
|
||||
id: z.array(z.string()).optional(),
|
||||
albumId: z.array(z.string()).optional(),
|
||||
artistId: z.array(z.string()).optional(),
|
||||
id: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const createFavorite = z.null();
|
||||
|
||||
const removeFavoriteParameters = z.object({
|
||||
albumId: z.array(z.string()).optional(),
|
||||
artistId: z.array(z.string()).optional(),
|
||||
id: z.array(z.string()).optional(),
|
||||
albumId: z.array(z.string()).optional(),
|
||||
artistId: z.array(z.string()).optional(),
|
||||
id: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const removeFavorite = z.null();
|
||||
|
||||
const setRatingParameters = z.object({
|
||||
id: z.string(),
|
||||
rating: z.number(),
|
||||
id: z.string(),
|
||||
rating: z.number(),
|
||||
});
|
||||
|
||||
const setRating = z.null();
|
||||
|
||||
const musicFolder = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const musicFolderList = z.object({
|
||||
musicFolders: z.object({
|
||||
musicFolder: z.array(musicFolder),
|
||||
}),
|
||||
musicFolders: z.object({
|
||||
musicFolder: z.array(musicFolder),
|
||||
}),
|
||||
});
|
||||
|
||||
const song = z.object({
|
||||
album: z.string().optional(),
|
||||
albumId: z.string().optional(),
|
||||
artist: z.string().optional(),
|
||||
artistId: z.string().optional(),
|
||||
averageRating: z.number().optional(),
|
||||
bitRate: z.number().optional(),
|
||||
contentType: z.string(),
|
||||
coverArt: z.string().optional(),
|
||||
created: z.string(),
|
||||
discNumber: z.number(),
|
||||
duration: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
id: z.string(),
|
||||
isDir: z.boolean(),
|
||||
isVideo: z.boolean(),
|
||||
parent: z.string(),
|
||||
path: z.string(),
|
||||
playCount: z.number().optional(),
|
||||
size: z.number(),
|
||||
starred: z.boolean().optional(),
|
||||
suffix: z.string(),
|
||||
title: z.string(),
|
||||
track: z.number().optional(),
|
||||
type: z.string(),
|
||||
userRating: z.number().optional(),
|
||||
year: z.number().optional(),
|
||||
album: z.string().optional(),
|
||||
albumId: z.string().optional(),
|
||||
artist: z.string().optional(),
|
||||
artistId: z.string().optional(),
|
||||
averageRating: z.number().optional(),
|
||||
bitRate: z.number().optional(),
|
||||
contentType: z.string(),
|
||||
coverArt: z.string().optional(),
|
||||
created: z.string(),
|
||||
discNumber: z.number(),
|
||||
duration: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
id: z.string(),
|
||||
isDir: z.boolean(),
|
||||
isVideo: z.boolean(),
|
||||
parent: z.string(),
|
||||
path: z.string(),
|
||||
playCount: z.number().optional(),
|
||||
size: z.number(),
|
||||
starred: z.boolean().optional(),
|
||||
suffix: z.string(),
|
||||
title: z.string(),
|
||||
track: z.number().optional(),
|
||||
type: z.string(),
|
||||
userRating: z.number().optional(),
|
||||
year: z.number().optional(),
|
||||
});
|
||||
|
||||
const album = z.object({
|
||||
album: z.string(),
|
||||
artist: z.string(),
|
||||
artistId: z.string(),
|
||||
coverArt: z.string(),
|
||||
created: z.string(),
|
||||
duration: z.number(),
|
||||
genre: z.string().optional(),
|
||||
id: z.string(),
|
||||
isDir: z.boolean(),
|
||||
isVideo: z.boolean(),
|
||||
name: z.string(),
|
||||
parent: z.string(),
|
||||
song: z.array(song),
|
||||
songCount: z.number(),
|
||||
starred: z.boolean().optional(),
|
||||
title: z.string(),
|
||||
userRating: z.number().optional(),
|
||||
year: z.number().optional(),
|
||||
album: z.string(),
|
||||
artist: z.string(),
|
||||
artistId: z.string(),
|
||||
coverArt: z.string(),
|
||||
created: z.string(),
|
||||
duration: z.number(),
|
||||
genre: z.string().optional(),
|
||||
id: z.string(),
|
||||
isDir: z.boolean(),
|
||||
isVideo: z.boolean(),
|
||||
name: z.string(),
|
||||
parent: z.string(),
|
||||
song: z.array(song),
|
||||
songCount: z.number(),
|
||||
starred: z.boolean().optional(),
|
||||
title: z.string(),
|
||||
userRating: z.number().optional(),
|
||||
year: z.number().optional(),
|
||||
});
|
||||
|
||||
const albumListParameters = z.object({
|
||||
fromYear: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
offset: z.number().optional(),
|
||||
size: z.number().optional(),
|
||||
toYear: z.number().optional(),
|
||||
type: z.string().optional(),
|
||||
fromYear: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
offset: z.number().optional(),
|
||||
size: z.number().optional(),
|
||||
toYear: z.number().optional(),
|
||||
type: z.string().optional(),
|
||||
});
|
||||
|
||||
const albumList = z.array(album.omit({ song: true }));
|
||||
|
||||
const albumArtist = z.object({
|
||||
albumCount: z.string(),
|
||||
artistImageUrl: z.string().optional(),
|
||||
coverArt: z.string().optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
albumCount: z.string(),
|
||||
artistImageUrl: z.string().optional(),
|
||||
coverArt: z.string().optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const albumArtistList = z.object({
|
||||
artist: z.array(albumArtist),
|
||||
name: z.string(),
|
||||
artist: z.array(albumArtist),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const artistInfoParameters = z.object({
|
||||
count: z.number().optional(),
|
||||
id: z.string(),
|
||||
includeNotPresent: z.boolean().optional(),
|
||||
count: z.number().optional(),
|
||||
id: z.string(),
|
||||
includeNotPresent: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const artistInfo = z.object({
|
||||
artistInfo: z.object({
|
||||
biography: z.string().optional(),
|
||||
largeImageUrl: z.string().optional(),
|
||||
lastFmUrl: z.string().optional(),
|
||||
mediumImageUrl: z.string().optional(),
|
||||
musicBrainzId: z.string().optional(),
|
||||
similarArtist: z.array(
|
||||
z.object({
|
||||
albumCount: z.string(),
|
||||
artistImageUrl: z.string().optional(),
|
||||
coverArt: z.string().optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
}),
|
||||
),
|
||||
smallImageUrl: z.string().optional(),
|
||||
}),
|
||||
artistInfo: z.object({
|
||||
biography: z.string().optional(),
|
||||
largeImageUrl: z.string().optional(),
|
||||
lastFmUrl: z.string().optional(),
|
||||
mediumImageUrl: z.string().optional(),
|
||||
musicBrainzId: z.string().optional(),
|
||||
similarArtist: z.array(
|
||||
z.object({
|
||||
albumCount: z.string(),
|
||||
artistImageUrl: z.string().optional(),
|
||||
coverArt: z.string().optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
}),
|
||||
),
|
||||
smallImageUrl: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const topSongsListParameters = z.object({
|
||||
artist: z.string(), // The name of the artist, not the artist ID
|
||||
count: z.number().optional(),
|
||||
artist: z.string(), // The name of the artist, not the artist ID
|
||||
count: z.number().optional(),
|
||||
});
|
||||
|
||||
const topSongsList = z.object({
|
||||
topSongs: z.object({
|
||||
song: z.array(song),
|
||||
}),
|
||||
topSongs: z.object({
|
||||
song: z.array(song),
|
||||
}),
|
||||
});
|
||||
|
||||
const scrobbleParameters = z.object({
|
||||
id: z.string(),
|
||||
submission: z.boolean().optional(),
|
||||
time: z.number().optional(), // The time (in milliseconds since 1 Jan 1970) at which the song was listened to.
|
||||
id: z.string(),
|
||||
submission: z.boolean().optional(),
|
||||
time: z.number().optional(), // The time (in milliseconds since 1 Jan 1970) at which the song was listened to.
|
||||
});
|
||||
|
||||
const scrobble = z.null();
|
||||
|
||||
const search3 = z.object({
|
||||
searchResult3: z.object({
|
||||
album: z.array(album),
|
||||
artist: z.array(albumArtist),
|
||||
song: z.array(song),
|
||||
}),
|
||||
searchResult3: z.object({
|
||||
album: z.array(album),
|
||||
artist: z.array(albumArtist),
|
||||
song: z.array(song),
|
||||
}),
|
||||
});
|
||||
|
||||
const search3Parameters = z.object({
|
||||
albumCount: z.number().optional(),
|
||||
albumOffset: z.number().optional(),
|
||||
artistCount: z.number().optional(),
|
||||
artistOffset: z.number().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
query: z.string().optional(),
|
||||
songCount: z.number().optional(),
|
||||
songOffset: z.number().optional(),
|
||||
albumCount: z.number().optional(),
|
||||
albumOffset: z.number().optional(),
|
||||
artistCount: z.number().optional(),
|
||||
artistOffset: z.number().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
query: z.string().optional(),
|
||||
songCount: z.number().optional(),
|
||||
songOffset: z.number().optional(),
|
||||
});
|
||||
|
||||
const randomSongListParameters = z.object({
|
||||
fromYear: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
size: z.number().optional(),
|
||||
toYear: z.number().optional(),
|
||||
fromYear: z.number().optional(),
|
||||
genre: z.string().optional(),
|
||||
musicFolderId: z.string().optional(),
|
||||
size: z.number().optional(),
|
||||
toYear: z.number().optional(),
|
||||
});
|
||||
|
||||
const randomSongList = z.object({
|
||||
randomSongs: z.object({
|
||||
song: z.array(song),
|
||||
}),
|
||||
randomSongs: z.object({
|
||||
song: z.array(song),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ssType = {
|
||||
_parameters: {
|
||||
albumList: albumListParameters,
|
||||
artistInfo: artistInfoParameters,
|
||||
authenticate: authenticateParameters,
|
||||
createFavorite: createFavoriteParameters,
|
||||
randomSongList: randomSongListParameters,
|
||||
removeFavorite: removeFavoriteParameters,
|
||||
scrobble: scrobbleParameters,
|
||||
search3: search3Parameters,
|
||||
setRating: setRatingParameters,
|
||||
topSongsList: topSongsListParameters,
|
||||
},
|
||||
_response: {
|
||||
album,
|
||||
albumArtist,
|
||||
albumArtistList,
|
||||
albumList,
|
||||
artistInfo,
|
||||
authenticate,
|
||||
baseResponse,
|
||||
createFavorite,
|
||||
musicFolderList,
|
||||
randomSongList,
|
||||
removeFavorite,
|
||||
scrobble,
|
||||
search3,
|
||||
setRating,
|
||||
song,
|
||||
topSongsList,
|
||||
},
|
||||
_parameters: {
|
||||
albumList: albumListParameters,
|
||||
artistInfo: artistInfoParameters,
|
||||
authenticate: authenticateParameters,
|
||||
createFavorite: createFavoriteParameters,
|
||||
randomSongList: randomSongListParameters,
|
||||
removeFavorite: removeFavoriteParameters,
|
||||
scrobble: scrobbleParameters,
|
||||
search3: search3Parameters,
|
||||
setRating: setRatingParameters,
|
||||
topSongsList: topSongsListParameters,
|
||||
},
|
||||
_response: {
|
||||
album,
|
||||
albumArtist,
|
||||
albumArtistList,
|
||||
albumList,
|
||||
artistInfo,
|
||||
authenticate,
|
||||
baseResponse,
|
||||
createFavorite,
|
||||
musicFolderList,
|
||||
randomSongList,
|
||||
removeFavorite,
|
||||
scrobble,
|
||||
search3,
|
||||
setRating,
|
||||
song,
|
||||
topSongsList,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue