mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Add all relevant subsonic endpoints to ts-rest
This commit is contained in:
@@ -1,93 +1,426 @@
|
||||
import { initClient, initContract } from '@ts-rest/core';
|
||||
import axios, { Method, AxiosError, isAxiosError, AxiosResponse } from 'axios';
|
||||
import axios, { AxiosError, AxiosResponse, Method, isAxiosError } from 'axios';
|
||||
import omitBy from 'lodash/omitBy';
|
||||
import qs from 'qs';
|
||||
import { z } from 'zod';
|
||||
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { SubsonicApi } from '/@/renderer/api/subsonic/subsonic-types';
|
||||
import { ServerListItem } from '/@/renderer/api/types';
|
||||
import { toast } from '/@/renderer/components/toast/index';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
|
||||
const c = initContract();
|
||||
|
||||
export const contract = c.router({
|
||||
authenticate: {
|
||||
changePassword: {
|
||||
method: 'GET',
|
||||
path: 'ping.view',
|
||||
query: ssType._parameters.authenticate,
|
||||
path: 'changePassword.view',
|
||||
query: SubsonicApi.changePassword.parameters,
|
||||
responses: {
|
||||
200: ssType._response.authenticate,
|
||||
200: SubsonicApi.changePassword.response,
|
||||
},
|
||||
},
|
||||
createFavorite: {
|
||||
createInternetRadioStation: {
|
||||
method: 'GET',
|
||||
path: 'star.view',
|
||||
query: ssType._parameters.createFavorite,
|
||||
path: 'createInternetRadioStation.view',
|
||||
query: SubsonicApi.createInternetRadioStation.parameters,
|
||||
responses: {
|
||||
200: ssType._response.createFavorite,
|
||||
200: SubsonicApi.createInternetRadioStation.response,
|
||||
},
|
||||
},
|
||||
createPlaylist: {
|
||||
method: 'GET',
|
||||
path: 'createPlaylist.view',
|
||||
query: SubsonicApi.createPlaylist.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.createPlaylist.response,
|
||||
},
|
||||
},
|
||||
createShare: {
|
||||
method: 'GET',
|
||||
path: 'createShare.view',
|
||||
query: SubsonicApi.createShare.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.createShare.response,
|
||||
},
|
||||
},
|
||||
createUser: {
|
||||
method: 'GET',
|
||||
path: 'createUser.view',
|
||||
query: SubsonicApi.createUser.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.createUser.response,
|
||||
},
|
||||
},
|
||||
deleteInternetRadioStation: {
|
||||
method: 'GET',
|
||||
path: 'deleteInternetRadioStation.view',
|
||||
query: SubsonicApi.deleteInternetRadioStation.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.deleteInternetRadioStation.response,
|
||||
},
|
||||
},
|
||||
deletePlaylist: {
|
||||
method: 'GET',
|
||||
path: 'deletePlaylist.view',
|
||||
query: SubsonicApi.deletePlaylist.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.deletePlaylist.response,
|
||||
},
|
||||
},
|
||||
deleteShare: {
|
||||
method: 'GET',
|
||||
path: 'deleteShare.view',
|
||||
query: SubsonicApi.deleteShare.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.deleteShare.response,
|
||||
},
|
||||
},
|
||||
deleteUser: {
|
||||
method: 'GET',
|
||||
path: 'deleteUser.view',
|
||||
query: SubsonicApi.deleteUser.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.deleteUser.response,
|
||||
},
|
||||
},
|
||||
getAlbum: {
|
||||
method: 'GET',
|
||||
path: 'getAlbum.view',
|
||||
query: SubsonicApi.getAlbum.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getAlbum.response,
|
||||
},
|
||||
},
|
||||
getAlbumInfo: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumInfo.view',
|
||||
query: SubsonicApi.getAlbumInfo.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getAlbumInfo.response,
|
||||
},
|
||||
},
|
||||
getAlbumInfo2: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumInfo2.view',
|
||||
query: SubsonicApi.getAlbumInfo2.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getAlbumInfo2.response,
|
||||
},
|
||||
},
|
||||
getAlbumList: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumList.view',
|
||||
query: SubsonicApi.getAlbumList.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getAlbumList.response,
|
||||
},
|
||||
},
|
||||
getAlbumList2: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumList2.view',
|
||||
query: SubsonicApi.getAlbumList2.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getAlbumList2.response,
|
||||
},
|
||||
},
|
||||
getArtist: {
|
||||
method: 'GET',
|
||||
path: 'getArtist.view',
|
||||
query: SubsonicApi.getArtist.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getArtist.response,
|
||||
},
|
||||
},
|
||||
getArtistInfo: {
|
||||
method: 'GET',
|
||||
path: 'getArtistInfo.view',
|
||||
query: ssType._parameters.artistInfo,
|
||||
query: SubsonicApi.getArtistInfo.parameters,
|
||||
responses: {
|
||||
200: ssType._response.artistInfo,
|
||||
200: SubsonicApi.getArtistInfo.response,
|
||||
},
|
||||
},
|
||||
getMusicFolderList: {
|
||||
getArtistInfo2: {
|
||||
method: 'GET',
|
||||
path: 'getArtistInfo2.view',
|
||||
query: SubsonicApi.getArtistInfo2.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getArtistInfo2.response,
|
||||
},
|
||||
},
|
||||
getArtists: {
|
||||
method: 'GET',
|
||||
path: 'getArtists.view',
|
||||
query: SubsonicApi.getArtists.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getArtists.response,
|
||||
},
|
||||
},
|
||||
getGenres: {
|
||||
method: 'GET',
|
||||
path: 'getGenres.view',
|
||||
query: SubsonicApi.getGenres.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getGenres.response,
|
||||
},
|
||||
},
|
||||
getIndexes: {
|
||||
method: 'GET',
|
||||
path: 'getIndexes.view',
|
||||
query: SubsonicApi.getIndexes.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getIndexes.response,
|
||||
},
|
||||
},
|
||||
getInternetRadioStations: {
|
||||
method: 'GET',
|
||||
path: 'getInternetRadioStations.view',
|
||||
query: SubsonicApi.getInternetRadioStations.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getInternetRadioStations.response,
|
||||
},
|
||||
},
|
||||
getLicense: {
|
||||
method: 'GET',
|
||||
path: 'getLicense.view',
|
||||
query: SubsonicApi.getLicense.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getLicense.response,
|
||||
},
|
||||
},
|
||||
getLyrics: {
|
||||
method: 'GET',
|
||||
path: 'getLyrics.view',
|
||||
query: SubsonicApi.getLyrics.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getLyrics.response,
|
||||
},
|
||||
},
|
||||
getMusicDirectory: {
|
||||
method: 'GET',
|
||||
path: 'getMusicDirectory.view',
|
||||
query: SubsonicApi.getMusicDirectory.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getMusicDirectory.response,
|
||||
},
|
||||
},
|
||||
getMusicFolders: {
|
||||
method: 'GET',
|
||||
path: 'getMusicFolders.view',
|
||||
responses: {
|
||||
200: ssType._response.musicFolderList,
|
||||
200: SubsonicApi.getMusicFolders.response,
|
||||
},
|
||||
},
|
||||
getRandomSongList: {
|
||||
getNowPlaying: {
|
||||
method: 'GET',
|
||||
path: 'getNowPlaying.view',
|
||||
query: SubsonicApi.getNowPlaying.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getNowPlaying.response,
|
||||
},
|
||||
},
|
||||
getOpenSubsonicExtensions: {
|
||||
method: 'GET',
|
||||
path: 'getOpenSubsonicExtensions.view',
|
||||
query: SubsonicApi.getOpenSubsonicExtensions.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getOpenSubsonicExtensions.response,
|
||||
},
|
||||
},
|
||||
getPlaylist: {
|
||||
method: 'GET',
|
||||
path: 'getPlaylist.view',
|
||||
query: SubsonicApi.getPlaylist.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getPlaylist.response,
|
||||
},
|
||||
},
|
||||
getPlaylists: {
|
||||
method: 'GET',
|
||||
path: 'getPlaylists.view',
|
||||
query: SubsonicApi.getPlaylists.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getPlaylists.response,
|
||||
},
|
||||
},
|
||||
getRandomSongs: {
|
||||
method: 'GET',
|
||||
path: 'getRandomSongs.view',
|
||||
query: ssType._parameters.randomSongList,
|
||||
query: SubsonicApi.getRandomSongs.parameters,
|
||||
responses: {
|
||||
200: ssType._response.randomSongList,
|
||||
200: SubsonicApi.getRandomSongs.response,
|
||||
},
|
||||
},
|
||||
getTopSongsList: {
|
||||
getScanStatus: {
|
||||
method: 'GET',
|
||||
path: 'getScanStatus.view',
|
||||
responses: {
|
||||
200: SubsonicApi.getScanStatus.response,
|
||||
},
|
||||
},
|
||||
getShares: {
|
||||
method: 'GET',
|
||||
path: 'getShares.view',
|
||||
query: SubsonicApi.getShares.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getShares.response,
|
||||
},
|
||||
},
|
||||
getSimilarSongs: {
|
||||
method: 'GET',
|
||||
path: 'getSimilarSongs.view',
|
||||
query: SubsonicApi.getSimilarSongs.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getSimilarSongs.response,
|
||||
},
|
||||
},
|
||||
getSimilarSongs2: {
|
||||
method: 'GET',
|
||||
path: 'getSimilarSongs2.view',
|
||||
query: SubsonicApi.getSimilarSongs2.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getSimilarSongs2.response,
|
||||
},
|
||||
},
|
||||
getSong: {
|
||||
method: 'GET',
|
||||
path: 'getSong.view',
|
||||
query: SubsonicApi.getSong.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getSong.response,
|
||||
},
|
||||
},
|
||||
getSongsByGenre: {
|
||||
method: 'GET',
|
||||
path: 'getSongsByGenre.view',
|
||||
query: SubsonicApi.getSongsByGenre.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getSongsByGenre.response,
|
||||
},
|
||||
},
|
||||
getStarred: {
|
||||
method: 'GET',
|
||||
path: 'getStarred.view',
|
||||
query: SubsonicApi.getStarred.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getStarred.response,
|
||||
},
|
||||
},
|
||||
getStarred2: {
|
||||
method: 'GET',
|
||||
path: 'getStarred2.view',
|
||||
query: SubsonicApi.getStarred2.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getStarred2.response,
|
||||
},
|
||||
},
|
||||
getTopSongs: {
|
||||
method: 'GET',
|
||||
path: 'getTopSongs.view',
|
||||
query: ssType._parameters.topSongsList,
|
||||
query: SubsonicApi.getTopSongs.parameters,
|
||||
responses: {
|
||||
200: ssType._response.topSongsList,
|
||||
200: SubsonicApi.getTopSongs.response,
|
||||
},
|
||||
},
|
||||
removeFavorite: {
|
||||
getUser: {
|
||||
method: 'GET',
|
||||
path: 'unstar.view',
|
||||
query: ssType._parameters.removeFavorite,
|
||||
path: 'getUser.view',
|
||||
query: SubsonicApi.getUser.parameters,
|
||||
responses: {
|
||||
200: ssType._response.removeFavorite,
|
||||
200: SubsonicApi.getUser.response,
|
||||
},
|
||||
},
|
||||
getUsers: {
|
||||
method: 'GET',
|
||||
path: 'getUsers.view',
|
||||
query: SubsonicApi.getUsers.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.getUsers.response,
|
||||
},
|
||||
},
|
||||
ping: {
|
||||
method: 'GET',
|
||||
path: 'ping.view',
|
||||
query: SubsonicApi.ping.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.ping.response,
|
||||
},
|
||||
},
|
||||
scrobble: {
|
||||
method: 'GET',
|
||||
path: 'scrobble.view',
|
||||
query: ssType._parameters.scrobble,
|
||||
query: SubsonicApi.scrobble.parameters,
|
||||
responses: {
|
||||
200: ssType._response.scrobble,
|
||||
200: SubsonicApi.scrobble.response,
|
||||
},
|
||||
},
|
||||
search3: {
|
||||
method: 'GET',
|
||||
path: 'search3.view',
|
||||
query: ssType._parameters.search3,
|
||||
query: SubsonicApi.search3.parameters,
|
||||
responses: {
|
||||
200: ssType._response.search3,
|
||||
200: SubsonicApi.search3.response,
|
||||
},
|
||||
},
|
||||
setRating: {
|
||||
method: 'GET',
|
||||
path: 'setRating.view',
|
||||
query: ssType._parameters.setRating,
|
||||
query: SubsonicApi.setRating.parameters,
|
||||
responses: {
|
||||
200: ssType._response.setRating,
|
||||
200: SubsonicApi.setRating.response,
|
||||
},
|
||||
},
|
||||
star: {
|
||||
method: 'GET',
|
||||
path: 'star.view',
|
||||
query: SubsonicApi.star.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.star.response,
|
||||
},
|
||||
},
|
||||
startScan: {
|
||||
method: 'GET',
|
||||
path: 'startScan.view',
|
||||
responses: {
|
||||
200: SubsonicApi.startScan.response,
|
||||
},
|
||||
},
|
||||
unstar: {
|
||||
method: 'GET',
|
||||
path: 'unstar.view',
|
||||
query: SubsonicApi.unstar.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.unstar.response,
|
||||
},
|
||||
},
|
||||
updateInternetRadioStation: {
|
||||
method: 'GET',
|
||||
path: 'updateInternetRadioStation.view',
|
||||
query: SubsonicApi.updateInternetRadioStation.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.updateInternetRadioStation.response,
|
||||
},
|
||||
},
|
||||
updatePlaylist: {
|
||||
method: 'GET',
|
||||
path: 'updatePlaylist.view',
|
||||
query: SubsonicApi.updatePlaylist.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.updatePlaylist.response,
|
||||
},
|
||||
},
|
||||
updateShare: {
|
||||
method: 'GET',
|
||||
path: 'updateShare.view',
|
||||
query: SubsonicApi.updateShare.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.updateShare.response,
|
||||
},
|
||||
},
|
||||
updateUser: {
|
||||
method: 'GET',
|
||||
path: 'updateUser.view',
|
||||
query: SubsonicApi.updateUser.parameters,
|
||||
responses: {
|
||||
200: SubsonicApi.updateUser.response,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -110,6 +443,8 @@ axiosClient.interceptors.response.use(
|
||||
title: i18n.t('error.genericError', { postProcess: 'sentenceCase' }) as string,
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(data['subsonic-response'].error);
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -131,7 +466,7 @@ const parsePath = (fullPath: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const ssApiClient = (args: {
|
||||
export const subsonicApiClient = (args: {
|
||||
server: ServerListItem | null;
|
||||
signal?: AbortSignal;
|
||||
url?: string;
|
||||
@@ -162,9 +497,7 @@ export const ssApiClient = (args: {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await axiosClient.request<
|
||||
z.infer<typeof ssType._response.baseResponse>
|
||||
>({
|
||||
const result = await axiosClient.request({
|
||||
data: body,
|
||||
headers,
|
||||
method: method as Method,
|
||||
@@ -180,7 +513,7 @@ export const ssApiClient = (args: {
|
||||
});
|
||||
|
||||
return {
|
||||
body: result.data['subsonic-response'],
|
||||
body: result.data,
|
||||
headers: result.headers as any,
|
||||
status: result.status,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import { z } from 'zod';
|
||||
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
|
||||
import { QueueSong, LibraryItem, AlbumArtist, Album } from '/@/renderer/api/types';
|
||||
import { SubsonicApi } from '/@/renderer/api/subsonic/subsonic-types';
|
||||
import { QueueSong, LibraryItem, AlbumArtist, Album, Genre } from '/@/renderer/api/types';
|
||||
import { ServerListItem, ServerType } from '/@/renderer/types';
|
||||
|
||||
const getCoverArtUrl = (args: {
|
||||
@@ -27,7 +27,7 @@ const getCoverArtUrl = (args: {
|
||||
};
|
||||
|
||||
const normalizeSong = (
|
||||
item: z.infer<typeof ssType._response.song>,
|
||||
item: z.infer<typeof SubsonicApi._baseTypes.song>,
|
||||
server: ServerListItem | null,
|
||||
deviceId: string,
|
||||
): QueueSong => {
|
||||
@@ -105,7 +105,7 @@ const normalizeSong = (
|
||||
};
|
||||
|
||||
const normalizeAlbumArtist = (
|
||||
item: z.infer<typeof ssType._response.albumArtist>,
|
||||
item: z.infer<typeof SubsonicApi._baseTypes.artist>,
|
||||
server: ServerListItem | null,
|
||||
): AlbumArtist => {
|
||||
const imageUrl =
|
||||
@@ -138,7 +138,9 @@ const normalizeAlbumArtist = (
|
||||
};
|
||||
|
||||
const normalizeAlbum = (
|
||||
item: z.infer<typeof ssType._response.album>,
|
||||
item:
|
||||
| z.infer<typeof SubsonicApi._baseTypes.album>
|
||||
| z.infer<typeof SubsonicApi._baseTypes.albumListEntry>,
|
||||
server: ServerListItem | null,
|
||||
): Album => {
|
||||
const imageUrl =
|
||||
@@ -189,8 +191,20 @@ const normalizeAlbum = (
|
||||
};
|
||||
};
|
||||
|
||||
export const ssNormalize = {
|
||||
const normalizeGenre = (item: z.infer<typeof SubsonicApi._baseTypes.genre>): Genre => {
|
||||
return {
|
||||
albumCount: item.albumCount,
|
||||
id: item.value,
|
||||
imageUrl: null,
|
||||
itemType: LibraryItem.GENRE,
|
||||
name: item.value,
|
||||
songCount: item.songCount,
|
||||
};
|
||||
};
|
||||
|
||||
export const subsonicNormalize = {
|
||||
album: normalizeAlbum,
|
||||
albumArtist: normalizeAlbumArtist,
|
||||
genre: normalizeGenre,
|
||||
song: normalizeSong,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user