add new api controller, rework and rename types

This commit is contained in:
jeffvli
2025-07-12 16:00:26 -07:00
parent 6c360c3c19
commit a7f21db563
14 changed files with 325 additions and 317 deletions
+27
View File
@@ -0,0 +1,27 @@
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;
};
+121 -101
View File
@@ -1,99 +1,89 @@
import {
AlbumDetailArgs,
AlbumDetailRequest,
AlbumDetailResponse,
AlbumInfo,
AlbumListArgs,
AlbumListRequest,
AlbumListResponse,
} from '/@/shared/types/domain/album-domain-types';
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
import {
AlbumArtistDetailArgs,
AlbumArtistDetailRequest,
AlbumArtistDetailResponse,
AlbumArtistListArgs,
AlbumArtistListRequest,
AlbumArtistListResponse,
ArtistListArgs,
ArtistListRequest,
ArtistListResponse,
} from '/@/shared/types/domain/artist-domain-types';
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
import { GenreListArgs, GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
import { GenreListRequest, GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
import {
LyricsArgs,
LyricsRequest,
LyricsResponse,
StructuredLyric,
StructuredLyricsArgs,
StructuredLyricsRequest,
} from '/@/shared/types/domain/lyric-domain-types';
import { TranscodingArgs } from '/@/shared/types/domain/player-domain-types';
import { TranscodingRequest } from '/@/shared/types/domain/player-domain-types';
import {
AddToPlaylistArgs,
AddToPlaylistResponse,
CreatePlaylistArgs,
CreatePlaylistRequest,
CreatePlaylistResponse,
DeletePlaylistArgs,
DeletePlaylistRequest,
DeletePlaylistResponse,
MoveItemArgs,
PlaylistDetailArgs,
MoveItemRequest,
PlaylistDetailRequest,
PlaylistDetailResponse,
PlaylistListArgs,
PlaylistListRequest,
PlaylistListResponse,
PlaylistSongListArgs,
RemoveFromPlaylistArgs,
PlaylistSongListRequest,
RemoveFromPlaylistRequest,
RemoveFromPlaylistResponse,
UpdatePlaylistArgs,
UpdatePlaylistRequest,
UpdatePlaylistResponse,
} from '/@/shared/types/domain/playlist-domain-types';
import { SearchArgs, SearchResponse } from '/@/shared/types/domain/search-domain-types';
import { SearchRequest, SearchResponse } from '/@/shared/types/domain/search-domain-types';
import {
ServerInfo,
ServerInfoArgs,
ServerInfoRequest,
ServerListItem,
ServerMusicFolderListArgs,
ServerMusicFolderListRequest,
ServerMusicFolderListResponse,
ServerType,
} from '/@/shared/types/domain/server-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import {
RandomSongListArgs,
SimilarSongsArgs,
RandomSongListRequest,
SimilarSongsRequest,
Song,
SongDetailArgs,
SongDetailRequest,
SongDetailResponse,
SongListArgs,
SongListRequest,
SongListResponse,
TopSongListArgs,
TopSongListRequest,
TopSongListResponse,
} from '/@/shared/types/domain/song-domain-types';
import { TagArgs, TagsResponse } from '/@/shared/types/domain/tag-domain-types';
import { TagRequest, TagsResponse } from '/@/shared/types/domain/tag-domain-types';
import {
DownloadArgs,
FavoriteArgs,
DownloadRequest,
FavoriteRequest,
FavoriteResponse,
RatingResponse,
ScrobbleArgs,
ScrobbleRequest,
ScrobbleResponse,
SetRatingArgs,
ShareItemArgs,
SetRatingRequest,
ShareItemRequest,
ShareItemResponse,
UserListArgs,
UserListRequest,
UserListResponse,
} from '/@/shared/types/domain/user-domain-types';
export interface AdapterError {
code: number;
message: string;
}
export type AdapterFn<TRequest, TResponse> = (
request: TRequest,
server: ServerListItem,
options?: AdapterRequestOptions,
) => Promise<[AdapterError, null] | [null, TResponse]>;
export interface AdapterRequestOptions {
export type ApiClientProps = {
baseUrl?: string;
cache?: 'default' | 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached' | 'reload';
credentials?: 'include' | 'omit' | 'same-origin';
integrity?: string;
keepalive?: boolean;
mode?: 'cors' | 'navigate' | 'no-cors' | 'same-origin';
parseAs?: 'arrayBuffer' | 'blob' | 'json' | 'text';
priority?: 'auto' | 'high' | 'low';
redirect?: 'error' | 'follow' | 'manual';
referrer?: string;
@@ -108,86 +98,116 @@ export interface AdapterRequestOptions {
| 'strict-origin-when-cross-origin'
| 'unsafe-url';
signal?: AbortSignal | null;
window?: null;
}
};
export type ControllerEndpoint = {
export type ApiController = {
_utility: {
getDownloadUrl: ApiControllerFn<DownloadRequest, string>;
getImageUrl: (
args: { id: string; size?: number; type: LibraryItem },
server: ServerListItem,
) => string;
getStreamUrl: (
args: { bitRate?: number; format?: string; id: string },
server: ServerListItem,
) => string;
};
album: {
getDetail: (args: AlbumDetailArgs) => Promise<AlbumDetailResponse>;
getInfo?: (args: AlbumDetailArgs) => Promise<AlbumInfo>;
getList: (args: AlbumListArgs) => Promise<AlbumListResponse>;
getListCount: (args: AlbumListArgs) => Promise<number>;
getDetail: ApiControllerFn<AlbumDetailRequest, AlbumDetailResponse>;
getInfo?: ApiControllerFn<AlbumDetailRequest, AlbumInfo>;
getList: ApiControllerFn<AlbumListRequest, AlbumListResponse>;
getListCount: ApiControllerFn<AlbumListRequest, number>;
};
albumArtist: {
getDetail: (args: AlbumArtistDetailArgs) => Promise<AlbumArtistDetailResponse>;
getList: (args: AlbumArtistListArgs) => Promise<AlbumArtistListResponse>;
getListCount: (args: AlbumArtistListArgs) => Promise<number>;
getDetail: ApiControllerFn<AlbumArtistDetailRequest, AlbumArtistDetailResponse>;
getList: ApiControllerFn<AlbumArtistListRequest, AlbumArtistListResponse>;
getListCount: ApiControllerFn<AlbumArtistListRequest, number>;
};
artist: {
getList: (args: ArtistListArgs) => Promise<ArtistListResponse>;
getListCount: (args: ArtistListArgs) => Promise<number>;
getList: ApiControllerFn<ArtistListRequest, ArtistListResponse>;
getListCount: ApiControllerFn<ArtistListRequest, number>;
};
favorite: {
create: (args: FavoriteArgs) => Promise<FavoriteResponse>;
delete: (args: FavoriteArgs) => Promise<FavoriteResponse>;
create: ApiControllerFn<FavoriteRequest, FavoriteResponse>;
delete: ApiControllerFn<FavoriteRequest, FavoriteResponse>;
};
genre: {
getList: (args: GenreListArgs) => Promise<GenreListResponse>;
getList: ApiControllerFn<GenreListRequest, GenreListResponse>;
};
musicFolder: {
getList: (args: ServerMusicFolderListArgs) => Promise<ServerMusicFolderListResponse>;
getList: ApiControllerFn<ServerMusicFolderListRequest, ServerMusicFolderListResponse>;
};
playlist: {
addTo: (args: AddToPlaylistArgs) => Promise<AddToPlaylistResponse>;
create: (args: CreatePlaylistArgs) => Promise<CreatePlaylistResponse>;
delete: (args: DeletePlaylistArgs) => Promise<DeletePlaylistResponse>;
getDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
getList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
getListCount: (args: PlaylistListArgs) => Promise<number>;
getSongList: (args: PlaylistSongListArgs) => Promise<SongListResponse>;
moveItem?: (args: MoveItemArgs) => Promise<void>;
removeFrom: (args: RemoveFromPlaylistArgs) => Promise<RemoveFromPlaylistResponse>;
update: (args: UpdatePlaylistArgs) => Promise<UpdatePlaylistResponse>;
addTo: ApiControllerFn<AddToPlaylistArgs, AddToPlaylistResponse>;
create: ApiControllerFn<CreatePlaylistRequest, CreatePlaylistResponse>;
delete: ApiControllerFn<DeletePlaylistRequest, DeletePlaylistResponse>;
getDetail: ApiControllerFn<PlaylistDetailRequest, PlaylistDetailResponse>;
getList: ApiControllerFn<PlaylistListRequest, PlaylistListResponse>;
getListCount: ApiControllerFn<PlaylistListRequest, number>;
getSongList: ApiControllerFn<PlaylistSongListRequest, SongListResponse>;
moveItem?: ApiControllerFn<MoveItemRequest, void>;
removeFrom: ApiControllerFn<RemoveFromPlaylistRequest, RemoveFromPlaylistResponse>;
update: ApiControllerFn<UpdatePlaylistRequest, UpdatePlaylistResponse>;
};
server: {
authenticate: (
url: string,
body: { legacy?: boolean; password: string; username: string },
) => Promise<AuthenticationResponse>;
getCoverArtUrl: (
args: { id: string; size?: number; type: LibraryItem },
server: ServerListItem,
) => string;
getRoles: (
args: BaseEndpointArgs,
) => Promise<Array<string | { label: string; value: string }>>;
getServerInfo: (args: ServerInfoArgs) => Promise<ServerInfo>;
getStreamUrl: (
args: { bitRate?: number; format?: string; id: string },
server: ServerListItem,
) => string;
getTags: (args: TagArgs) => Promise<TagsResponse>;
getTranscodingUrl: (args: TranscodingArgs) => string;
getRoles: ApiControllerFn<
BaseEndpointArgs,
Array<string | { label: string; value: string }>
>;
getServerInfo: ApiControllerFn<ServerInfoRequest, ServerInfo>;
getTags: ApiControllerFn<TagRequest, TagsResponse>;
getTranscodingUrl: ApiControllerFn<TranscodingRequest, string>;
getType: () => ServerType;
scrobble: (args: ScrobbleArgs) => Promise<ScrobbleResponse>;
search: (args: SearchArgs) => Promise<SearchResponse>;
scrobble: ApiControllerFn<ScrobbleRequest, ScrobbleResponse>;
search: ApiControllerFn<SearchRequest, SearchResponse>;
};
song: {
getDetail: (args: SongDetailArgs) => Promise<SongDetailResponse>;
getDownloadUrl: (args: DownloadArgs) => string;
getList: (args: SongListArgs) => Promise<SongListResponse>;
getListCount: (args: SongListArgs) => Promise<number>;
getLyrics?: (args: LyricsArgs) => Promise<LyricsResponse>;
getRandomList: (args: RandomSongListArgs) => Promise<SongListResponse>;
getSimilar: (args: SimilarSongsArgs) => Promise<Song[]>;
getStructuredLyrics?: (args: StructuredLyricsArgs) => Promise<StructuredLyric[]>;
getTopList: (args: TopSongListArgs) => Promise<TopSongListResponse>;
getDetail: ApiControllerFn<SongDetailRequest, SongDetailResponse>;
getList: ApiControllerFn<SongListRequest, SongListResponse>;
getListCount: ApiControllerFn<SongListRequest, number>;
getLyrics?: ApiControllerFn<LyricsRequest, LyricsResponse>;
getRandomList: ApiControllerFn<RandomSongListRequest, SongListResponse>;
getSimilar: ApiControllerFn<SimilarSongsRequest, Song[]>;
getStructuredLyrics?: ApiControllerFn<StructuredLyricsRequest, StructuredLyric[]>;
getTopList: ApiControllerFn<TopSongListRequest, TopSongListResponse>;
};
user: {
getList?: (args: UserListArgs) => Promise<UserListResponse>;
setRating?: (args: SetRatingArgs) => Promise<RatingResponse>;
shareItem?: (args: ShareItemArgs) => Promise<ShareItemResponse>;
getList?: ApiControllerFn<UserListRequest, UserListResponse>;
setRating?: ApiControllerFn<SetRatingRequest, RatingResponse>;
shareItem?: ApiControllerFn<ShareItemRequest, ShareItemResponse>;
};
};
export type ExtractAdapterResponse<T> = T extends AdapterFn<any, infer R> ? R : never;
export interface ApiControllerError {
code: number;
message: string;
}
export type ApiControllerFn<TRequest, TResponse> = (
request: TRequest,
server: ServerListItem,
options?: ApiClientProps,
) => Promise<[ApiControllerError, null] | [null, TResponse]>;
export type BaseEndpointArgs = {
apiClientProps: {
signal?: AbortSignal;
};
};
export interface BasePaginatedResponse<T> {
error?: any | string;
items: T;
startIndex: number;
totalRecordCount: null | number;
}
export interface BaseQuery<T> {
sortBy: T;
sortOrder: ListSortOrder;
}
export type ExtractControllerResponse<T> = T extends ApiControllerFn<any, infer R> ? R : never;
@@ -6,11 +6,7 @@ import { JFAlbumListSort } from '/@/shared/api/jellyfin.types';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { NDAlbumListSort } from '/@/shared/api/navidrome.types';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
import { Genre } from '/@/shared/types/domain/genre-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
@@ -71,8 +67,6 @@ export enum AlbumListSort {
YEAR = 'year',
}
export type AlbumListArgs = BaseEndpointArgs & { query: AlbumListQuery };
export interface AlbumListQuery extends BaseQuery<AlbumListSort> {
_custom?: {
jellyfin?: Partial<z.infer<typeof jfType._parameters.albumList>>;
@@ -90,7 +84,10 @@ export interface AlbumListQuery extends BaseQuery<AlbumListSort> {
startIndex: number;
}
export type AlbumListRequest = { query: AlbumListQuery };
export type AlbumListResponse = BasePaginatedResponse<Album[]> | null | undefined;
type AlbumListSortMap = {
jellyfin: Record<AlbumListSort, JFAlbumListSort | undefined>;
navidrome: Record<AlbumListSort, NDAlbumListSort | undefined>;
@@ -200,10 +197,10 @@ export type Album = {
userRating: null | number;
} & { songs?: Song[] };
export type AlbumDetailArgs = BaseEndpointArgs & { query: AlbumDetailQuery };
export type AlbumDetailQuery = { id: string };
export type AlbumDetailRequest = { query: AlbumDetailQuery };
export type AlbumDetailResponse = Album | null | undefined;
export type AlbumInfo = {
+74 -96
View File
@@ -1,103 +1,79 @@
import { BaseEndpointArgs } from '/@/shared/types/adapter/api-controller-types';
import {
AlbumDetailArgs,
AlbumDetailRequest,
AlbumDetailResponse,
AlbumInfo,
AlbumListArgs,
AlbumListRequest,
AlbumListResponse,
} from '/@/shared/types/domain/album-domain-types';
import {
AlbumArtistDetailArgs,
AlbumArtistDetailRequest,
AlbumArtistDetailResponse,
AlbumArtistListArgs,
AlbumArtistListRequest,
AlbumArtistListResponse,
ArtistListArgs,
ArtistListRequest,
ArtistListResponse,
} from '/@/shared/types/domain/artist-domain-types';
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
import { GenreListArgs, GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
import { GenreListRequest, GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
import {
LyricsArgs,
LyricsRequest,
LyricsResponse,
StructuredLyric,
StructuredLyricsArgs,
StructuredLyricsRequest,
} from '/@/shared/types/domain/lyric-domain-types';
import { TranscodingArgs } from '/@/shared/types/domain/player-domain-types';
import { TranscodingRequest } from '/@/shared/types/domain/player-domain-types';
import {
AddToPlaylistArgs,
AddToPlaylistResponse,
CreatePlaylistArgs,
CreatePlaylistRequest,
CreatePlaylistResponse,
DeletePlaylistArgs,
DeletePlaylistRequest,
DeletePlaylistResponse,
MoveItemArgs,
PlaylistDetailArgs,
MoveItemRequest,
PlaylistDetailRequest,
PlaylistDetailResponse,
PlaylistListArgs,
PlaylistListRequest,
PlaylistListResponse,
PlaylistSongListArgs,
RemoveFromPlaylistArgs,
PlaylistSongListRequest,
RemoveFromPlaylistRequest,
RemoveFromPlaylistResponse,
UpdatePlaylistArgs,
UpdatePlaylistRequest,
UpdatePlaylistResponse,
} from '/@/shared/types/domain/playlist-domain-types';
import { SearchArgs, SearchResponse } from '/@/shared/types/domain/search-domain-types';
import { SearchRequest, SearchResponse } from '/@/shared/types/domain/search-domain-types';
import {
ServerInfo,
ServerInfoArgs,
ServerListItem,
ServerMusicFolderListArgs,
ServerInfoRequest,
ServerMusicFolderListRequest,
ServerMusicFolderListResponse,
} from '/@/shared/types/domain/server-domain-types';
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import {
RandomSongListArgs,
SimilarSongsArgs,
RandomSongListRequest,
SimilarSongsRequest,
Song,
SongDetailArgs,
SongDetailRequest,
SongDetailResponse,
SongListArgs,
SongListRequest,
SongListResponse,
TopSongListArgs,
TopSongListRequest,
TopSongListResponse,
} from '/@/shared/types/domain/song-domain-types';
import { TagArgs, TagsResponse } from '/@/shared/types/domain/tag-domain-types';
import { TagRequest, TagsResponse } from '/@/shared/types/domain/tag-domain-types';
import {
DownloadArgs,
FavoriteArgs,
DownloadRequest,
FavoriteRequest,
FavoriteResponse,
RatingResponse,
ScrobbleArgs,
ScrobbleRequest,
ScrobbleResponse,
SetRatingArgs,
ShareItemArgs,
SetRatingRequest,
ShareItemRequest,
ShareItemResponse,
UserListArgs,
UserListRequest,
UserListResponse,
} from '/@/shared/types/domain/user-domain-types';
export type BaseEndpointArgs = {
apiClientProps: {
server: null | ServerListItem;
signal?: AbortSignal;
};
};
export interface BasePaginatedResponse<T> {
error?: any | string;
items: T;
startIndex: number;
totalRecordCount: null | number;
}
export interface BaseQuery<T> {
sortBy: T;
sortOrder: ListSortOrder;
}
export type EndpointDetails = {
server: ServerListItem;
};
export const instanceOfCancellationError = (error: any) => {
return 'revert' in error;
};
@@ -108,44 +84,46 @@ export type ControllerEndpoint = {
url: string,
body: { legacy?: boolean; password: string; username: string },
) => Promise<AuthenticationResponse>;
createFavorite: (args: FavoriteArgs) => Promise<FavoriteResponse>;
createPlaylist: (args: CreatePlaylistArgs) => Promise<CreatePlaylistResponse>;
deleteFavorite: (args: FavoriteArgs) => Promise<FavoriteResponse>;
deletePlaylist: (args: DeletePlaylistArgs) => Promise<DeletePlaylistResponse>;
getAlbumArtistDetail: (args: AlbumArtistDetailArgs) => Promise<AlbumArtistDetailResponse>;
getAlbumArtistList: (args: AlbumArtistListArgs) => Promise<AlbumArtistListResponse>;
getAlbumArtistListCount: (args: AlbumArtistListArgs) => Promise<number>;
getAlbumDetail: (args: AlbumDetailArgs) => Promise<AlbumDetailResponse>;
getAlbumInfo?: (args: AlbumDetailArgs) => Promise<AlbumInfo>;
getAlbumList: (args: AlbumListArgs) => Promise<AlbumListResponse>;
getAlbumListCount: (args: AlbumListArgs) => Promise<number>;
getArtistList: (args: ArtistListArgs) => Promise<ArtistListResponse>;
getArtistListCount: (args: ArtistListArgs) => Promise<number>;
getDownloadUrl: (args: DownloadArgs) => string;
getGenreList: (args: GenreListArgs) => Promise<GenreListResponse>;
getLyrics?: (args: LyricsArgs) => Promise<LyricsResponse>;
getMusicFolderList: (args: ServerMusicFolderListArgs) => Promise<ServerMusicFolderListResponse>;
getPlaylistDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
getPlaylistList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
getPlaylistListCount: (args: PlaylistListArgs) => Promise<number>;
getPlaylistSongList: (args: PlaylistSongListArgs) => Promise<SongListResponse>;
getRandomSongList: (args: RandomSongListArgs) => Promise<SongListResponse>;
createFavorite: (args: FavoriteRequest) => Promise<FavoriteResponse>;
createPlaylist: (args: CreatePlaylistRequest) => Promise<CreatePlaylistResponse>;
deleteFavorite: (args: FavoriteRequest) => Promise<FavoriteResponse>;
deletePlaylist: (args: DeletePlaylistRequest) => Promise<DeletePlaylistResponse>;
getAlbumArtistDetail: (args: AlbumArtistDetailRequest) => Promise<AlbumArtistDetailResponse>;
getAlbumArtistList: (args: AlbumArtistListRequest) => Promise<AlbumArtistListResponse>;
getAlbumArtistListCount: (args: AlbumArtistListRequest) => Promise<number>;
getAlbumDetail: (args: AlbumDetailRequest) => Promise<AlbumDetailResponse>;
getAlbumInfo?: (args: AlbumDetailRequest) => Promise<AlbumInfo>;
getAlbumList: (args: AlbumListRequest) => Promise<AlbumListResponse>;
getAlbumListCount: (args: AlbumListRequest) => Promise<number>;
getArtistList: (args: ArtistListRequest) => Promise<ArtistListResponse>;
getArtistListCount: (args: ArtistListRequest) => Promise<number>;
getDownloadUrl: (args: DownloadRequest) => string;
getGenreList: (args: GenreListRequest) => Promise<GenreListResponse>;
getLyrics?: (args: LyricsRequest) => Promise<LyricsResponse>;
getMusicFolderList: (
args: ServerMusicFolderListRequest,
) => Promise<ServerMusicFolderListResponse>;
getPlaylistDetail: (args: PlaylistDetailRequest) => Promise<PlaylistDetailResponse>;
getPlaylistList: (args: PlaylistListRequest) => Promise<PlaylistListResponse>;
getPlaylistListCount: (args: PlaylistListRequest) => Promise<number>;
getPlaylistSongList: (args: PlaylistSongListRequest) => Promise<SongListResponse>;
getRandomSongList: (args: RandomSongListRequest) => Promise<SongListResponse>;
getRoles: (args: BaseEndpointArgs) => Promise<Array<string | { label: string; value: string }>>;
getServerInfo: (args: ServerInfoArgs) => Promise<ServerInfo>;
getSimilarSongs: (args: SimilarSongsArgs) => Promise<Song[]>;
getSongDetail: (args: SongDetailArgs) => Promise<SongDetailResponse>;
getSongList: (args: SongListArgs) => Promise<SongListResponse>;
getSongListCount: (args: SongListArgs) => Promise<number>;
getStructuredLyrics?: (args: StructuredLyricsArgs) => Promise<StructuredLyric[]>;
getTags?: (args: TagArgs) => Promise<TagsResponse>;
getTopSongs: (args: TopSongListArgs) => Promise<TopSongListResponse>;
getTranscodingUrl: (args: TranscodingArgs) => string;
getUserList?: (args: UserListArgs) => Promise<UserListResponse>;
movePlaylistItem?: (args: MoveItemArgs) => Promise<void>;
removeFromPlaylist: (args: RemoveFromPlaylistArgs) => Promise<RemoveFromPlaylistResponse>;
scrobble: (args: ScrobbleArgs) => Promise<ScrobbleResponse>;
search: (args: SearchArgs) => Promise<SearchResponse>;
setRating?: (args: SetRatingArgs) => Promise<RatingResponse>;
shareItem?: (args: ShareItemArgs) => Promise<ShareItemResponse>;
updatePlaylist: (args: UpdatePlaylistArgs) => Promise<UpdatePlaylistResponse>;
getServerInfo: (args: ServerInfoRequest) => Promise<ServerInfo>;
getSimilarSongs: (args: SimilarSongsRequest) => Promise<Song[]>;
getSongDetail: (args: SongDetailRequest) => Promise<SongDetailResponse>;
getSongList: (args: SongListRequest) => Promise<SongListResponse>;
getSongListCount: (args: SongListRequest) => Promise<number>;
getStructuredLyrics?: (args: StructuredLyricsRequest) => Promise<StructuredLyric[]>;
getTags?: (args: TagRequest) => Promise<TagsResponse>;
getTopSongs: (args: TopSongListRequest) => Promise<TopSongListResponse>;
getTranscodingUrl: (args: TranscodingRequest) => string;
getUserList?: (args: UserListRequest) => Promise<UserListResponse>;
movePlaylistItem?: (args: MoveItemRequest) => Promise<void>;
removeFromPlaylist: (args: RemoveFromPlaylistRequest) => Promise<RemoveFromPlaylistResponse>;
scrobble: (args: ScrobbleRequest) => Promise<ScrobbleResponse>;
search: (args: SearchRequest) => Promise<SearchResponse>;
setRating?: (args: SetRatingRequest) => Promise<RatingResponse>;
shareItem?: (args: ShareItemRequest) => Promise<ShareItemResponse>;
updatePlaylist: (args: UpdatePlaylistRequest) => Promise<UpdatePlaylistResponse>;
};
+11 -12
View File
@@ -6,11 +6,7 @@ import { JFAlbumArtistListSort, JFArtistListSort } from '/@/shared/api/jellyfin.
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { NDAlbumArtistListSort } from '/@/shared/api/navidrome.types';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
import { Genre } from '/@/shared/types/domain/genre-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
@@ -142,13 +138,11 @@ export enum ArtistListSort {
SONG_COUNT = 'songCount',
}
export type AlbumArtistDetailArgs = BaseEndpointArgs & { query: AlbumArtistDetailQuery };
export type AlbumArtistDetailQuery = { id: string };
export type AlbumArtistDetailResponse = AlbumArtist | null;
export type AlbumArtistDetailRequest = { query: AlbumArtistDetailQuery };
export type ArtistListArgs = BaseEndpointArgs & { query: ArtistListQuery };
export type AlbumArtistDetailResponse = AlbumArtist | null;
export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
_custom?: {
@@ -162,6 +156,8 @@ export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
startIndex: number;
}
export type ArtistListRequest = { query: ArtistListQuery };
export type ArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
type ArtistListSortMap = {
jellyfin: Record<ArtistListSort, JFArtistListSort | undefined>;
@@ -224,8 +220,6 @@ export enum AlbumArtistListSort {
SONG_COUNT = 'songCount',
}
export type AlbumArtistListArgs = BaseEndpointArgs & { query: AlbumArtistListQuery };
export interface AlbumArtistListQuery extends BaseQuery<AlbumArtistListSort> {
_custom?: {
jellyfin?: Partial<z.infer<typeof jfType._parameters.albumArtistList>>;
@@ -236,14 +230,19 @@ export interface AlbumArtistListQuery extends BaseQuery<AlbumArtistListSort> {
searchTerm?: string;
startIndex: number;
}
export type AlbumArtistListRequest = { query: AlbumArtistListQuery };
export type AlbumArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
export type ArtistInfoArgs = BaseEndpointArgs & { query: ArtistInfoQuery };
export type ArtistInfoQuery = {
artistId: string;
limit: number;
musicFolderId?: string;
};
export type ArtistInfoRequest = { query: ArtistInfoQuery };
export const sortAlbumArtistList = (
artists: AlbumArtist[],
sortBy: AlbumArtistListSort | ArtistListSort,
@@ -1,11 +1,7 @@
import i18n from '/@/i18n/i18n';
import { JFGenreListSort } from '/@/shared/api/jellyfin.types';
import { NDGenreListSort } from '/@/shared/api/navidrome.types';
import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { UserListSort } from '/@/shared/types/domain/user-domain-types';
@@ -30,8 +26,6 @@ export type Genre = {
songCount?: number;
};
export type GenreListArgs = BaseEndpointArgs & { query: GenreListQuery };
export interface GenreListQuery extends BaseQuery<GenreListSort> {
_custom?: {
jellyfin?: null;
@@ -43,9 +37,10 @@ export interface GenreListQuery extends BaseQuery<GenreListSort> {
startIndex: number;
}
export type GenreListRequest = { query: GenreListQuery };
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
export type GenresResponse = Genre[];
type GenreListSortMap = {
jellyfin: Record<GenreListSort, JFGenreListSort | undefined>;
navidrome: Record<GenreListSort, NDGenreListSort | undefined>;
@@ -1,4 +1,3 @@
import { BaseEndpointArgs } from './api-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
export enum LyricSource {
@@ -35,9 +34,6 @@ export type LyricGetQuery = {
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
export type LyricsArgs = BaseEndpointArgs & {
query: LyricsQuery;
};
export type LyricSearchQuery = {
album?: string;
artist?: string;
@@ -50,22 +46,26 @@ export type LyricsQuery = {
songId: string;
};
export type LyricsRequest = {
query: LyricsQuery;
};
export type LyricsResponse = string | SynchronizedLyricsArray;
export type StructuredLyric = (StructuredSyncedLyric | StructuredUnsyncedLyric) & {
lang: string;
};
export type StructuredLyricsArgs = BaseEndpointArgs & {
query: LyricsQuery;
};
export type StructuredLyricsRequest = { query: LyricsQuery };
export type StructuredSyncedLyric = Omit<FullLyricsMetadata, 'lyrics'> & {
lyrics: SynchronizedLyricsArray;
synced: true;
};
export type StructuredUnsyncedLyric = Omit<FullLyricsMetadata, 'lyrics'> & {
lyrics: string;
synced: false;
};
export type SynchronizedLyricsArray = Array<[number, string]>;
@@ -1,4 +1,3 @@
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { PlayerStatus } from '/@/shared/types/types';
@@ -29,15 +28,17 @@ export interface QueueData {
next?: QueueSong;
previous?: QueueSong;
}
export type QueueSong = Song & {
uniqueId: string;
};
export type TranscodingArgs = BaseEndpointArgs & {
query: TranscodingQuery;
};
export type TranscodingQuery = {
base: string;
bitrate?: number;
format?: string;
};
export type TranscodingRequest = {
query: TranscodingQuery;
};
@@ -9,7 +9,7 @@ import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
} from '/@/shared/types/adapter/api-controller-types';
import { Genre } from '/@/shared/types/domain/genre-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
@@ -58,8 +58,6 @@ export type AddToPlaylistQuery = {
export type AddToPlaylistResponse = null | undefined;
export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody; serverId?: string };
export type CreatePlaylistBody = {
_custom?: {
navidrome?: {
@@ -74,15 +72,20 @@ export type CreatePlaylistBody = {
public?: boolean;
};
export type CreatePlaylistResponse = undefined | { id: string };
export type DeletePlaylistArgs = BaseEndpointArgs & {
query: DeletePlaylistQuery;
export type CreatePlaylistRequest = {
body: CreatePlaylistBody;
serverId?: string;
};
export type CreatePlaylistResponse = undefined | { id: string };
export type DeletePlaylistQuery = { id: string };
export type DeletePlaylistRequest = {
query: DeletePlaylistQuery;
serverId?: string;
};
export type DeletePlaylistResponse = null | undefined;
export type Playlist = {
@@ -104,8 +107,6 @@ export type Playlist = {
songCount: null | number;
sync?: boolean | null;
};
export type PlaylistListArgs = BaseEndpointArgs & { query: PlaylistListQuery };
export interface PlaylistListQuery extends BaseQuery<PlaylistListSort> {
_custom?: {
jellyfin?: Partial<z.infer<typeof jfType._parameters.playlistList>>;
@@ -116,26 +117,22 @@ export interface PlaylistListQuery extends BaseQuery<PlaylistListSort> {
startIndex: number;
}
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
export type PlaylistListRequest = { query: PlaylistListQuery };
export type RemoveFromPlaylistArgs = BaseEndpointArgs & {
query: RemoveFromPlaylistQuery;
serverId?: string;
};
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
export type RemoveFromPlaylistQuery = {
id: string;
songId: string[];
};
export type RemoveFromPlaylistResponse = null | undefined;
export type UpdatePlaylistArgs = BaseEndpointArgs & {
body: UpdatePlaylistBody;
query: UpdatePlaylistQuery;
export type RemoveFromPlaylistRequest = {
query: RemoveFromPlaylistQuery;
serverId?: string;
};
export type RemoveFromPlaylistResponse = null | undefined;
export type UpdatePlaylistBody = {
_custom?: {
navidrome?: {
@@ -155,6 +152,12 @@ export type UpdatePlaylistQuery = {
id: string;
};
export type UpdatePlaylistRequest = {
body: UpdatePlaylistBody;
query: UpdatePlaylistQuery;
serverId?: string;
};
export type UpdatePlaylistResponse = null | undefined;
type PlaylistListSortMap = {
@@ -189,10 +192,6 @@ export const playlistListSortMap: PlaylistListSortMap = {
updatedAt: undefined,
},
};
export type MoveItemArgs = BaseEndpointArgs & {
query: MoveItemQuery;
};
export type MoveItemQuery = {
endingIndex: number;
playlistId: string;
@@ -200,15 +199,18 @@ export type MoveItemQuery = {
trackId: string;
};
export type PlaylistDetailArgs = BaseEndpointArgs & { query: PlaylistDetailQuery };
export type MoveItemRequest = {
query: MoveItemQuery;
};
export type PlaylistDetailQuery = {
id: string;
};
export type PlaylistDetailRequest = { query: PlaylistDetailQuery };
export type PlaylistDetailResponse = Playlist;
export type PlaylistSongListArgs = BaseEndpointArgs & { query: PlaylistSongListQuery };
export type PlaylistSongListQuery = {
id: string;
limit?: number;
@@ -217,4 +219,6 @@ export type PlaylistSongListQuery = {
startIndex: number;
};
export type PlaylistSongListRequest = { query: PlaylistSongListQuery };
export type PlaylistSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
@@ -1,5 +1,4 @@
import { Album } from '/@/shared/types/domain/album-domain-types';
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
import { AlbumArtist } from '/@/shared/types/domain/artist-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
@@ -17,10 +16,6 @@ export type SearchAlbumsQuery = {
query?: string;
};
export type SearchArgs = BaseEndpointArgs & {
query: SearchQuery;
};
export type SearchQuery = {
albumArtistLimit?: number;
albumArtistStartIndex?: number;
@@ -32,6 +27,8 @@ export type SearchQuery = {
songStartIndex?: number;
};
export type SearchRequest = { query: SearchQuery };
export type SearchResponse = {
albumArtists: AlbumArtist[];
albums: Album[];
@@ -1,6 +1,6 @@
import i18n from 'src/i18n/i18n';
import { BaseEndpointArgs, BasePaginatedResponse } from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse } from '/@/shared/types/adapter/api-controller-types';
export enum ServerListSortOptions {
CREATED_AT = 'createdAt',
@@ -40,7 +40,7 @@ export type ServerInfo = {
version: string;
};
export type ServerInfoArgs = BaseEndpointArgs;
export type ServerInfoRequest = null;
export type ServerListItem = {
credential: string;
@@ -61,10 +61,10 @@ export type ServerMusicFolder = {
name: string;
};
export type ServerMusicFolderListArgs = BaseEndpointArgs;
export type ServerMusicFolderListQuery = null;
export type ServerMusicFolderListRequest = null;
export type ServerMusicFolderListResponse =
| BasePaginatedResponse<ServerMusicFolder[]>
| null
+19 -18
View File
@@ -6,11 +6,7 @@ import { JFSongListSort } from '/@/shared/api/jellyfin.types';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { NDSongListSort } from '/@/shared/api/navidrome.types';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
import { Genre } from '/@/shared/types/domain/genre-domain-types';
import { Played, QueueSong } from '/@/shared/types/domain/player-domain-types';
@@ -58,6 +54,7 @@ export const SongListSortOptionsLabels = {
[SongListSortOptions.RELEASE_DATE]: i18n.t('filter.releaseDate'),
[SongListSortOptions.YEAR]: i18n.t('filter.year'),
};
export enum SongListSort {
ALBUM = 'album',
ALBUM_ARTIST = 'albumArtist',
@@ -78,6 +75,7 @@ export enum SongListSort {
RELEASE_DATE = 'releaseDate',
YEAR = 'year',
}
export type Song = {
album: null | string;
albumArtists: RelatedArtist[];
@@ -121,7 +119,6 @@ export type Song = {
userFavorite: boolean;
userRating: null | number;
};
export type SongListArgs = BaseEndpointArgs & { query: SongListQuery };
export interface SongListQuery extends BaseQuery<SongListSort> {
_custom?: {
@@ -143,6 +140,8 @@ export interface SongListQuery extends BaseQuery<SongListSort> {
startIndex: number;
}
export type SongListRequest = { query: SongListQuery };
export type SongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
type SongListSortMap = {
jellyfin: Record<SongListSort, JFSongListSort | undefined>;
@@ -212,15 +211,12 @@ export const songListSortMap: SongListSortMap = {
year: undefined,
},
};
export type GainInfo = {
album?: number;
track?: number;
};
export type RandomSongListArgs = BaseEndpointArgs & {
query: RandomSongListQuery;
};
export type RandomSongListQuery = {
genre?: string;
limit?: number;
@@ -229,30 +225,35 @@ export type RandomSongListQuery = {
musicFolderId?: string;
played: Played;
};
export type RandomSongListResponse = SongListResponse;
export type SimilarSongsArgs = BaseEndpointArgs & {
query: SimilarSongsQuery;
};
export type RandomSongListRequest = { query: RandomSongListQuery };
export type RandomSongListResponse = SongListResponse;
export type SimilarSongsQuery = {
albumArtistIds: string[];
count?: number;
songId: string;
};
export type SongDetailArgs = BaseEndpointArgs & { query: SongDetailQuery };
export type SimilarSongsRequest = { query: SimilarSongsQuery };
export type SongDetailQuery = { id: string };
export type SongDetailResponse = null | Song | undefined;
export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery };
export type SongDetailRequest = { query: SongDetailQuery };
export type SongDetailResponse = null | Song | undefined;
export type TopSongListQuery = {
artist: string;
artistId: string;
limit?: number;
};
export type TopSongListRequest = { query: TopSongListQuery };
export type TopSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
export const sortSongList = (
songs: QueueSong[],
sortBy: SongListSort,
@@ -299,7 +300,7 @@ export const sortSongList = (
results = orderBy(
results,
[
(v) => v.genres?.[0].name.toLowerCase(),
(v) => v.genres?.[0]?.name.toLowerCase(),
(v) => v.album?.toLowerCase(),
'discNumber',
'trackNumber',
+2 -5
View File
@@ -1,4 +1,3 @@
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
export type Tag = {
@@ -6,15 +5,13 @@ export type Tag = {
options: string[];
};
export type TagArgs = BaseEndpointArgs & {
query: TagQuery;
};
export type TagQuery = {
folder?: string;
type: LibraryItem.ALBUM | LibraryItem.SONG;
};
export type TagRequest = { query: TagQuery };
export type TagsResponse = {
boolTags?: string[];
enumTags?: Tag[];
+15 -23
View File
@@ -1,10 +1,6 @@
import i18n from '/@/i18n/i18n';
import { NDUserListSort } from '/@/shared/api/navidrome.types';
import {
BaseEndpointArgs,
BasePaginatedResponse,
BaseQuery,
} from '/@/shared/types/domain/api-domain-types';
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
import { AnyLibraryItems, LibraryItem } from '/@/shared/types/domain/shared-domain-types';
export enum UserListSortOptions {
@@ -21,24 +17,23 @@ export const UserListSortOptionsLabels = {
[UserListSortOptions.UPDATED_AT]: i18n.t('filter.updatedAt'),
};
export type FavoriteArgs = BaseEndpointArgs & { query: FavoriteQuery; serverId?: string };
export type FavoriteQuery = {
id: string[];
type: LibraryItem;
};
export type FavoriteResponse = null | undefined;
export type FavoriteRequest = { query: FavoriteQuery; serverId?: string };
export type FavoriteResponse = null;
export type RatingQuery = {
item: AnyLibraryItems;
rating: number;
};
export type RatingResponse = null | undefined;
export type RatingResponse = null;
export type SetRatingArgs = BaseEndpointArgs & { query: RatingQuery; serverId?: string };
export type UserListArgs = BaseEndpointArgs & { query: UserListQuery };
export type SetRatingRequest = { query: RatingQuery; serverId?: string };
export interface UserListQuery extends BaseQuery<UserListSort> {
_custom?: {
@@ -51,6 +46,8 @@ export interface UserListQuery extends BaseQuery<UserListSort> {
startIndex: number;
}
export type UserListRequest = { query: UserListQuery };
export type UserListResponse = BasePaginatedResponse<User[]> | null | undefined;
type UserListSortMap = {
@@ -75,18 +72,11 @@ export enum UserListSort {
NAME = 'name',
}
export type DownloadArgs = BaseEndpointArgs & {
query: DownloadQuery;
};
export type DownloadQuery = {
id: string;
};
export type ScrobbleArgs = BaseEndpointArgs & {
query: ScrobbleQuery;
serverId?: string;
};
export type DownloadRequest = { query: DownloadQuery };
export type ScrobbleQuery = {
event?: 'pause' | 'start' | 'timeupdate' | 'unpause';
@@ -95,9 +85,9 @@ export type ScrobbleQuery = {
submission: boolean;
};
export type ScrobbleResponse = null | undefined;
export type ScrobbleRequest = { query: ScrobbleQuery; serverId?: string };
export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody; serverId?: string };
export type ScrobbleResponse = null;
export type ShareItemBody = {
description: string;
@@ -107,7 +97,9 @@ export type ShareItemBody = {
resourceType: string;
};
export type ShareItemResponse = undefined | { id: string };
export type ShareItemRequest = { body: ShareItemBody; serverId?: string };
export type ShareItemResponse = null | { id: string };
export type User = {
createdAt: null | string;