mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
move all domain types to separate files
This commit is contained in:
@@ -1,517 +0,0 @@
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import reverse from 'lodash/reverse';
|
||||
import shuffle from 'lodash/shuffle';
|
||||
|
||||
import {
|
||||
AlbumArtistDetailArgs,
|
||||
AlbumArtistDetailResponse,
|
||||
AlbumArtistListArgs,
|
||||
AlbumArtistListResponse,
|
||||
AlbumArtistListSort,
|
||||
ArtistListArgs,
|
||||
ArtistListResponse,
|
||||
ArtistListSort,
|
||||
} from './domain/artist-domain-types';
|
||||
|
||||
import { JFSortOrder } from '/@/shared/api/jellyfin.types';
|
||||
import { NDSortOrder } from '/@/shared/api/navidrome.types';
|
||||
import {
|
||||
Album,
|
||||
AlbumDetailArgs,
|
||||
AlbumDetailResponse,
|
||||
AlbumInfo,
|
||||
AlbumListArgs,
|
||||
AlbumListResponse,
|
||||
AlbumListSort,
|
||||
} from '/@/shared/types/domain/album-domain-types';
|
||||
import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { GenreListArgs, GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
|
||||
import {
|
||||
LyricsArgs,
|
||||
LyricsResponse,
|
||||
StructuredLyric,
|
||||
StructuredLyricsArgs,
|
||||
} from '/@/shared/types/domain/lyric-domain-types';
|
||||
import {
|
||||
AddToPlaylistArgs,
|
||||
AddToPlaylistResponse,
|
||||
CreatePlaylistArgs,
|
||||
CreatePlaylistResponse,
|
||||
DeletePlaylistArgs,
|
||||
DeletePlaylistResponse,
|
||||
Playlist,
|
||||
PlaylistDetailArgs,
|
||||
PlaylistDetailResponse,
|
||||
PlaylistListArgs,
|
||||
PlaylistListResponse,
|
||||
PlaylistSongListArgs,
|
||||
RemoveFromPlaylistArgs,
|
||||
RemoveFromPlaylistResponse,
|
||||
UpdatePlaylistArgs,
|
||||
UpdatePlaylistResponse,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { SearchArgs, SearchResponse } from '/@/shared/types/domain/search-domain-types';
|
||||
import {
|
||||
ServerInfo,
|
||||
ServerInfoArgs,
|
||||
ServerListItem,
|
||||
ServerMusicFolderListArgs,
|
||||
ServerMusicFolderListResponse,
|
||||
} from '/@/shared/types/domain/server-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import {
|
||||
RandomSongListArgs,
|
||||
SimilarSongsArgs,
|
||||
Song,
|
||||
SongDetailArgs,
|
||||
SongDetailResponse,
|
||||
SongListArgs,
|
||||
SongListResponse,
|
||||
SongListSort,
|
||||
TopSongListArgs,
|
||||
TopSongListResponse,
|
||||
} from '/@/shared/types/domain/song-domain-types';
|
||||
import {
|
||||
FavoriteArgs,
|
||||
FavoriteResponse,
|
||||
RatingResponse,
|
||||
ScrobbleArgs,
|
||||
ScrobbleResponse,
|
||||
SetRatingArgs,
|
||||
UserListArgs,
|
||||
UserListResponse,
|
||||
} from '/@/shared/types/domain/user-domain-types';
|
||||
import { PlayerStatus } from '/@/shared/types/types';
|
||||
|
||||
export enum LibraryItem {
|
||||
ALBUM = 'album',
|
||||
ALBUM_ARTIST = 'albumArtist',
|
||||
ARTIST = 'artist',
|
||||
GENRE = 'genre',
|
||||
PLAYLIST = 'playlist',
|
||||
SONG = 'song',
|
||||
}
|
||||
|
||||
export type AnyLibraryItem = Album | AlbumArtist | Artist | Playlist | QueueSong | Song;
|
||||
|
||||
export type AnyLibraryItems =
|
||||
| Album[]
|
||||
| AlbumArtist[]
|
||||
| Artist[]
|
||||
| Playlist[]
|
||||
| QueueSong[]
|
||||
| Song[];
|
||||
|
||||
export interface PlayerData {
|
||||
current: {
|
||||
index: number;
|
||||
nextIndex?: number;
|
||||
player: 1 | 2;
|
||||
previousIndex?: number;
|
||||
shuffledIndex: number;
|
||||
song?: QueueSong;
|
||||
status: PlayerStatus;
|
||||
};
|
||||
player1?: QueueSong;
|
||||
player2?: QueueSong;
|
||||
queue: QueueData;
|
||||
}
|
||||
|
||||
export interface QueueData {
|
||||
current?: QueueSong;
|
||||
length: number;
|
||||
next?: QueueSong;
|
||||
previous?: QueueSong;
|
||||
}
|
||||
|
||||
export type QueueSong = Song & {
|
||||
uniqueId: string;
|
||||
};
|
||||
|
||||
type SortOrderMap = {
|
||||
jellyfin: Record<ListSortOrder, JFSortOrder>;
|
||||
navidrome: Record<ListSortOrder, NDSortOrder>;
|
||||
subsonic: Record<ListSortOrder, undefined>;
|
||||
};
|
||||
|
||||
export const sortOrderMap: SortOrderMap = {
|
||||
jellyfin: {
|
||||
ASC: JFSortOrder.ASC,
|
||||
DESC: JFSortOrder.DESC,
|
||||
},
|
||||
navidrome: {
|
||||
ASC: NDSortOrder.ASC,
|
||||
DESC: NDSortOrder.DESC,
|
||||
},
|
||||
subsonic: {
|
||||
ASC: undefined,
|
||||
DESC: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export enum ExternalSource {
|
||||
LASTFM = 'LASTFM',
|
||||
MUSICBRAINZ = 'MUSICBRAINZ',
|
||||
SPOTIFY = 'SPOTIFY',
|
||||
THEAUDIODB = 'THEAUDIODB',
|
||||
}
|
||||
|
||||
export enum ExternalType {
|
||||
ID = 'ID',
|
||||
LINK = 'LINK',
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
BACKDROP = 'BACKDROP',
|
||||
LOGO = 'LOGO',
|
||||
PRIMARY = 'PRIMARY',
|
||||
SCREENSHOT = 'SCREENSHOT',
|
||||
}
|
||||
|
||||
export enum Played {
|
||||
All = 'all',
|
||||
Never = 'never',
|
||||
Played = 'played',
|
||||
}
|
||||
|
||||
export type AuthenticationResponse = {
|
||||
credential: string;
|
||||
ndCredential?: string;
|
||||
userId: null | string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
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 type GainInfo = {
|
||||
album?: number;
|
||||
track?: number;
|
||||
};
|
||||
|
||||
export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody; serverId?: string };
|
||||
|
||||
export type ShareItemBody = {
|
||||
description: string;
|
||||
downloadable: boolean;
|
||||
expires: number;
|
||||
resourceIds: string;
|
||||
resourceType: string;
|
||||
};
|
||||
|
||||
export type ShareItemResponse = undefined | { id: string };
|
||||
|
||||
export const instanceOfCancellationError = (error: any) => {
|
||||
return 'revert' in error;
|
||||
};
|
||||
|
||||
export enum LyricSource {
|
||||
GENIUS = 'Genius',
|
||||
LRCLIB = 'lrclib.net',
|
||||
NETEASE = 'NetEase',
|
||||
}
|
||||
|
||||
export type ControllerEndpoint = {
|
||||
addToPlaylist: (args: AddToPlaylistArgs) => Promise<AddToPlaylistResponse>;
|
||||
authenticate: (
|
||||
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>;
|
||||
// getArtistInfo?: (args: any) => void;
|
||||
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>;
|
||||
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<TagResponses>;
|
||||
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>;
|
||||
};
|
||||
|
||||
export type DownloadArgs = BaseEndpointArgs & {
|
||||
query: DownloadQuery;
|
||||
};
|
||||
|
||||
export type DownloadQuery = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
// This type from https://wicg.github.io/local-font-access/#fontdata
|
||||
// NOTE: it is still experimental, so this should be updates as appropriate
|
||||
export type FontData = {
|
||||
family: string;
|
||||
fullName: string;
|
||||
postscriptName: string;
|
||||
style: string;
|
||||
};
|
||||
|
||||
export type MoveItemArgs = BaseEndpointArgs & {
|
||||
query: MoveItemQuery;
|
||||
};
|
||||
|
||||
export type MoveItemQuery = {
|
||||
endingIndex: number;
|
||||
playlistId: string;
|
||||
startingIndex: number;
|
||||
trackId: string;
|
||||
};
|
||||
|
||||
export type Tag = {
|
||||
name: string;
|
||||
options: string[];
|
||||
};
|
||||
|
||||
export type TagArgs = BaseEndpointArgs & {
|
||||
query: TagQuery;
|
||||
};
|
||||
|
||||
export type TagQuery = {
|
||||
folder?: string;
|
||||
type: LibraryItem.ALBUM | LibraryItem.SONG;
|
||||
};
|
||||
|
||||
export type TagResponses = {
|
||||
boolTags?: string[];
|
||||
enumTags?: Tag[];
|
||||
};
|
||||
|
||||
export type TranscodingArgs = BaseEndpointArgs & {
|
||||
query: TranscodingQuery;
|
||||
};
|
||||
|
||||
export type TranscodingQuery = {
|
||||
base: string;
|
||||
bitrate?: number;
|
||||
format?: string;
|
||||
};
|
||||
|
||||
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: ListSortOrder) => {
|
||||
let results = albums;
|
||||
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['albumArtist', (v) => v.name.toLowerCase()],
|
||||
[order, 'asc'],
|
||||
);
|
||||
break;
|
||||
case AlbumListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
case AlbumListSort.FAVORITED:
|
||||
results = orderBy(results, ['starred'], [order]);
|
||||
break;
|
||||
case AlbumListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
case AlbumListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
case AlbumListSort.RATING:
|
||||
results = orderBy(results, ['userRating'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['createdAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_PLAYED:
|
||||
results = orderBy(results, ['lastPlayedAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.SONG_COUNT:
|
||||
results = orderBy(results, ['songCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.YEAR:
|
||||
results = orderBy(results, ['releaseYear'], [order]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const sortSongList = (
|
||||
songs: QueueSong[],
|
||||
sortBy: SongListSort,
|
||||
sortOrder: ListSortOrder,
|
||||
) => {
|
||||
let results = songs;
|
||||
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case SongListSort.ALBUM:
|
||||
results = orderBy(
|
||||
results,
|
||||
[(v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['albumArtist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['artist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.FAVORITED:
|
||||
results = orderBy(results, ['userFavorite', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.GENRE:
|
||||
results = orderBy(
|
||||
results,
|
||||
[
|
||||
(v) => v.genres?.[0].name.toLowerCase(),
|
||||
(v) => v.album?.toLowerCase(),
|
||||
'discNumber',
|
||||
'trackNumber',
|
||||
],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ID:
|
||||
if (order === 'desc') {
|
||||
results = reverse(results as any);
|
||||
}
|
||||
break;
|
||||
|
||||
case SongListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
|
||||
case SongListSort.RATING:
|
||||
results = orderBy(results, ['userRating', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['created'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.YEAR:
|
||||
results = orderBy(
|
||||
results,
|
||||
['year', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
||||
[order, 'asc', 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const sortAlbumArtistList = (
|
||||
artists: AlbumArtist[],
|
||||
sortBy: AlbumArtistListSort | ArtistListSort,
|
||||
sortOrder: ListSortOrder,
|
||||
) => {
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
let results = artists;
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumArtistListSort.ALBUM_COUNT:
|
||||
results = orderBy(artists, ['albumCount', (v) => v.name.toLowerCase()], [order, 'asc']);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.FAVORITED:
|
||||
results = orderBy(artists, ['starred'], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.NAME:
|
||||
results = orderBy(artists, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.RATING:
|
||||
results = orderBy(artists, ['userRating'], [order]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
import { orderBy, shuffle } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFAlbumListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||
import { NDAlbumListSort } from '/@/shared/api/navidrome.types';
|
||||
@@ -9,11 +10,11 @@ import {
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
LibraryItem,
|
||||
} from '/@/shared/types/domain-types';
|
||||
} from '/@/shared/types/domain/api-domain-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';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export enum AlbumListSortOptions {
|
||||
@@ -51,6 +52,7 @@ export const AlbumListSortOptionsLabels = {
|
||||
[AlbumListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||
[AlbumListSortOptions.YEAR]: i18n.t('filter.year'),
|
||||
};
|
||||
|
||||
export enum AlbumListSort {
|
||||
ALBUM_ARTIST = 'albumArtist',
|
||||
ARTIST = 'artist',
|
||||
@@ -87,7 +89,6 @@ export interface AlbumListQuery extends BaseQuery<AlbumListSort> {
|
||||
searchTerm?: string;
|
||||
startIndex: number;
|
||||
}
|
||||
// Album List
|
||||
|
||||
export type AlbumListResponse = BasePaginatedResponse<Album[]> | null | undefined;
|
||||
type AlbumListSortMap = {
|
||||
@@ -150,13 +151,20 @@ export const albumListSortMap: AlbumListSortMap = {
|
||||
year: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export type Album = {
|
||||
albumArtist: string;
|
||||
albumArtists: RelatedArtist[];
|
||||
artists: RelatedArtist[];
|
||||
backdropImageUrl: null | string;
|
||||
comment: null | string;
|
||||
createdAt: string;
|
||||
createdDate: string;
|
||||
description: null | string;
|
||||
discTitles: {
|
||||
disc: number;
|
||||
title: string;
|
||||
}[];
|
||||
displayArtist: null | string;
|
||||
duration: null | number;
|
||||
genres: Genre[];
|
||||
id: string;
|
||||
@@ -164,33 +172,91 @@ export type Album = {
|
||||
imageUrl: null | string;
|
||||
isCompilation: boolean | null;
|
||||
itemType: LibraryItem.ALBUM;
|
||||
lastPlayedAt: null | string;
|
||||
mbzId: null | string;
|
||||
mbzAlbumId: null | string;
|
||||
mbzReleaseGroupId: null | string;
|
||||
missing: boolean;
|
||||
name: string;
|
||||
originalDate: null | string;
|
||||
originalReleaseDate: null | string;
|
||||
participants: null | Record<string, RelatedArtist[]>;
|
||||
playCount: null | number;
|
||||
releaseDate: null | string;
|
||||
releaseTypes: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
releaseYear: null | number;
|
||||
serverId: string;
|
||||
serverType: ServerType;
|
||||
size: null | number;
|
||||
songCount: null | number;
|
||||
songs?: Song[];
|
||||
tags: null | Record<string, string[]>;
|
||||
sortName: string;
|
||||
tags: Record<string, string[]>;
|
||||
uniqueId: string;
|
||||
updatedAt: string;
|
||||
updatedDate: string;
|
||||
userFavorite: boolean;
|
||||
userFavoriteDate: null | string;
|
||||
userLastPlayedDate: null | string;
|
||||
userPlayCount: null | number;
|
||||
userRating: null | number;
|
||||
} & { songs?: Song[] };
|
||||
|
||||
export type AlbumDetailArgs = BaseEndpointArgs & { query: AlbumDetailQuery };
|
||||
// Album Detail
|
||||
|
||||
export type AlbumDetailQuery = { id: string };
|
||||
|
||||
export type AlbumDetailResponse = Album | null | undefined;
|
||||
|
||||
export type AlbumInfo = {
|
||||
imageUrl: null | string;
|
||||
notes: null | string;
|
||||
};
|
||||
|
||||
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: ListSortOrder) => {
|
||||
let results = albums;
|
||||
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['albumArtist', (v) => v.name.toLowerCase()],
|
||||
[order, 'asc'],
|
||||
);
|
||||
break;
|
||||
case AlbumListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
case AlbumListSort.FAVORITED:
|
||||
results = orderBy(results, ['starred'], [order]);
|
||||
break;
|
||||
case AlbumListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
case AlbumListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
case AlbumListSort.RATING:
|
||||
results = orderBy(results, ['userRating'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['createdAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.RECENTLY_PLAYED:
|
||||
results = orderBy(results, ['lastPlayedAt'], [order]);
|
||||
break;
|
||||
case AlbumListSort.SONG_COUNT:
|
||||
results = orderBy(results, ['songCount'], [order]);
|
||||
break;
|
||||
case AlbumListSort.YEAR:
|
||||
results = orderBy(results, ['releaseYear'], [order]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
AlbumDetailArgs,
|
||||
AlbumDetailResponse,
|
||||
AlbumInfo,
|
||||
AlbumListArgs,
|
||||
AlbumListResponse,
|
||||
} from '/@/shared/types/domain/album-domain-types';
|
||||
import {
|
||||
AlbumArtistDetailArgs,
|
||||
AlbumArtistDetailResponse,
|
||||
AlbumArtistListArgs,
|
||||
AlbumArtistListResponse,
|
||||
ArtistListArgs,
|
||||
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 {
|
||||
LyricsArgs,
|
||||
LyricsResponse,
|
||||
StructuredLyric,
|
||||
StructuredLyricsArgs,
|
||||
} from '/@/shared/types/domain/lyric-domain-types';
|
||||
import { TranscodingArgs } from '/@/shared/types/domain/player-domain-types';
|
||||
import {
|
||||
AddToPlaylistArgs,
|
||||
AddToPlaylistResponse,
|
||||
CreatePlaylistArgs,
|
||||
CreatePlaylistResponse,
|
||||
DeletePlaylistArgs,
|
||||
DeletePlaylistResponse,
|
||||
MoveItemArgs,
|
||||
PlaylistDetailArgs,
|
||||
PlaylistDetailResponse,
|
||||
PlaylistListArgs,
|
||||
PlaylistListResponse,
|
||||
PlaylistSongListArgs,
|
||||
RemoveFromPlaylistArgs,
|
||||
RemoveFromPlaylistResponse,
|
||||
UpdatePlaylistArgs,
|
||||
UpdatePlaylistResponse,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { SearchArgs, SearchResponse } from '/@/shared/types/domain/search-domain-types';
|
||||
import {
|
||||
ServerInfo,
|
||||
ServerInfoArgs,
|
||||
ServerListItem,
|
||||
ServerMusicFolderListArgs,
|
||||
ServerMusicFolderListResponse,
|
||||
} from '/@/shared/types/domain/server-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import {
|
||||
RandomSongListArgs,
|
||||
SimilarSongsArgs,
|
||||
Song,
|
||||
SongDetailArgs,
|
||||
SongDetailResponse,
|
||||
SongListArgs,
|
||||
SongListResponse,
|
||||
TopSongListArgs,
|
||||
TopSongListResponse,
|
||||
} from '/@/shared/types/domain/song-domain-types';
|
||||
import { TagArgs, TagsResponse } from '/@/shared/types/domain/tag-domain-types';
|
||||
import {
|
||||
DownloadArgs,
|
||||
FavoriteArgs,
|
||||
FavoriteResponse,
|
||||
RatingResponse,
|
||||
ScrobbleArgs,
|
||||
ScrobbleResponse,
|
||||
SetRatingArgs,
|
||||
ShareItemArgs,
|
||||
ShareItemResponse,
|
||||
UserListArgs,
|
||||
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;
|
||||
};
|
||||
|
||||
export type ControllerEndpoint = {
|
||||
addToPlaylist: (args: AddToPlaylistArgs) => Promise<AddToPlaylistResponse>;
|
||||
authenticate: (
|
||||
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>;
|
||||
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>;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
import { orderBy } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFAlbumArtistListSort, JFArtistListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||
import { NDAlbumArtistListSort } from '/@/shared/api/navidrome.types';
|
||||
@@ -9,10 +10,10 @@ import {
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
LibraryItem,
|
||||
} from '/@/shared/types/domain-types';
|
||||
} from '/@/shared/types/domain/api-domain-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';
|
||||
|
||||
export enum ArtistListSortOptions {
|
||||
ALBUM_COUNT = 'albumCount',
|
||||
@@ -243,3 +244,35 @@ export type ArtistInfoQuery = {
|
||||
limit: number;
|
||||
musicFolderId?: string;
|
||||
};
|
||||
export const sortAlbumArtistList = (
|
||||
artists: AlbumArtist[],
|
||||
sortBy: AlbumArtistListSort | ArtistListSort,
|
||||
sortOrder: ListSortOrder,
|
||||
) => {
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
let results = artists;
|
||||
|
||||
switch (sortBy) {
|
||||
case AlbumArtistListSort.ALBUM_COUNT:
|
||||
results = orderBy(artists, ['albumCount', (v) => v.name.toLowerCase()], [order, 'asc']);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.FAVORITED:
|
||||
results = orderBy(artists, ['starred'], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.NAME:
|
||||
results = orderBy(artists, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case AlbumArtistListSort.RATING:
|
||||
results = orderBy(artists, ['userRating'], [order]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export type AuthenticationResponse = {
|
||||
credential: string;
|
||||
ndCredential?: string;
|
||||
userId: null | string;
|
||||
username: string;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
export enum ExternalSource {
|
||||
LASTFM = 'LASTFM',
|
||||
MUSICBRAINZ = 'MUSICBRAINZ',
|
||||
SPOTIFY = 'SPOTIFY',
|
||||
THEAUDIODB = 'THEAUDIODB',
|
||||
}
|
||||
|
||||
export enum ExternalType {
|
||||
ID = 'ID',
|
||||
LINK = 'LINK',
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
|
||||
import { UserListSort } from './user-domain-types';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFGenreListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { NDGenreListSort } from '/@/shared/api/navidrome.types';
|
||||
import {
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
LibraryItem,
|
||||
} from '/@/shared/types/domain-types';
|
||||
} from '/@/shared/types/domain/api-domain-types';
|
||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { UserListSort } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
export enum GenreListSortOptions {
|
||||
ALBUM_COUNT = 'albumCount',
|
||||
@@ -22,6 +20,7 @@ export const GenreListSortOptionsLabels = {
|
||||
[GenreListSortOptions.NAME]: i18n.t('filter.name'),
|
||||
[GenreListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||
};
|
||||
|
||||
export type Genre = {
|
||||
albumCount?: number;
|
||||
id: string;
|
||||
@@ -43,7 +42,6 @@ export interface GenreListQuery extends BaseQuery<GenreListSort> {
|
||||
searchTerm?: string;
|
||||
startIndex: number;
|
||||
}
|
||||
// Genre List
|
||||
|
||||
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum ImageType {
|
||||
BACKDROP = 'BACKDROP',
|
||||
LOGO = 'LOGO',
|
||||
PRIMARY = 'PRIMARY',
|
||||
SCREENSHOT = 'SCREENSHOT',
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
import { BaseEndpointArgs, LyricSource } from '/@/shared/types/domain-types';
|
||||
import { BaseEndpointArgs } from './api-domain-types';
|
||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export enum LyricSource {
|
||||
GENIUS = 'Genius',
|
||||
LRCLIB = 'lrclib.net',
|
||||
NETEASE = 'NetEase',
|
||||
}
|
||||
|
||||
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
|
||||
lyrics: LyricsResponse;
|
||||
remote: boolean;
|
||||
source: string;
|
||||
};
|
||||
|
||||
export type InternetProviderLyricResponse = {
|
||||
artist: string;
|
||||
id: string;
|
||||
@@ -21,6 +26,7 @@ export type InternetProviderLyricSearchResponse = {
|
||||
score?: number;
|
||||
source: LyricSource;
|
||||
};
|
||||
|
||||
export type LyricGetQuery = {
|
||||
remoteSongId: string;
|
||||
remoteSource: LyricSource;
|
||||
@@ -32,7 +38,6 @@ export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||
export type LyricsArgs = BaseEndpointArgs & {
|
||||
query: LyricsQuery;
|
||||
};
|
||||
|
||||
export type LyricSearchQuery = {
|
||||
album?: string;
|
||||
artist?: string;
|
||||
@@ -40,6 +45,7 @@ export type LyricSearchQuery = {
|
||||
name?: string;
|
||||
};
|
||||
export type LyricsOverride = Omit<FullLyricsMetadata, 'lyrics'> & { id: string };
|
||||
|
||||
export type LyricsQuery = {
|
||||
songId: string;
|
||||
};
|
||||
@@ -58,7 +64,6 @@ export type StructuredSyncedLyric = Omit<FullLyricsMetadata, 'lyrics'> & {
|
||||
lyrics: SynchronizedLyricsArray;
|
||||
synced: true;
|
||||
};
|
||||
|
||||
export type StructuredUnsyncedLyric = Omit<FullLyricsMetadata, 'lyrics'> & {
|
||||
lyrics: string;
|
||||
synced: false;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
|
||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||
import { PlayerStatus } from '/@/shared/types/types';
|
||||
|
||||
export enum Played {
|
||||
All = 'all',
|
||||
Never = 'never',
|
||||
Played = 'played',
|
||||
}
|
||||
|
||||
export interface PlayerData {
|
||||
current: {
|
||||
index: number;
|
||||
nextIndex?: number;
|
||||
player: 1 | 2;
|
||||
previousIndex?: number;
|
||||
shuffledIndex: number;
|
||||
song?: QueueSong;
|
||||
status: PlayerStatus;
|
||||
};
|
||||
player1?: QueueSong;
|
||||
player2?: QueueSong;
|
||||
queue: QueueData;
|
||||
}
|
||||
|
||||
export interface QueueData {
|
||||
current?: QueueSong;
|
||||
length: number;
|
||||
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;
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Genre } from './genre-domain-types';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFPlaylistListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||
import { NDPlaylistListSort } from '/@/shared/api/navidrome.types';
|
||||
@@ -11,10 +9,10 @@ import {
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
LibraryItem,
|
||||
} from '/@/shared/types/domain-types';
|
||||
} from '/@/shared/types/domain/api-domain-types';
|
||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Song, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export enum PlaylistListSortOptions {
|
||||
@@ -191,6 +189,17 @@ export const playlistListSortMap: PlaylistListSortMap = {
|
||||
updatedAt: undefined,
|
||||
},
|
||||
};
|
||||
export type MoveItemArgs = BaseEndpointArgs & {
|
||||
query: MoveItemQuery;
|
||||
};
|
||||
|
||||
export type MoveItemQuery = {
|
||||
endingIndex: number;
|
||||
playlistId: string;
|
||||
startingIndex: number;
|
||||
trackId: string;
|
||||
};
|
||||
|
||||
export type PlaylistDetailArgs = BaseEndpointArgs & { query: PlaylistDetailQuery };
|
||||
|
||||
export type PlaylistDetailQuery = {
|
||||
@@ -200,7 +209,6 @@ export type PlaylistDetailQuery = {
|
||||
export type PlaylistDetailResponse = Playlist;
|
||||
|
||||
export type PlaylistSongListArgs = BaseEndpointArgs & { query: PlaylistSongListQuery };
|
||||
|
||||
export type PlaylistSongListQuery = {
|
||||
id: string;
|
||||
limit?: number;
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
||||
import { PlayerRepeat, PlayerStatus, SongState } from '/@/shared/types/types';
|
||||
|
||||
export interface ClientAuth {
|
||||
event: 'authenticate';
|
||||
header: string;
|
||||
}
|
||||
|
||||
export type ClientEvent =
|
||||
| ClientAuth
|
||||
| ClientFavorite
|
||||
| ClientPosition
|
||||
| ClientRating
|
||||
| ClientSimpleEvent
|
||||
| ClientVolume;
|
||||
|
||||
export interface ClientFavorite {
|
||||
event: 'favorite';
|
||||
favorite: boolean;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface ClientPosition {
|
||||
event: 'position';
|
||||
position: number;
|
||||
}
|
||||
|
||||
export interface ClientRating {
|
||||
event: 'rating';
|
||||
id: string;
|
||||
rating: number;
|
||||
}
|
||||
export interface ClientSimpleEvent {
|
||||
event: 'next' | 'pause' | 'play' | 'previous' | 'proxy' | 'repeat' | 'shuffle';
|
||||
}
|
||||
|
||||
export interface ClientVolume {
|
||||
event: 'volume';
|
||||
volume: number;
|
||||
}
|
||||
|
||||
export interface ServerError {
|
||||
data: string;
|
||||
event: 'error';
|
||||
}
|
||||
|
||||
export type ServerEvent =
|
||||
| ServerError
|
||||
| ServerFavorite
|
||||
| ServerPlayStatus
|
||||
| ServerPosition
|
||||
| ServerProxy
|
||||
| ServerRating
|
||||
| ServerRepeat
|
||||
| ServerShuffle
|
||||
| ServerSong
|
||||
| ServerState
|
||||
| ServerVolume;
|
||||
|
||||
export interface ServerFavorite {
|
||||
data: { favorite: boolean; id: string };
|
||||
event: 'favorite';
|
||||
}
|
||||
|
||||
export interface ServerPlayStatus {
|
||||
data: PlayerStatus;
|
||||
event: 'playback';
|
||||
}
|
||||
|
||||
export interface ServerPosition {
|
||||
data: number;
|
||||
event: 'position';
|
||||
}
|
||||
|
||||
export interface ServerProxy {
|
||||
data: string;
|
||||
event: 'proxy';
|
||||
}
|
||||
|
||||
export interface ServerRating {
|
||||
data: { id: string; rating: number };
|
||||
event: 'rating';
|
||||
}
|
||||
|
||||
export interface ServerRepeat {
|
||||
data: PlayerRepeat;
|
||||
event: 'repeat';
|
||||
}
|
||||
|
||||
export interface ServerShuffle {
|
||||
data: boolean;
|
||||
event: 'shuffle';
|
||||
}
|
||||
|
||||
export interface ServerSong {
|
||||
data: null | QueueSong;
|
||||
event: 'song';
|
||||
}
|
||||
|
||||
export interface ServerState {
|
||||
data: SongState;
|
||||
event: 'state';
|
||||
}
|
||||
|
||||
export interface ServerVolume {
|
||||
data: number;
|
||||
event: 'volume';
|
||||
}
|
||||
|
||||
export interface SongUpdateSocket extends Omit<SongState, 'song'> {
|
||||
position?: number;
|
||||
song?: null | QueueSong;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseEndpointArgs } from '/@/shared/types/domain-types';
|
||||
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';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
|
||||
import { BaseEndpointArgs, BasePaginatedResponse } from '/@/shared/types/domain-types';
|
||||
import { ServerFeatures } from '/@/shared/types/features-types';
|
||||
import { BaseEndpointArgs, BasePaginatedResponse } from '/@/shared/types/domain/api-domain-types';
|
||||
|
||||
export enum ServerListSortOptions {
|
||||
CREATED_AT = 'createdAt',
|
||||
@@ -17,12 +16,24 @@ export const ServerListSortOptionsLabels = {
|
||||
[ServerListSortOptions.UPDATED_AT]: i18n.t('filter.updatedAt'),
|
||||
};
|
||||
|
||||
export enum ServerFeature {
|
||||
BFR = 'bfr',
|
||||
LYRICS_MULTIPLE_STRUCTURED = 'lyricsMultipleStructured',
|
||||
LYRICS_SINGLE_STRUCTURED = 'lyricsSingleStructured',
|
||||
PLAYLISTS_SMART = 'playlistsSmart',
|
||||
PUBLIC_PLAYLIST = 'publicPlaylist',
|
||||
SHARING_ALBUM_SONG = 'sharingAlbumSong',
|
||||
TAGS = 'tags',
|
||||
}
|
||||
|
||||
export enum ServerType {
|
||||
JELLYFIN = 'jellyfin',
|
||||
NAVIDROME = 'navidrome',
|
||||
SUBSONIC = 'subsonic',
|
||||
}
|
||||
|
||||
export type ServerFeatures = Partial<Record<ServerFeature, number[]>>;
|
||||
|
||||
export type ServerInfo = {
|
||||
features: ServerFeatures;
|
||||
id?: string;
|
||||
@@ -53,8 +64,10 @@ export type ServerMusicFolder = {
|
||||
export type ServerMusicFolderListArgs = BaseEndpointArgs;
|
||||
|
||||
export type ServerMusicFolderListQuery = null;
|
||||
|
||||
export type ServerMusicFolderListResponse =
|
||||
| BasePaginatedResponse<ServerMusicFolder[]>
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
export type ServerMusicFoldersResponse = ServerMusicFolder[];
|
||||
|
||||
@@ -1,4 +1,58 @@
|
||||
import { JFSortOrder } from '/@/shared/api/jellyfin.types';
|
||||
import { NDSortOrder } from '/@/shared/api/navidrome.types';
|
||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||
import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export enum LibraryItem {
|
||||
ALBUM = 'album',
|
||||
ALBUM_ARTIST = 'albumArtist',
|
||||
ARTIST = 'artist',
|
||||
GENRE = 'genre',
|
||||
PLAYLIST = 'playlist',
|
||||
SONG = 'song',
|
||||
}
|
||||
export enum ListSortOrder {
|
||||
ASC = 'ASC',
|
||||
DESC = 'DESC',
|
||||
}
|
||||
|
||||
export type AnyLibraryItem = Album | AlbumArtist | Artist | Playlist | QueueSong | Song;
|
||||
|
||||
export type AnyLibraryItems =
|
||||
| Album[]
|
||||
| AlbumArtist[]
|
||||
| Artist[]
|
||||
| Playlist[]
|
||||
| QueueSong[]
|
||||
| Song[];
|
||||
type SortOrderMap = {
|
||||
jellyfin: Record<ListSortOrder, JFSortOrder>;
|
||||
navidrome: Record<ListSortOrder, NDSortOrder>;
|
||||
subsonic: Record<ListSortOrder, undefined>;
|
||||
};
|
||||
|
||||
export const sortOrderMap: SortOrderMap = {
|
||||
jellyfin: {
|
||||
ASC: JFSortOrder.ASC,
|
||||
DESC: JFSortOrder.DESC,
|
||||
},
|
||||
navidrome: {
|
||||
ASC: NDSortOrder.ASC,
|
||||
DESC: NDSortOrder.DESC,
|
||||
},
|
||||
subsonic: {
|
||||
ASC: undefined,
|
||||
DESC: undefined,
|
||||
},
|
||||
}; // This type from https://wicg.github.io/local-font-access/#fontdata
|
||||
// NOTE: it is still experimental, so this should be updates as appropriate
|
||||
|
||||
export type FontData = {
|
||||
family: string;
|
||||
fullName: string;
|
||||
postscriptName: string;
|
||||
style: string;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
import { orderBy, reverse, shuffle } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFSongListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||
import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
||||
@@ -9,13 +10,12 @@ import {
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
GainInfo,
|
||||
LibraryItem,
|
||||
Played,
|
||||
} from '/@/shared/types/domain-types';
|
||||
} from '/@/shared/types/domain/api-domain-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';
|
||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
export enum SongListSortOptions {
|
||||
ALBUM = 'album',
|
||||
@@ -212,6 +212,11 @@ export const songListSortMap: SongListSortMap = {
|
||||
year: undefined,
|
||||
},
|
||||
};
|
||||
export type GainInfo = {
|
||||
album?: number;
|
||||
track?: number;
|
||||
};
|
||||
|
||||
export type RandomSongListArgs = BaseEndpointArgs & {
|
||||
query: RandomSongListQuery;
|
||||
};
|
||||
@@ -224,8 +229,8 @@ export type RandomSongListQuery = {
|
||||
musicFolderId?: string;
|
||||
played: Played;
|
||||
};
|
||||
|
||||
export type RandomSongListResponse = SongListResponse;
|
||||
|
||||
export type SimilarSongsArgs = BaseEndpointArgs & {
|
||||
query: SimilarSongsQuery;
|
||||
};
|
||||
@@ -235,11 +240,11 @@ export type SimilarSongsQuery = {
|
||||
count?: number;
|
||||
songId: string;
|
||||
};
|
||||
|
||||
export type SongDetailArgs = BaseEndpointArgs & { query: SongDetailQuery };
|
||||
export type SongDetailQuery = { id: string };
|
||||
|
||||
export type SongDetailQuery = { id: string };
|
||||
export type SongDetailResponse = null | Song | undefined;
|
||||
|
||||
export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery };
|
||||
|
||||
export type TopSongListQuery = {
|
||||
@@ -247,5 +252,99 @@ export type TopSongListQuery = {
|
||||
artistId: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export type TopSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||
export const sortSongList = (
|
||||
songs: QueueSong[],
|
||||
sortBy: SongListSort,
|
||||
sortOrder: ListSortOrder,
|
||||
) => {
|
||||
let results = songs;
|
||||
|
||||
const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc';
|
||||
|
||||
switch (sortBy) {
|
||||
case SongListSort.ALBUM:
|
||||
results = orderBy(
|
||||
results,
|
||||
[(v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ALBUM_ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['albumArtist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ARTIST:
|
||||
results = orderBy(
|
||||
results,
|
||||
['artist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.DURATION:
|
||||
results = orderBy(results, ['duration'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.FAVORITED:
|
||||
results = orderBy(results, ['userFavorite', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.GENRE:
|
||||
results = orderBy(
|
||||
results,
|
||||
[
|
||||
(v) => v.genres?.[0].name.toLowerCase(),
|
||||
(v) => v.album?.toLowerCase(),
|
||||
'discNumber',
|
||||
'trackNumber',
|
||||
],
|
||||
[order, order, 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
case SongListSort.ID:
|
||||
if (order === 'desc') {
|
||||
results = reverse(results as any);
|
||||
}
|
||||
break;
|
||||
|
||||
case SongListSort.NAME:
|
||||
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.PLAY_COUNT:
|
||||
results = orderBy(results, ['playCount'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RANDOM:
|
||||
results = shuffle(results);
|
||||
break;
|
||||
|
||||
case SongListSort.RATING:
|
||||
results = orderBy(results, ['userRating', (v) => v.name.toLowerCase()], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.RECENTLY_ADDED:
|
||||
results = orderBy(results, ['created'], [order]);
|
||||
break;
|
||||
|
||||
case SongListSort.YEAR:
|
||||
results = orderBy(
|
||||
results,
|
||||
['year', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
||||
[order, 'asc', 'asc', 'asc'],
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { BaseEndpointArgs } from '/@/shared/types/domain/api-domain-types';
|
||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
export type Tag = {
|
||||
name: string;
|
||||
options: string[];
|
||||
};
|
||||
|
||||
export type TagArgs = BaseEndpointArgs & {
|
||||
query: TagQuery;
|
||||
};
|
||||
|
||||
export type TagQuery = {
|
||||
folder?: string;
|
||||
type: LibraryItem.ALBUM | LibraryItem.SONG;
|
||||
};
|
||||
|
||||
export type TagsResponse = {
|
||||
boolTags?: string[];
|
||||
enumTags?: Tag[];
|
||||
};
|
||||
@@ -1,13 +1,11 @@
|
||||
import i18n from 'src/i18n/i18n';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { NDUserListSort } from '/@/shared/api/navidrome.types';
|
||||
import {
|
||||
AnyLibraryItems,
|
||||
BaseEndpointArgs,
|
||||
BasePaginatedResponse,
|
||||
BaseQuery,
|
||||
LibraryItem,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { RatingQuery } from '/@/shared/types/domain/user-domain-types';
|
||||
import { NDUserListSort } from '/@/shared/api/navidrome.types';
|
||||
} from '/@/shared/types/domain/api-domain-types';
|
||||
import { AnyLibraryItems, LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
export enum UserListSortOptions {
|
||||
CREATED_AT = 'createdAt',
|
||||
@@ -22,6 +20,7 @@ export const UserListSortOptionsLabels = {
|
||||
[UserListSortOptions.NAME]: i18n.t('filter.name'),
|
||||
[UserListSortOptions.UPDATED_AT]: i18n.t('filter.updatedAt'),
|
||||
};
|
||||
|
||||
export type FavoriteArgs = BaseEndpointArgs & { query: FavoriteQuery; serverId?: string };
|
||||
|
||||
export type FavoriteQuery = {
|
||||
@@ -36,7 +35,9 @@ export type RatingQuery = {
|
||||
};
|
||||
|
||||
export type RatingResponse = null | undefined;
|
||||
|
||||
export type SetRatingArgs = BaseEndpointArgs & { query: RatingQuery; serverId?: string };
|
||||
|
||||
export type UserListArgs = BaseEndpointArgs & { query: UserListQuery };
|
||||
|
||||
export interface UserListQuery extends BaseQuery<UserListSort> {
|
||||
@@ -51,6 +52,7 @@ export interface UserListQuery extends BaseQuery<UserListSort> {
|
||||
}
|
||||
|
||||
export type UserListResponse = BasePaginatedResponse<User[]> | null | undefined;
|
||||
|
||||
type UserListSortMap = {
|
||||
jellyfin: Record<UserListSort, undefined>;
|
||||
navidrome: Record<UserListSort, NDUserListSort | undefined>;
|
||||
@@ -68,6 +70,19 @@ export const userListSortMap: UserListSortMap = {
|
||||
name: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export enum UserListSort {
|
||||
NAME = 'name',
|
||||
}
|
||||
|
||||
export type DownloadArgs = BaseEndpointArgs & {
|
||||
query: DownloadQuery;
|
||||
};
|
||||
|
||||
export type DownloadQuery = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type ScrobbleArgs = BaseEndpointArgs & {
|
||||
query: ScrobbleQuery;
|
||||
serverId?: string;
|
||||
@@ -81,9 +96,19 @@ export type ScrobbleQuery = {
|
||||
};
|
||||
|
||||
export type ScrobbleResponse = null | undefined;
|
||||
export enum UserListSort {
|
||||
NAME = 'name',
|
||||
}
|
||||
|
||||
export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody; serverId?: string };
|
||||
|
||||
export type ShareItemBody = {
|
||||
description: string;
|
||||
downloadable: boolean;
|
||||
expires: number;
|
||||
resourceIds: string;
|
||||
resourceType: string;
|
||||
};
|
||||
|
||||
export type ShareItemResponse = undefined | { id: string };
|
||||
|
||||
export type User = {
|
||||
createdAt: null | string;
|
||||
email: null | string;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Should follow a strict naming convention: "<FEATURE GROUP>_<FEATURE NAME>"
|
||||
// For example: <FEATURE GROUP>: "Playlists", <FEATURE NAME>: "Smart" = "PLAYLISTS_SMART"
|
||||
export enum ServerFeature {
|
||||
BFR = 'bfr',
|
||||
LYRICS_MULTIPLE_STRUCTURED = 'lyricsMultipleStructured',
|
||||
LYRICS_SINGLE_STRUCTURED = 'lyricsSingleStructured',
|
||||
PLAYLISTS_SMART = 'playlistsSmart',
|
||||
PUBLIC_PLAYLIST = 'publicPlaylist',
|
||||
SHARING_ALBUM_SONG = 'sharingAlbumSong',
|
||||
TAGS = 'tags',
|
||||
}
|
||||
|
||||
export type ServerFeatures = Partial<Record<ServerFeature, number[]>>;
|
||||
Reference in New Issue
Block a user