mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-15 07:54:18 +02:00
temp progress
This commit is contained in:
@@ -168,7 +168,7 @@ ipcMain.on('update-song', (_event, song: QueueSong | undefined) => {
|
|||||||
'xesam:contentCreated': song.releaseDate,
|
'xesam:contentCreated': song.releaseDate,
|
||||||
'xesam:discNumber': song.discNumber ? song.discNumber : null,
|
'xesam:discNumber': song.discNumber ? song.discNumber : null,
|
||||||
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
|
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
|
||||||
'xesam:lastUsed': song.lastPlayedAt,
|
'xesam:lastUsed': song.userLastPlayedDate,
|
||||||
'xesam:title': song.name || null,
|
'xesam:title': song.name || null,
|
||||||
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
|
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
|
||||||
'xesam:useCount':
|
'xesam:useCount':
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { getServerById } from '/@/renderer/store';
|
||||||
|
import {
|
||||||
|
controller as subsonicAdapter,
|
||||||
|
apiClient as subsonicApiClient,
|
||||||
|
middleware as subsonicMiddleware,
|
||||||
|
} from '/@/shared/api/subsonic/subsonic-controller';
|
||||||
|
import { ApiController } from '/@/shared/types/adapter/api-controller-types';
|
||||||
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
|
|
||||||
|
export const serverApi = {
|
||||||
|
[ServerType.JELLYFIN]: {
|
||||||
|
apiClient: null,
|
||||||
|
controller: {},
|
||||||
|
middleware: null,
|
||||||
|
},
|
||||||
|
[ServerType.NAVIDROME]: {
|
||||||
|
apiClient: null,
|
||||||
|
controller: {},
|
||||||
|
middleware: null,
|
||||||
|
},
|
||||||
|
[ServerType.SUBSONIC]: {
|
||||||
|
apiClient: subsonicApiClient,
|
||||||
|
controller: subsonicAdapter,
|
||||||
|
middleware: subsonicMiddleware,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const api = (serverId: string): ApiController => {
|
||||||
|
const server = getServerById(serverId);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
throw new Error('No server or api client selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { apiClient, controller, middleware } = serverApi[server.type];
|
||||||
|
|
||||||
|
if (middleware) {
|
||||||
|
apiClient.use(middleware(server));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apiClient) {
|
||||||
|
throw new Error('No api client found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller as ApiController;
|
||||||
|
};
|
||||||
@@ -4,11 +4,9 @@ import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-control
|
|||||||
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
|
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
|
||||||
import { useAuthStore } from '/@/renderer/store';
|
import { useAuthStore } from '/@/renderer/store';
|
||||||
import { toast } from '/@/shared/components/toast/toast';
|
import { toast } from '/@/shared/components/toast/toast';
|
||||||
import {
|
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
|
||||||
AuthenticationResponse,
|
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
|
||||||
ControllerEndpoint,
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
ServerType,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
|
|
||||||
type ApiController = {
|
type ApiController = {
|
||||||
jellyfin: ControllerEndpoint;
|
jellyfin: ControllerEndpoint;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller
|
|||||||
import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
||||||
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
|
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
|
||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
||||||
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
|
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
|
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
|
||||||
import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
|
import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
|
||||||
@@ -14,7 +14,7 @@ import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types
|
|||||||
import { genreListSortMap } from '/@/shared/types/domain/genre-domain-types';
|
import { genreListSortMap } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import {
|
import {
|
||||||
playlistListSortMap,
|
playlistListSortMap,
|
||||||
PlaylistSongListArgs,
|
PlaylistSongListRequest,
|
||||||
PlaylistSongListResponse,
|
PlaylistSongListResponse,
|
||||||
} from '/@/shared/types/domain/playlist-domain-types';
|
} from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import {
|
import {
|
||||||
@@ -424,7 +424,9 @@ export const NavidromeController: ControllerEndpoint = {
|
|||||||
apiClientProps,
|
apiClientProps,
|
||||||
query: { ...query, limit: 1, startIndex: 0 },
|
query: { ...query, limit: 1, startIndex: 0 },
|
||||||
}).then((result) => result!.totalRecordCount!),
|
}).then((result) => result!.totalRecordCount!),
|
||||||
getPlaylistSongList: async (args: PlaylistSongListArgs): Promise<PlaylistSongListResponse> => {
|
getPlaylistSongList: async (
|
||||||
|
args: PlaylistSongListRequest,
|
||||||
|
): Promise<PlaylistSongListResponse> => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
const res = await ndApiClient(apiClientProps).getPlaylistSongList({
|
const res = await ndApiClient(apiClientProps).getPlaylistSongList({
|
||||||
@@ -520,7 +522,7 @@ export const NavidromeController: ControllerEndpoint = {
|
|||||||
if (res.status === 200 && res.body.similarSongs?.song) {
|
if (res.status === 200 && res.body.similarSongs?.song) {
|
||||||
const similar = res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
|
const similar = res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
|
||||||
if (song.id !== query.songId) {
|
if (song.id !== query.songId) {
|
||||||
acc.push(ssNormalize.song(song, apiClientProps.server));
|
acc.push(normalize.song(song, apiClientProps.server));
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { contract, ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
|
import { contract, ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
|
||||||
import { randomString } from '/@/renderer/utils';
|
import { randomString } from '/@/renderer/utils';
|
||||||
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
||||||
import {
|
import {
|
||||||
AlbumListSortType,
|
AlbumListSortType,
|
||||||
ssType,
|
ssType,
|
||||||
@@ -200,11 +200,11 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...ssNormalize.albumArtist(artist, apiClientProps.server, 300),
|
...normalize.albumArtist(artist, apiClientProps.server, 300),
|
||||||
albums: artist.album?.map((album) => ssNormalize.album(album, apiClientProps.server)),
|
albums: artist.album?.map((album) => normalize.album(album, apiClientProps.server)),
|
||||||
similarArtists:
|
similarArtists:
|
||||||
artistInfo?.similarArtist?.map((artist) =>
|
artistInfo?.similarArtist?.map((artist) =>
|
||||||
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
|
normalize.albumArtist(artist, apiClientProps.server, 300),
|
||||||
) || null,
|
) || null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -224,7 +224,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
const artists = (res.body.artists?.index || []).flatMap((index) => index.artist);
|
const artists = (res.body.artists?.index || []).flatMap((index) => index.artist);
|
||||||
|
|
||||||
let results = artists.map((artist) =>
|
let results = artists.map((artist) =>
|
||||||
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
|
normalize.albumArtist(artist, apiClientProps.server, 300),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (query.searchTerm) {
|
if (query.searchTerm) {
|
||||||
@@ -260,7 +260,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
throw new Error('Failed to get album detail');
|
throw new Error('Failed to get album detail');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssNormalize.album(res.body.album, apiClientProps.server);
|
return normalize.album(res.body.album, apiClientProps.server);
|
||||||
},
|
},
|
||||||
getAlbumList: async (args) => {
|
getAlbumList: async (args) => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
@@ -284,7 +284,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
|
|
||||||
const results =
|
const results =
|
||||||
res.body.searchResult3?.album?.map((album) =>
|
res.body.searchResult3?.album?.map((album) =>
|
||||||
ssNormalize.album(album, apiClientProps.server),
|
normalize.album(album, apiClientProps.server),
|
||||||
) || [];
|
) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -319,7 +319,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
return artist.body.artist.album ?? [];
|
return artist.body.artist.album ?? [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const items = albums.map((album) => ssNormalize.album(album, apiClientProps.server));
|
const items = albums.map((album) => normalize.album(album, apiClientProps.server));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: sortAlbumList(items, query.sortBy, query.sortOrder),
|
items: sortAlbumList(items, query.sortBy, query.sortOrder),
|
||||||
@@ -341,7 +341,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
|
|
||||||
const results =
|
const results =
|
||||||
res.body.starred?.album?.map((album) =>
|
res.body.starred?.album?.map((album) =>
|
||||||
ssNormalize.album(album, apiClientProps.server),
|
normalize.album(album, apiClientProps.server),
|
||||||
) || [];
|
) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -404,7 +404,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
return {
|
return {
|
||||||
items:
|
items:
|
||||||
res.body.albumList2.album?.map((album) =>
|
res.body.albumList2.album?.map((album) =>
|
||||||
ssNormalize.album(album, apiClientProps.server, 300),
|
normalize.album(album, apiClientProps.server, 300),
|
||||||
) || [],
|
) || [],
|
||||||
startIndex: query.startIndex,
|
startIndex: query.startIndex,
|
||||||
totalRecordCount: null,
|
totalRecordCount: null,
|
||||||
@@ -574,7 +574,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let results = artists.map((artist) =>
|
let results = artists.map((artist) =>
|
||||||
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
|
normalize.albumArtist(artist, apiClientProps.server, 300),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (query.searchTerm) {
|
if (query.searchTerm) {
|
||||||
@@ -635,7 +635,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const genres = results.map(ssNormalize.genre);
|
const genres = results.map(normalize.genre);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: genres,
|
items: genres,
|
||||||
@@ -674,7 +674,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
throw new Error('Failed to get playlist detail');
|
throw new Error('Failed to get playlist detail');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssNormalize.playlist(res.body.playlist, apiClientProps.server);
|
return normalize.playlist(res.body.playlist, apiClientProps.server);
|
||||||
},
|
},
|
||||||
getPlaylistList: async ({ apiClientProps, query }) => {
|
getPlaylistList: async ({ apiClientProps, query }) => {
|
||||||
const sortOrder = query.sortOrder.toLowerCase() as 'asc' | 'desc';
|
const sortOrder = query.sortOrder.toLowerCase() as 'asc' | 'desc';
|
||||||
@@ -719,7 +719,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: results.map((playlist) => ssNormalize.playlist(playlist, apiClientProps.server)),
|
items: results.map((playlist) => normalize.playlist(playlist, apiClientProps.server)),
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: results.length,
|
totalRecordCount: results.length,
|
||||||
};
|
};
|
||||||
@@ -755,7 +755,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let results =
|
let results =
|
||||||
res.body.playlist.entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) ||
|
res.body.playlist.entry?.map((song) => normalize.song(song, apiClientProps.server)) ||
|
||||||
[];
|
[];
|
||||||
|
|
||||||
if (query.sortBy && query.sortOrder) {
|
if (query.sortBy && query.sortOrder) {
|
||||||
@@ -788,7 +788,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
const results = res.body.randomSongs?.song || [];
|
const results = res.body.randomSongs?.song || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)),
|
items: results.map((song) => normalize.song(song, apiClientProps.server)),
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: res.body.randomSongs?.song?.length || 0,
|
totalRecordCount: res.body.randomSongs?.song?.length || 0,
|
||||||
};
|
};
|
||||||
@@ -873,7 +873,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
|
|
||||||
return res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
|
return res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
|
||||||
if (song.id !== query.songId) {
|
if (song.id !== query.songId) {
|
||||||
acc.push(ssNormalize.song(song, apiClientProps.server));
|
acc.push(normalize.song(song, apiClientProps.server));
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
@@ -892,7 +892,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
throw new Error('Failed to get song detail');
|
throw new Error('Failed to get song detail');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssNormalize.song(res.body.song, apiClientProps.server);
|
return normalize.song(res.body.song, apiClientProps.server);
|
||||||
},
|
},
|
||||||
getSongList: async ({ apiClientProps, query }) => {
|
getSongList: async ({ apiClientProps, query }) => {
|
||||||
const fromAlbumPromises: Promise<ServerInferResponses<typeof contract.getAlbum>>[] = [];
|
const fromAlbumPromises: Promise<ServerInferResponses<typeof contract.getAlbum>>[] = [];
|
||||||
@@ -918,7 +918,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
return {
|
return {
|
||||||
items:
|
items:
|
||||||
res.body.searchResult3?.song?.map((song) =>
|
res.body.searchResult3?.song?.map((song) =>
|
||||||
ssNormalize.song(song, apiClientProps.server),
|
normalize.song(song, apiClientProps.server),
|
||||||
) || [],
|
) || [],
|
||||||
startIndex: query.startIndex,
|
startIndex: query.startIndex,
|
||||||
totalRecordCount: null,
|
totalRecordCount: null,
|
||||||
@@ -942,7 +942,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
const results = res.body.songsByGenre?.song || [];
|
const results = res.body.songsByGenre?.song || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)) || [],
|
items: results.map((song) => normalize.song(song, apiClientProps.server)) || [],
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: null,
|
totalRecordCount: null,
|
||||||
};
|
};
|
||||||
@@ -961,7 +961,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
|
|
||||||
const results =
|
const results =
|
||||||
(res.body.starred?.song || []).map((song) =>
|
(res.body.starred?.song || []).map((song) =>
|
||||||
ssNormalize.song(song, apiClientProps.server),
|
normalize.song(song, apiClientProps.server),
|
||||||
) || [];
|
) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1035,7 +1035,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)),
|
items: results.map((song) => normalize.song(song, apiClientProps.server)),
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: results.length,
|
totalRecordCount: results.length,
|
||||||
};
|
};
|
||||||
@@ -1060,7 +1060,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
return {
|
return {
|
||||||
items:
|
items:
|
||||||
res.body.searchResult3?.song?.map((song) =>
|
res.body.searchResult3?.song?.map((song) =>
|
||||||
ssNormalize.song(song, apiClientProps.server),
|
normalize.song(song, apiClientProps.server),
|
||||||
) || [],
|
) || [],
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: null,
|
totalRecordCount: null,
|
||||||
@@ -1297,7 +1297,7 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
return {
|
return {
|
||||||
items:
|
items:
|
||||||
res.body.topSongs?.song?.map((song) =>
|
res.body.topSongs?.song?.map((song) =>
|
||||||
ssNormalize.song(song, apiClientProps.server),
|
normalize.song(song, apiClientProps.server),
|
||||||
) || [],
|
) || [],
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: res.body.topSongs?.song?.length || 0,
|
totalRecordCount: res.body.topSongs?.song?.length || 0,
|
||||||
@@ -1367,13 +1367,13 @@ export const SubsonicController: ControllerEndpoint = {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
albumArtists: (res.body.searchResult3?.artist || [])?.map((artist) =>
|
albumArtists: (res.body.searchResult3?.artist || [])?.map((artist) =>
|
||||||
ssNormalize.albumArtist(artist, apiClientProps.server),
|
normalize.albumArtist(artist, apiClientProps.server),
|
||||||
),
|
),
|
||||||
albums: (res.body.searchResult3?.album || []).map((album) =>
|
albums: (res.body.searchResult3?.album || []).map((album) =>
|
||||||
ssNormalize.album(album, apiClientProps.server),
|
normalize.album(album, apiClientProps.server),
|
||||||
),
|
),
|
||||||
songs: (res.body.searchResult3?.song || []).map((song) =>
|
songs: (res.body.searchResult3?.song || []).map((song) =>
|
||||||
ssNormalize.song(song, apiClientProps.server),
|
normalize.song(song, apiClientProps.server),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
|||||||
import { useAlbumArtistListDataStore } from '/@/renderer/store/album-artist-list-data.store';
|
import { useAlbumArtistListDataStore } from '/@/renderer/store/album-artist-list-data.store';
|
||||||
import { useAlbumListDataStore } from '/@/renderer/store/album-list-data.store';
|
import { useAlbumListDataStore } from '/@/renderer/store/album-list-data.store';
|
||||||
import { useListStore } from '/@/renderer/store/list.store';
|
import { useListStore } from '/@/renderer/store/list.store';
|
||||||
import { ServerListItem } from '/@/shared/types/domain-types';
|
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
|
||||||
|
|
||||||
export interface AuthSlice extends AuthState {
|
export interface AuthSlice extends AuthState {
|
||||||
actions: {
|
actions: {
|
||||||
@@ -70,8 +70,8 @@ export const useAuthStore = createWithEqualityFn<AuthSlice>()(
|
|||||||
...args,
|
...args,
|
||||||
};
|
};
|
||||||
|
|
||||||
state.serverList[id] = updatedServer;
|
state.serverList[id] = updatedServer as ServerListItem;
|
||||||
state.currentServer = updatedServer;
|
state.currentServer = updatedServer as ServerListItem;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { adapter as subsonicAdapter } from './subsonic/subsonic-controller';
|
|
||||||
|
|
||||||
import i18n from '/@/i18n/i18n';
|
|
||||||
import { ApiController } from '/@/shared/types/adapter/api-controller-types';
|
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
|
||||||
|
|
||||||
interface ApiControllerOptions {
|
|
||||||
type: ServerType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const adapters = {
|
|
||||||
[ServerType.JELLYFIN]: {},
|
|
||||||
[ServerType.NAVIDROME]: {},
|
|
||||||
[ServerType.SUBSONIC]: subsonicAdapter,
|
|
||||||
} as Record<ServerType, ApiController>;
|
|
||||||
|
|
||||||
export const apiController = (options: ApiControllerOptions): ApiController => {
|
|
||||||
const { type } = options;
|
|
||||||
|
|
||||||
const adapter = adapters[type];
|
|
||||||
|
|
||||||
if (!adapter) {
|
|
||||||
throw new Error(i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter;
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,7 @@ import { JFAlbum, JFGenre, JFMusicFolder, JFPlaylist } from '/@/shared/api/jelly
|
|||||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import {
|
import {
|
||||||
@@ -213,6 +213,7 @@ const normalizeSong = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
_itemType: LibraryItem.SONG,
|
||||||
album: item.Album,
|
album: item.Album,
|
||||||
albumArtists: item.AlbumArtists?.map((entry) => ({
|
albumArtists: item.AlbumArtists?.map((entry) => ({
|
||||||
id: entry.Id,
|
id: entry.Id,
|
||||||
@@ -256,8 +257,7 @@ const normalizeSong = (
|
|||||||
id: item.Id,
|
id: item.Id,
|
||||||
imagePlaceholderUrl: null,
|
imagePlaceholderUrl: null,
|
||||||
imageUrl: getSongCoverArtUrl({ baseUrl: server?.url || '', item, size: imageSize || 100 }),
|
imageUrl: getSongCoverArtUrl({ baseUrl: server?.url || '', item, size: imageSize || 100 }),
|
||||||
itemType: LibraryItem.SONG,
|
isCompilation: null,
|
||||||
lastPlayedAt: null,
|
|
||||||
lyrics: null,
|
lyrics: null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
participants: getPeople(item),
|
participants: getPeople(item),
|
||||||
@@ -279,8 +279,9 @@ const normalizeSong = (
|
|||||||
tags: getTags(item),
|
tags: getTags(item),
|
||||||
trackNumber: item.IndexNumber,
|
trackNumber: item.IndexNumber,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.DateCreated,
|
updatedDate: item.DateCreated,
|
||||||
userFavorite: (item.UserData && item.UserData.IsFavorite) || false,
|
userFavorite: (item.UserData && item.UserData.IsFavorite) || false,
|
||||||
|
userLastPlayedDate: null,
|
||||||
userRating: null,
|
userRating: null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -291,6 +292,9 @@ const normalizeAlbum = (
|
|||||||
imageSize?: number,
|
imageSize?: number,
|
||||||
): Album => {
|
): Album => {
|
||||||
return {
|
return {
|
||||||
|
_itemType: LibraryItem.ALBUM,
|
||||||
|
_serverId: server?.id || '',
|
||||||
|
_serverType: ServerType.JELLYFIN,
|
||||||
albumArtist: item.AlbumArtist,
|
albumArtist: item.AlbumArtist,
|
||||||
albumArtists:
|
albumArtists:
|
||||||
item.AlbumArtists.map((entry) => ({
|
item.AlbumArtists.map((entry) => ({
|
||||||
@@ -321,7 +325,6 @@ const normalizeAlbum = (
|
|||||||
size: imageSize || 300,
|
size: imageSize || 300,
|
||||||
}),
|
}),
|
||||||
isCompilation: null,
|
isCompilation: null,
|
||||||
itemType: LibraryItem.ALBUM,
|
|
||||||
lastPlayedAt: null,
|
lastPlayedAt: null,
|
||||||
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
@@ -330,8 +333,6 @@ const normalizeAlbum = (
|
|||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
||||||
releaseYear: item.ProductionYear || null,
|
releaseYear: item.ProductionYear || null,
|
||||||
serverId: server?.id || '',
|
|
||||||
serverType: ServerType.JELLYFIN,
|
|
||||||
size: null,
|
size: null,
|
||||||
songCount: item?.ChildCount || null,
|
songCount: item?.ChildCount || null,
|
||||||
songs: item.Songs?.map((song) => normalizeSong(song, server, '', imageSize)),
|
songs: item.Songs?.map((song) => normalizeSong(song, server, '', imageSize)),
|
||||||
@@ -349,7 +350,7 @@ const normalizeAlbumArtist = (
|
|||||||
},
|
},
|
||||||
server: null | ServerListItem,
|
server: null | ServerListItem,
|
||||||
imageSize?: number,
|
imageSize?: number,
|
||||||
): AlbumArtist => {
|
): Artist => {
|
||||||
const similarArtists =
|
const similarArtists =
|
||||||
item.similarArtists?.Items?.filter((entry) => entry.Name !== 'Various Artists').map(
|
item.similarArtists?.Items?.filter((entry) => entry.Name !== 'Various Artists').map(
|
||||||
(entry) => ({
|
(entry) => ({
|
||||||
@@ -364,6 +365,8 @@ const normalizeAlbumArtist = (
|
|||||||
) || [];
|
) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
_serverId: server?.id || '',
|
||||||
|
_serverType: ServerType.JELLYFIN,
|
||||||
albumCount: item.AlbumCount ?? null,
|
albumCount: item.AlbumCount ?? null,
|
||||||
backgroundImageUrl: null,
|
backgroundImageUrl: null,
|
||||||
biography: item.Overview || null,
|
biography: item.Overview || null,
|
||||||
@@ -381,15 +384,13 @@ const normalizeAlbumArtist = (
|
|||||||
size: imageSize || 300,
|
size: imageSize || 300,
|
||||||
}),
|
}),
|
||||||
itemType: LibraryItem.ALBUM_ARTIST,
|
itemType: LibraryItem.ALBUM_ARTIST,
|
||||||
lastPlayedAt: null,
|
mbzId: item.ProviderIds?.MusicBrainzArtist || null,
|
||||||
mbz: item.ProviderIds?.MusicBrainzArtist || null,
|
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
serverId: server?.id || '',
|
|
||||||
serverType: ServerType.JELLYFIN,
|
|
||||||
similarArtists,
|
similarArtists,
|
||||||
songCount: item.SongCount ?? null,
|
songCount: item.SongCount ?? null,
|
||||||
userFavorite: item.UserData?.IsFavorite || false,
|
userFavorite: item.UserData?.IsFavorite || false,
|
||||||
|
userLastPlayedDate: null,
|
||||||
userRating: null,
|
userRating: null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -408,6 +409,9 @@ const normalizePlaylist = (
|
|||||||
const imagePlaceholderUrl = null;
|
const imagePlaceholderUrl = null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
_itemType: LibraryItem.PLAYLIST,
|
||||||
|
_serverId: server?.id || '',
|
||||||
|
_serverType: ServerType.JELLYFIN,
|
||||||
description: item.Overview || null,
|
description: item.Overview || null,
|
||||||
duration: item.RunTimeTicks / 10000,
|
duration: item.RunTimeTicks / 10000,
|
||||||
genres: item.GenreItems?.map((entry) => ({
|
genres: item.GenreItems?.map((entry) => ({
|
||||||
@@ -419,14 +423,11 @@ const normalizePlaylist = (
|
|||||||
id: item.Id,
|
id: item.Id,
|
||||||
imagePlaceholderUrl,
|
imagePlaceholderUrl,
|
||||||
imageUrl: imageUrl || null,
|
imageUrl: imageUrl || null,
|
||||||
itemType: LibraryItem.PLAYLIST,
|
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
owner: null,
|
owner: null,
|
||||||
ownerId: null,
|
ownerId: null,
|
||||||
public: null,
|
public: null,
|
||||||
rules: null,
|
rules: null,
|
||||||
serverId: server?.id || '',
|
|
||||||
serverType: ServerType.JELLYFIN,
|
|
||||||
size: null,
|
size: null,
|
||||||
songCount: item?.ChildCount || null,
|
songCount: item?.ChildCount || null,
|
||||||
sync: null,
|
sync: null,
|
||||||
@@ -482,10 +483,10 @@ const getGenreCoverArtUrl = (args: {
|
|||||||
|
|
||||||
const normalizeGenre = (item: JFGenre, server: null | ServerListItem): Genre => {
|
const normalizeGenre = (item: JFGenre, server: null | ServerListItem): Genre => {
|
||||||
return {
|
return {
|
||||||
|
_itemType: LibraryItem.GENRE,
|
||||||
albumCount: undefined,
|
albumCount: undefined,
|
||||||
id: item.Id,
|
id: item.Id,
|
||||||
imageUrl: getGenreCoverArtUrl({ baseUrl: server?.url || '', item, size: 200 }),
|
imageUrl: getGenreCoverArtUrl({ baseUrl: server?.url || '', item, size: 200 }),
|
||||||
itemType: LibraryItem.GENRE,
|
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
songCount: undefined,
|
songCount: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
|||||||
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
@@ -151,9 +151,9 @@ const normalizeSong = (
|
|||||||
bpm: item.bpm ? item.bpm : null,
|
bpm: item.bpm ? item.bpm : null,
|
||||||
channels: item.channels ? item.channels : null,
|
channels: item.channels ? item.channels : null,
|
||||||
comment: item.comment ? item.comment : null,
|
comment: item.comment ? item.comment : null,
|
||||||
compilation: item.compilation,
|
isCompilation: item.compilation,
|
||||||
container: item.suffix,
|
container: item.suffix,
|
||||||
createdAt: item.createdAt.split('T')[0],
|
createdDate: item.createdAt.split('T')[0],
|
||||||
discNumber: item.discNumber,
|
discNumber: item.discNumber,
|
||||||
discSubtitle: item.discSubtitle ? item.discSubtitle : null,
|
discSubtitle: item.discSubtitle ? item.discSubtitle : null,
|
||||||
duration: item.duration * 1000,
|
duration: item.duration * 1000,
|
||||||
@@ -170,8 +170,8 @@ const normalizeSong = (
|
|||||||
id,
|
id,
|
||||||
imagePlaceholderUrl,
|
imagePlaceholderUrl,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
itemType: LibraryItem.SONG,
|
_itemType: LibraryItem.SONG,
|
||||||
lastPlayedAt: normalizePlayDate(item),
|
userLastPlayedDate: normalizePlayDate(item),
|
||||||
lyrics: item.lyrics ? item.lyrics : null,
|
lyrics: item.lyrics ? item.lyrics : null,
|
||||||
name: item.title,
|
name: item.title,
|
||||||
// Thankfully, Windows is merciful and allows a mix of separators. So, we can use the
|
// Thankfully, Windows is merciful and allows a mix of separators. So, we can use the
|
||||||
@@ -196,7 +196,7 @@ const normalizeSong = (
|
|||||||
tags: item.tags || null,
|
tags: item.tags || null,
|
||||||
trackNumber: item.trackNumber,
|
trackNumber: item.trackNumber,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.updatedAt,
|
updatedDate: item.updatedAt,
|
||||||
userFavorite: item.starred || false,
|
userFavorite: item.starred || false,
|
||||||
userRating: item.rating || null,
|
userRating: item.rating || null,
|
||||||
};
|
};
|
||||||
@@ -237,7 +237,7 @@ const normalizeAlbum = (
|
|||||||
imagePlaceholderUrl,
|
imagePlaceholderUrl,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
isCompilation: item.compilation,
|
isCompilation: item.compilation,
|
||||||
itemType: LibraryItem.ALBUM,
|
_itemType: LibraryItem.ALBUM,
|
||||||
lastPlayedAt: normalizePlayDate(item),
|
lastPlayedAt: normalizePlayDate(item),
|
||||||
|
|
||||||
mbzId: item.mbzAlbumId || null,
|
mbzId: item.mbzAlbumId || null,
|
||||||
@@ -253,8 +253,8 @@ const normalizeAlbum = (
|
|||||||
: new Date(Date.UTC(item.minYear, 0, 1))
|
: new Date(Date.UTC(item.minYear, 0, 1))
|
||||||
).toISOString(),
|
).toISOString(),
|
||||||
releaseYear: item.minYear,
|
releaseYear: item.minYear,
|
||||||
serverId: server?.id || 'unknown',
|
_serverId: server?.id || 'unknown',
|
||||||
serverType: ServerType.NAVIDROME,
|
_serverType: ServerType.NAVIDROME,
|
||||||
size: item.size,
|
size: item.size,
|
||||||
songCount: item.songCount,
|
songCount: item.songCount,
|
||||||
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
||||||
@@ -271,7 +271,7 @@ const normalizeAlbumArtist = (
|
|||||||
similarArtists?: z.infer<typeof ssType._response.artistInfo>['artistInfo']['similarArtist'];
|
similarArtists?: z.infer<typeof ssType._response.artistInfo>['artistInfo']['similarArtist'];
|
||||||
},
|
},
|
||||||
server: null | ServerListItem,
|
server: null | ServerListItem,
|
||||||
): AlbumArtist => {
|
): Artist => {
|
||||||
let imageUrl = getImageUrl({ url: item?.largeImageUrl || null });
|
let imageUrl = getImageUrl({ url: item?.largeImageUrl || null });
|
||||||
|
|
||||||
if (!imageUrl) {
|
if (!imageUrl) {
|
||||||
@@ -314,12 +314,12 @@ const normalizeAlbumArtist = (
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
imageUrl: imageUrl || null,
|
imageUrl: imageUrl || null,
|
||||||
itemType: LibraryItem.ALBUM_ARTIST,
|
itemType: LibraryItem.ALBUM_ARTIST,
|
||||||
lastPlayedAt: normalizePlayDate(item),
|
userLastPlayedDate: normalizePlayDate(item),
|
||||||
mbz: item.mbzArtistId || null,
|
mbzId: item.mbzArtistId || null,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
playCount: item.playCount || 0,
|
playCount: item.playCount || 0,
|
||||||
serverId: server?.id || 'unknown',
|
_serverId: server?.id || 'unknown',
|
||||||
serverType: ServerType.NAVIDROME,
|
_serverType: ServerType.NAVIDROME,
|
||||||
similarArtists:
|
similarArtists:
|
||||||
item.similarArtists?.map((artist) => ({
|
item.similarArtists?.map((artist) => ({
|
||||||
id: artist.id,
|
id: artist.id,
|
||||||
@@ -353,14 +353,14 @@ const normalizePlaylist = (
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
imagePlaceholderUrl,
|
imagePlaceholderUrl,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
itemType: LibraryItem.PLAYLIST,
|
_itemType: LibraryItem.PLAYLIST,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
owner: item.ownerName,
|
owner: item.ownerName,
|
||||||
ownerId: item.ownerId,
|
ownerId: item.ownerId,
|
||||||
public: item.public,
|
public: item.public,
|
||||||
rules: item?.rules || null,
|
rules: item?.rules || null,
|
||||||
serverId: server?.id || 'unknown',
|
_serverId: server?.id || 'unknown',
|
||||||
serverType: ServerType.NAVIDROME,
|
_serverType: ServerType.NAVIDROME,
|
||||||
size: item.size,
|
size: item.size,
|
||||||
songCount: item.songCount,
|
songCount: item.songCount,
|
||||||
sync: item.sync,
|
sync: item.sync,
|
||||||
@@ -372,7 +372,7 @@ const normalizeGenre = (item: NDGenre): Genre => {
|
|||||||
albumCount: undefined,
|
albumCount: undefined,
|
||||||
id: item.id,
|
id: item.id,
|
||||||
imageUrl: null,
|
imageUrl: null,
|
||||||
itemType: LibraryItem.GENRE,
|
_itemType: LibraryItem.GENRE,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
songCount: undefined,
|
songCount: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
import createClient, { Middleware } from 'openapi-fetch';
|
import createClient, { Client, Middleware } from 'openapi-fetch';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
|
|
||||||
import { paths } from './subsonic-schema';
|
import { paths } from './subsonic-schema';
|
||||||
|
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
||||||
import { ApiController } from '/@/shared/types/adapter/api-controller-types';
|
import { API_CLIENT_NAME, ApiController } from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { ApiControllerError } from '/@/shared/types/adapter/api-controller-types';
|
import { ApiControllerError } from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||||
|
|
||||||
export function deserializeCredential(credential: string): Record<string, string> {
|
function deserializeCredential(credential: string): Record<string, string> {
|
||||||
return JSON.parse(credential);
|
return JSON.parse(credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serializeCredential(
|
function serializeCredential(username: string, credential: Record<string, string>, type: string) {
|
||||||
username: string,
|
|
||||||
credential: Record<string, string>,
|
|
||||||
type: string,
|
|
||||||
) {
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'apiKey':
|
case 'apiKey':
|
||||||
return JSON.stringify({ apiKey: credential.apiKey });
|
return JSON.stringify({ apiKey: credential.apiKey });
|
||||||
@@ -31,32 +27,36 @@ export function serializeCredential(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const middleware: Middleware = {
|
export const apiClient = createClient<paths>({
|
||||||
onRequest: async () => {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const client = createClient<paths>({
|
|
||||||
querySerializer: (params) => qs.stringify(params, { arrayFormat: 'repeat' }),
|
querySerializer: (params) => qs.stringify(params, { arrayFormat: 'repeat' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
client.use(middleware);
|
export const middleware: (server: ServerListItem) => Middleware = (server: ServerListItem) => ({
|
||||||
|
onRequest: async ({ params }) => {
|
||||||
|
const credential = deserializeCredential(server.credential);
|
||||||
|
|
||||||
|
if (params.query) {
|
||||||
|
params.query.v = '1.16.1';
|
||||||
|
params.query.c = API_CLIENT_NAME;
|
||||||
|
params.query.f = 'json';
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(credential)) {
|
||||||
|
params.query[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const client: SubsonicClient = createClient<paths>({
|
||||||
|
querySerializer: (params) => qs.stringify(params, { arrayFormat: 'repeat' }),
|
||||||
|
});
|
||||||
|
|
||||||
type ErrorResponseArgs = {
|
type ErrorResponseArgs = {
|
||||||
code?: number;
|
code?: number;
|
||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// type Req<T extends keyof paths> = paths[T]['get']['parameters'];
|
type SubsonicClient = Client<paths, `${string}/${string}`>;
|
||||||
|
|
||||||
// type Res<T extends keyof paths> = T extends keyof paths
|
|
||||||
// ? paths[T]['get'] extends {
|
|
||||||
// responses: {
|
|
||||||
// '200': { content: { 'application/json': { 'subsonic-response'?: infer R } } };
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// ? NonNullable<R>
|
|
||||||
// : never
|
|
||||||
// : never;
|
|
||||||
|
|
||||||
function errorResponse(args: ErrorResponseArgs): [ApiControllerError, null] {
|
function errorResponse(args: ErrorResponseArgs): [ApiControllerError, null] {
|
||||||
const message = `${i18n.t('error.genericError', { postProcess: 'sentenceCase' }) as string}${
|
const message = `${i18n.t('error.genericError', { postProcess: 'sentenceCase' }) as string}${
|
||||||
@@ -131,7 +131,7 @@ function toHttpErrorCode(subsonicErrorCode: number): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const adapter: ApiController = {
|
export const controller: ApiController = {
|
||||||
_utility: {
|
_utility: {
|
||||||
getImageUrl: (
|
getImageUrl: (
|
||||||
args: { id: string; size?: number; type: LibraryItem },
|
args: { id: string; size?: number; type: LibraryItem },
|
||||||
|
|||||||
@@ -211,3 +211,5 @@ export interface BaseQuery<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ExtractControllerResponse<T> = T extends ApiControllerFn<any, infer R> ? R : never;
|
export type ExtractControllerResponse<T> = T extends ApiControllerFn<any, infer R> ? R : never;
|
||||||
|
|
||||||
|
export const API_CLIENT_NAME = 'Feishin';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import i18n from 'src/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
|
|
||||||
import { BasePaginatedResponse } from '/@/shared/types/adapter/api-controller-types';
|
import { BasePaginatedResponse } from '/@/shared/types/adapter/api-controller-types';
|
||||||
|
|
||||||
export enum ServerListSortOptions {
|
export enum ServerListSortOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user