Add all relevant subsonic endpoints to ts-rest

This commit is contained in:
jeffvli
2023-12-03 16:45:30 -08:00
parent 7b639b45f7
commit ca695ca155
3 changed files with 1258 additions and 186 deletions
+370 -37
View File
@@ -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