feishin/src/renderer/api/jellyfin/jellyfin-api.ts

352 lines
9.8 KiB
TypeScript
Raw Normal View History

import { useAuthStore } from '/@/renderer/store';
import { jfType } from '/@/renderer/api/jellyfin/jellyfin-types';
import { initClient, initContract } from '@ts-rest/core';
import axios, { AxiosError, AxiosResponse, isAxiosError, Method } from 'axios';
import qs from 'qs';
import { ServerListItem } from '/@/renderer/types';
import omitBy from 'lodash/omitBy';
2023-05-21 18:19:02 -07:00
import { z } from 'zod';
import { authenticationFailure } from '/@/renderer/api/utils';
const c = initContract();
export const contract = c.router({
2023-07-01 19:10:05 -07:00
addToPlaylist: {
body: z.null(),
method: 'POST',
path: 'playlists/:id/items',
query: jfType._parameters.addToPlaylist,
responses: {
204: jfType._response.addToPlaylist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
authenticate: {
body: jfType._parameters.authenticate,
headers: z.object({
'X-Emby-Authorization': z.string(),
}),
method: 'POST',
path: 'users/authenticatebyname',
responses: {
200: jfType._response.authenticate,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
createFavorite: {
body: jfType._parameters.favorite,
method: 'POST',
path: 'users/:userId/favoriteitems/:id',
responses: {
200: jfType._response.favorite,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
createPlaylist: {
body: jfType._parameters.createPlaylist,
method: 'POST',
path: 'playlists',
responses: {
200: jfType._response.createPlaylist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
deletePlaylist: {
body: null,
method: 'DELETE',
path: 'items/:id',
responses: {
204: jfType._response.deletePlaylist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getAlbumArtistDetail: {
method: 'GET',
path: 'users/:userId/items/:id',
query: jfType._parameters.albumArtistDetail,
responses: {
200: jfType._response.albumArtist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getAlbumArtistList: {
method: 'GET',
path: 'artists/albumArtists',
query: jfType._parameters.albumArtistList,
responses: {
200: jfType._response.albumArtistList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getAlbumDetail: {
method: 'GET',
path: 'users/:userId/items/:id',
query: jfType._parameters.albumDetail,
responses: {
200: jfType._response.album,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getAlbumList: {
method: 'GET',
path: 'users/:userId/items',
query: jfType._parameters.albumList,
responses: {
200: jfType._response.albumList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getArtistList: {
method: 'GET',
path: 'artists',
query: jfType._parameters.albumArtistList,
responses: {
200: jfType._response.albumArtistList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getGenreList: {
method: 'GET',
path: 'genres',
2023-07-31 17:16:48 -07:00
query: jfType._parameters.genreList,
2023-07-01 19:10:05 -07:00
responses: {
200: jfType._response.genreList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getMusicFolderList: {
method: 'GET',
path: 'users/:userId/items',
responses: {
200: jfType._response.musicFolderList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getPlaylistDetail: {
method: 'GET',
path: 'users/:userId/items/:id',
query: jfType._parameters.playlistDetail,
responses: {
200: jfType._response.playlist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getPlaylistList: {
method: 'GET',
path: 'users/:userId/items',
query: jfType._parameters.playlistList,
responses: {
200: jfType._response.playlistList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getPlaylistSongList: {
method: 'GET',
path: 'playlists/:id/items',
query: jfType._parameters.songList,
responses: {
200: jfType._response.playlistSongList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getSimilarArtistList: {
method: 'GET',
path: 'artists/:id/similar',
query: jfType._parameters.similarArtistList,
responses: {
200: jfType._response.albumArtistList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getSongDetail: {
method: 'GET',
path: 'song/:id',
responses: {
200: jfType._response.song,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getSongList: {
method: 'GET',
path: 'users/:userId/items',
query: jfType._parameters.songList,
responses: {
200: jfType._response.songList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
getSongLyrics: {
method: 'GET',
path: 'users/:userId/Items/:id/Lyrics',
responses: {
200: jfType._response.lyrics,
404: jfType._response.error,
},
2023-06-02 23:54:34 -07:00
},
2023-07-01 19:10:05 -07:00
getTopSongsList: {
method: 'GET',
path: 'users/:userId/items',
query: jfType._parameters.songList,
responses: {
200: jfType._response.topSongsList,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
removeFavorite: {
body: jfType._parameters.favorite,
method: 'DELETE',
path: 'users/:userId/favoriteitems/:id',
responses: {
200: jfType._response.favorite,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
removeFromPlaylist: {
body: null,
method: 'DELETE',
path: 'playlists/:id/items',
query: jfType._parameters.removeFromPlaylist,
responses: {
200: jfType._response.removeFromPlaylist,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
scrobblePlaying: {
body: jfType._parameters.scrobble,
method: 'POST',
path: 'sessions/playing',
responses: {
200: jfType._response.scrobble,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
scrobbleProgress: {
body: jfType._parameters.scrobble,
method: 'POST',
path: 'sessions/playing/progress',
responses: {
200: jfType._response.scrobble,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
scrobbleStopped: {
body: jfType._parameters.scrobble,
method: 'POST',
path: 'sessions/playing/stopped',
responses: {
200: jfType._response.scrobble,
400: jfType._response.error,
},
},
2023-07-01 19:10:05 -07:00
search: {
method: 'GET',
path: 'users/:userId/items',
query: jfType._parameters.search,
responses: {
200: jfType._response.search,
400: jfType._response.error,
},
2023-05-19 02:06:58 -07:00
},
2023-07-01 19:10:05 -07:00
updatePlaylist: {
body: jfType._parameters.updatePlaylist,
method: 'PUT',
path: 'items/:id',
responses: {
200: jfType._response.updatePlaylist,
400: jfType._response.error,
},
},
});
const axiosClient = axios.create({});
axiosClient.defaults.paramsSerializer = (params) => {
2023-07-01 19:10:05 -07:00
return qs.stringify(params, { arrayFormat: 'repeat' });
};
axiosClient.interceptors.response.use(
2023-07-01 19:10:05 -07:00
(response) => {
return response;
},
(error) => {
if (error.response && error.response.status === 401) {
const currentServer = useAuthStore.getState().currentServer;
2023-07-01 19:10:05 -07:00
authenticationFailure(currentServer);
}
2023-07-01 19:10:05 -07:00
return Promise.reject(error);
},
);
const parsePath = (fullPath: string) => {
2023-07-01 19:10:05 -07:00
const [path, params] = fullPath.split('?');
2023-07-01 19:10:05 -07:00
const parsedParams = qs.parse(params);
const notNilParams = omitBy(parsedParams, (value) => value === 'undefined' || value === 'null');
2023-07-01 19:10:05 -07:00
return {
params: notNilParams,
path,
};
};
export const jfApiClient = (args: {
2023-07-01 19:10:05 -07:00
server: ServerListItem | null;
signal?: AbortSignal;
url?: string;
}) => {
2023-07-01 19:10:05 -07:00
const { server, url, signal } = args;
2023-07-01 19:10:05 -07:00
return initClient(contract, {
api: async ({ path, method, headers, body }) => {
let baseUrl: string | undefined;
let token: string | undefined;
2023-07-01 19:10:05 -07:00
const { params, path: api } = parsePath(path);
2023-07-01 19:10:05 -07:00
if (server) {
baseUrl = `${server?.url}`;
token = server?.credential;
} else {
baseUrl = url;
}
2023-07-01 19:10:05 -07:00
try {
const result = await axiosClient.request({
data: body,
headers: {
...headers,
...(token && { 'X-MediaBrowser-Token': token }),
},
method: method as Method,
params,
signal,
url: `${baseUrl}/${api}`,
});
return {
body: result.data,
headers: result.headers as any,
status: result.status,
};
} catch (e: Error | AxiosError | any) {
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: '',
jsonQuery: false,
});
};