mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-24 21:07:41 +02:00
begin reorganizing domain types
This commit is contained in:
@@ -227,7 +227,11 @@
|
|||||||
"songCount": "song count",
|
"songCount": "song count",
|
||||||
"title": "title",
|
"title": "title",
|
||||||
"toYear": "to year",
|
"toYear": "to year",
|
||||||
"trackNumber": "track"
|
"trackNumber": "track",
|
||||||
|
"createdAt": "created at",
|
||||||
|
"updatedAt": "updated at",
|
||||||
|
"type": "type",
|
||||||
|
"email": "email"
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"addServer": {
|
"addServer": {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
getSearchResults as searchNetease,
|
getSearchResults as searchNetease,
|
||||||
} from './netease';
|
} from './netease';
|
||||||
|
|
||||||
import { Song } from '/@/shared/types/domain-types';
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
export enum LyricSource {
|
export enum LyricSource {
|
||||||
GENIUS = 'Genius',
|
GENIUS = 'Genius',
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
|
|
||||||
import {
|
import { InternetProviderLyricSearchResponse } from '/@/shared/types/domain/lyric-domain-types';
|
||||||
InternetProviderLyricSearchResponse,
|
import { LyricSearchQuery } from '/@/shared/types/domain/lyric-domain-types';
|
||||||
LyricSearchQuery,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
|
|
||||||
export const orderSearchResults = (args: {
|
export const orderSearchResults = (args: {
|
||||||
params: LyricSearchQuery;
|
params: LyricSearchQuery;
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { JFAlbum, JFGenre, JFMusicFolder, JFPlaylist } from '/@/shared/api/jellyfin.types';
|
import { JFAlbum, JFGenre, JFMusicFolder, JFPlaylist } from '/@/shared/api/jellyfin.types';
|
||||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||||
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
|
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import {
|
import {
|
||||||
Album,
|
ServerListItem,
|
||||||
AlbumArtist,
|
ServerMusicFolder,
|
||||||
Genre,
|
ServerType,
|
||||||
LibraryItem,
|
} from '/@/shared/types/domain/server-domain-types';
|
||||||
MusicFolder,
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
Playlist,
|
|
||||||
RelatedArtist,
|
|
||||||
Song,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/types';
|
|
||||||
|
|
||||||
const getStreamUrl = (args: {
|
const getStreamUrl = (args: {
|
||||||
container?: string;
|
container?: string;
|
||||||
@@ -432,7 +432,7 @@ const normalizePlaylist = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeMusicFolder = (item: JFMusicFolder): MusicFolder => {
|
const normalizeMusicFolder = (item: JFMusicFolder): ServerMusicFolder => {
|
||||||
return {
|
return {
|
||||||
id: item.Id,
|
id: item.Id,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ import z from 'zod';
|
|||||||
import { NDGenre } from '/@/shared/api/navidrome.types';
|
import { NDGenre } from '/@/shared/api/navidrome.types';
|
||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import {
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
Album,
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
AlbumArtist,
|
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
Genre,
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
LibraryItem,
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
Playlist,
|
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
RelatedArtist,
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
Song,
|
import { User } from '/@/shared/types/domain/user-domain-types';
|
||||||
User,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/types';
|
|
||||||
|
|
||||||
const getImageUrl = (args: { url: null | string }) => {
|
const getImageUrl = (args: { url: null | string }) => {
|
||||||
const { url } = args;
|
const { url } = args;
|
||||||
|
|||||||
@@ -2,17 +2,12 @@ import { nanoid } from 'nanoid';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import {
|
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
Album,
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
AlbumArtist,
|
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
Genre,
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
LibraryItem,
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
Playlist,
|
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
QueueSong,
|
|
||||||
RelatedArtist,
|
|
||||||
ServerListItem,
|
|
||||||
ServerType,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
|
|
||||||
const getCoverArtUrl = (args: {
|
const getCoverArtUrl = (args: {
|
||||||
baseUrl: string | undefined;
|
baseUrl: string | undefined;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import semverCoerce from 'semver/functions/coerce';
|
|||||||
import semverGte from 'semver/functions/gte';
|
import semverGte from 'semver/functions/gte';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { ServerListItem } from '/@/shared/types/domain-types';
|
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { ServerFeature } from '/@/shared/types/features-types';
|
import { ServerFeature } from '/@/shared/types/features-types';
|
||||||
|
|
||||||
// Since ts-rest client returns a strict response type, we need to add the headers to the body object
|
// Since ts-rest client returns a strict response type, we need to add the headers to the body object
|
||||||
|
|||||||
+104
-1096
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
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,
|
||||||
|
LibraryItem,
|
||||||
|
} from '/@/shared/types/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 { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
|
export enum AlbumListSortOptions {
|
||||||
|
ALBUM_ARTIST = 'albumArtist',
|
||||||
|
ARTIST = 'artist',
|
||||||
|
COMMUNITY_RATING = 'communityRating',
|
||||||
|
CRITIC_RATING = 'criticRating',
|
||||||
|
DATE_ADDED = 'dateAdded',
|
||||||
|
DATE_PLAYED = 'datePlayed',
|
||||||
|
DURATION = 'duration',
|
||||||
|
IS_FAVORITE = 'isFavorite',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
TRACK_COUNT = 'trackCount',
|
||||||
|
YEAR = 'year',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AlbumListSortOptionsLabels = {
|
||||||
|
[AlbumListSortOptions.ALBUM_ARTIST]: i18n.t('filter.albumArtist'),
|
||||||
|
[AlbumListSortOptions.ARTIST]: i18n.t('filter.artist'),
|
||||||
|
[AlbumListSortOptions.COMMUNITY_RATING]: i18n.t('filter.communityRating'),
|
||||||
|
[AlbumListSortOptions.CRITIC_RATING]: i18n.t('filter.criticRating'),
|
||||||
|
[AlbumListSortOptions.DATE_ADDED]: i18n.t('filter.dateAdded'),
|
||||||
|
[AlbumListSortOptions.DATE_PLAYED]: i18n.t('filter.datePlayed'),
|
||||||
|
[AlbumListSortOptions.DURATION]: i18n.t('filter.duration'),
|
||||||
|
[AlbumListSortOptions.IS_FAVORITE]: i18n.t('filter.isFavorite'),
|
||||||
|
[AlbumListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[AlbumListSortOptions.PLAY_COUNT]: i18n.t('filter.playCount'),
|
||||||
|
[AlbumListSortOptions.RANDOM]: i18n.t('filter.random'),
|
||||||
|
[AlbumListSortOptions.RATING]: i18n.t('filter.rating'),
|
||||||
|
[AlbumListSortOptions.RELEASE_DATE]: i18n.t('filter.releaseDate'),
|
||||||
|
[AlbumListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||||
|
[AlbumListSortOptions.YEAR]: i18n.t('filter.year'),
|
||||||
|
};
|
||||||
|
export enum AlbumListSort {
|
||||||
|
ALBUM_ARTIST = 'albumArtist',
|
||||||
|
ARTIST = 'artist',
|
||||||
|
COMMUNITY_RATING = 'communityRating',
|
||||||
|
CRITIC_RATING = 'criticRating',
|
||||||
|
DURATION = 'duration',
|
||||||
|
FAVORITED = 'favorited',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
|
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
SONG_COUNT = 'songCount',
|
||||||
|
YEAR = 'year',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AlbumListArgs = BaseEndpointArgs & { query: AlbumListQuery };
|
||||||
|
|
||||||
|
export interface AlbumListQuery extends BaseQuery<AlbumListSort> {
|
||||||
|
_custom?: {
|
||||||
|
jellyfin?: Partial<z.infer<typeof jfType._parameters.albumList>>;
|
||||||
|
navidrome?: Partial<z.infer<typeof ndType._parameters.albumList>>;
|
||||||
|
};
|
||||||
|
artistIds?: string[];
|
||||||
|
compilation?: boolean;
|
||||||
|
favorite?: boolean;
|
||||||
|
genres?: string[];
|
||||||
|
limit?: number;
|
||||||
|
maxYear?: number;
|
||||||
|
minYear?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
// Album List
|
||||||
|
|
||||||
|
export type AlbumListResponse = BasePaginatedResponse<Album[]> | null | undefined;
|
||||||
|
type AlbumListSortMap = {
|
||||||
|
jellyfin: Record<AlbumListSort, JFAlbumListSort | undefined>;
|
||||||
|
navidrome: Record<AlbumListSort, NDAlbumListSort | undefined>;
|
||||||
|
subsonic: Record<AlbumListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const albumListSortMap: AlbumListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
albumArtist: JFAlbumListSort.ALBUM_ARTIST,
|
||||||
|
artist: undefined,
|
||||||
|
communityRating: JFAlbumListSort.COMMUNITY_RATING,
|
||||||
|
criticRating: JFAlbumListSort.CRITIC_RATING,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
name: JFAlbumListSort.NAME,
|
||||||
|
playCount: JFAlbumListSort.PLAY_COUNT,
|
||||||
|
random: JFAlbumListSort.RANDOM,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: JFAlbumListSort.RECENTLY_ADDED,
|
||||||
|
recentlyPlayed: undefined,
|
||||||
|
releaseDate: JFAlbumListSort.RELEASE_DATE,
|
||||||
|
songCount: undefined,
|
||||||
|
year: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
albumArtist: NDAlbumListSort.ALBUM_ARTIST,
|
||||||
|
artist: NDAlbumListSort.ARTIST,
|
||||||
|
communityRating: undefined,
|
||||||
|
criticRating: undefined,
|
||||||
|
duration: NDAlbumListSort.DURATION,
|
||||||
|
favorited: NDAlbumListSort.STARRED,
|
||||||
|
name: NDAlbumListSort.NAME,
|
||||||
|
playCount: NDAlbumListSort.PLAY_COUNT,
|
||||||
|
random: NDAlbumListSort.RANDOM,
|
||||||
|
rating: NDAlbumListSort.RATING,
|
||||||
|
recentlyAdded: NDAlbumListSort.RECENTLY_ADDED,
|
||||||
|
recentlyPlayed: NDAlbumListSort.PLAY_DATE,
|
||||||
|
// Recent versions of Navidrome support release date, but fallback to year for now
|
||||||
|
releaseDate: NDAlbumListSort.YEAR,
|
||||||
|
songCount: NDAlbumListSort.SONG_COUNT,
|
||||||
|
year: NDAlbumListSort.YEAR,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
albumArtist: undefined,
|
||||||
|
artist: undefined,
|
||||||
|
communityRating: undefined,
|
||||||
|
criticRating: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
name: undefined,
|
||||||
|
playCount: undefined,
|
||||||
|
random: undefined,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
recentlyPlayed: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
year: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export type Album = {
|
||||||
|
albumArtist: string;
|
||||||
|
albumArtists: RelatedArtist[];
|
||||||
|
artists: RelatedArtist[];
|
||||||
|
backdropImageUrl: null | string;
|
||||||
|
comment: null | string;
|
||||||
|
createdAt: string;
|
||||||
|
duration: null | number;
|
||||||
|
genres: Genre[];
|
||||||
|
id: string;
|
||||||
|
imagePlaceholderUrl: null | string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
isCompilation: boolean | null;
|
||||||
|
itemType: LibraryItem.ALBUM;
|
||||||
|
lastPlayedAt: null | string;
|
||||||
|
mbzId: null | string;
|
||||||
|
name: string;
|
||||||
|
originalDate: null | string;
|
||||||
|
participants: null | Record<string, RelatedArtist[]>;
|
||||||
|
playCount: null | number;
|
||||||
|
releaseDate: null | string;
|
||||||
|
releaseYear: null | number;
|
||||||
|
serverId: string;
|
||||||
|
serverType: ServerType;
|
||||||
|
size: null | number;
|
||||||
|
songCount: null | number;
|
||||||
|
songs?: Song[];
|
||||||
|
tags: null | Record<string, string[]>;
|
||||||
|
uniqueId: string;
|
||||||
|
updatedAt: string;
|
||||||
|
userFavorite: boolean;
|
||||||
|
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;
|
||||||
|
};
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { JFAlbumArtistListSort, JFArtistListSort } from '/@/shared/api/jellyfin.types';
|
||||||
|
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,
|
||||||
|
LibraryItem,
|
||||||
|
} from '/@/shared/types/domain-types';
|
||||||
|
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
|
|
||||||
|
export enum ArtistListSortOptions {
|
||||||
|
ALBUM_COUNT = 'albumCount',
|
||||||
|
DURATION = 'duration',
|
||||||
|
IS_FAVORITE = 'isFavorite',
|
||||||
|
NAME = 'name',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
TRACK_COUNT = 'trackCount',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ArtistListSortOptionsLabels = {
|
||||||
|
[ArtistListSortOptions.ALBUM_COUNT]: i18n.t('filter.albumCount'),
|
||||||
|
[ArtistListSortOptions.DURATION]: i18n.t('filter.duration'),
|
||||||
|
[ArtistListSortOptions.IS_FAVORITE]: i18n.t('filter.isFavorite'),
|
||||||
|
[ArtistListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[ArtistListSortOptions.RANDOM]: i18n.t('filter.random'),
|
||||||
|
[ArtistListSortOptions.RATING]: i18n.t('filter.rating'),
|
||||||
|
[ArtistListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AlbumArtist = {
|
||||||
|
albumCount: null | number;
|
||||||
|
backgroundImageUrl: null | string;
|
||||||
|
biography: null | string;
|
||||||
|
duration: null | number;
|
||||||
|
genres: Genre[];
|
||||||
|
id: string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
itemType: LibraryItem.ALBUM_ARTIST;
|
||||||
|
lastPlayedAt: null | string;
|
||||||
|
mbz: null | string;
|
||||||
|
name: string;
|
||||||
|
playCount: null | number;
|
||||||
|
serverId: string;
|
||||||
|
serverType: ServerType;
|
||||||
|
similarArtists: null | RelatedArtist[];
|
||||||
|
songCount: null | number;
|
||||||
|
userFavorite: boolean;
|
||||||
|
userRating: null | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Artist = {
|
||||||
|
biography: null | string;
|
||||||
|
createdAt: string;
|
||||||
|
id: string;
|
||||||
|
itemType: LibraryItem.ARTIST;
|
||||||
|
name: string;
|
||||||
|
remoteCreatedAt: null | string;
|
||||||
|
serverFolderId: string;
|
||||||
|
serverId: string;
|
||||||
|
serverType: ServerType;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RelatedAlbumArtist = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RelatedArtist = {
|
||||||
|
id: string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AlbumArtistListSortMap = {
|
||||||
|
jellyfin: Record<AlbumArtistListSort, JFAlbumArtistListSort | undefined>;
|
||||||
|
navidrome: Record<AlbumArtistListSort, NDAlbumArtistListSort | undefined>;
|
||||||
|
subsonic: Record<AlbumArtistListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const albumArtistListSortMap: AlbumArtistListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
album: JFAlbumArtistListSort.ALBUM,
|
||||||
|
albumCount: undefined,
|
||||||
|
duration: JFAlbumArtistListSort.DURATION,
|
||||||
|
favorited: undefined,
|
||||||
|
name: JFAlbumArtistListSort.NAME,
|
||||||
|
playCount: undefined,
|
||||||
|
random: JFAlbumArtistListSort.RANDOM,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: JFAlbumArtistListSort.RECENTLY_ADDED,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
album: undefined,
|
||||||
|
albumCount: NDAlbumArtistListSort.ALBUM_COUNT,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: NDAlbumArtistListSort.FAVORITED,
|
||||||
|
name: NDAlbumArtistListSort.NAME,
|
||||||
|
playCount: NDAlbumArtistListSort.PLAY_COUNT,
|
||||||
|
random: undefined,
|
||||||
|
rating: NDAlbumArtistListSort.RATING,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: NDAlbumArtistListSort.SONG_COUNT,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
album: undefined,
|
||||||
|
albumCount: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
name: undefined,
|
||||||
|
playCount: undefined,
|
||||||
|
random: undefined,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ArtistListSort {
|
||||||
|
ALBUM = 'album',
|
||||||
|
ALBUM_COUNT = 'albumCount',
|
||||||
|
DURATION = 'duration',
|
||||||
|
FAVORITED = 'favorited',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
SONG_COUNT = 'songCount',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AlbumArtistDetailArgs = BaseEndpointArgs & { query: AlbumArtistDetailQuery };
|
||||||
|
|
||||||
|
export type AlbumArtistDetailQuery = { id: string };
|
||||||
|
|
||||||
|
export type AlbumArtistDetailResponse = AlbumArtist | null;
|
||||||
|
|
||||||
|
export type ArtistListArgs = BaseEndpointArgs & { query: ArtistListQuery };
|
||||||
|
|
||||||
|
export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
|
||||||
|
_custom?: {
|
||||||
|
jellyfin?: Partial<z.infer<typeof jfType._parameters.albumArtistList>>;
|
||||||
|
navidrome?: Partial<z.infer<typeof ndType._parameters.albumArtistList>>;
|
||||||
|
};
|
||||||
|
limit?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
role?: string;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
|
||||||
|
type ArtistListSortMap = {
|
||||||
|
jellyfin: Record<ArtistListSort, JFArtistListSort | undefined>;
|
||||||
|
navidrome: Record<ArtistListSort, undefined>;
|
||||||
|
subsonic: Record<ArtistListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const artistListSortMap: ArtistListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
album: JFArtistListSort.ALBUM,
|
||||||
|
albumCount: undefined,
|
||||||
|
duration: JFArtistListSort.DURATION,
|
||||||
|
favorited: undefined,
|
||||||
|
name: JFArtistListSort.NAME,
|
||||||
|
playCount: undefined,
|
||||||
|
random: JFArtistListSort.RANDOM,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: JFArtistListSort.RECENTLY_ADDED,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
album: undefined,
|
||||||
|
albumCount: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
name: undefined,
|
||||||
|
playCount: undefined,
|
||||||
|
random: undefined,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
album: undefined,
|
||||||
|
albumCount: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
name: undefined,
|
||||||
|
playCount: undefined,
|
||||||
|
random: undefined,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export enum AlbumArtistListSort {
|
||||||
|
ALBUM = 'album',
|
||||||
|
ALBUM_COUNT = 'albumCount',
|
||||||
|
DURATION = 'duration',
|
||||||
|
FAVORITED = 'favorited',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
SONG_COUNT = 'songCount',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AlbumArtistListArgs = BaseEndpointArgs & { query: AlbumArtistListQuery };
|
||||||
|
|
||||||
|
export interface AlbumArtistListQuery extends BaseQuery<AlbumArtistListSort> {
|
||||||
|
_custom?: {
|
||||||
|
jellyfin?: Partial<z.infer<typeof jfType._parameters.albumArtistList>>;
|
||||||
|
navidrome?: Partial<z.infer<typeof ndType._parameters.albumArtistList>>;
|
||||||
|
};
|
||||||
|
limit?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
export type AlbumArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
|
||||||
|
export type ArtistInfoArgs = BaseEndpointArgs & { query: ArtistInfoQuery };
|
||||||
|
|
||||||
|
export type ArtistInfoQuery = {
|
||||||
|
artistId: string;
|
||||||
|
limit: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
|
||||||
|
import { UserListSort } from './user-domain-types';
|
||||||
|
|
||||||
|
import { JFGenreListSort } from '/@/shared/api/jellyfin.types';
|
||||||
|
import { NDGenreListSort } from '/@/shared/api/navidrome.types';
|
||||||
|
import {
|
||||||
|
BaseEndpointArgs,
|
||||||
|
BasePaginatedResponse,
|
||||||
|
BaseQuery,
|
||||||
|
LibraryItem,
|
||||||
|
} from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
|
export enum GenreListSortOptions {
|
||||||
|
ALBUM_COUNT = 'albumCount',
|
||||||
|
NAME = 'name',
|
||||||
|
TRACK_COUNT = 'trackCount',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GenreListSortOptionsLabels = {
|
||||||
|
[GenreListSortOptions.ALBUM_COUNT]: i18n.t('filter.albumCount'),
|
||||||
|
[GenreListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[GenreListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||||
|
};
|
||||||
|
export type Genre = {
|
||||||
|
albumCount?: number;
|
||||||
|
id: string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
itemType: LibraryItem.GENRE;
|
||||||
|
name: string;
|
||||||
|
songCount?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GenreListArgs = BaseEndpointArgs & { query: GenreListQuery };
|
||||||
|
|
||||||
|
export interface GenreListQuery extends BaseQuery<GenreListSort> {
|
||||||
|
_custom?: {
|
||||||
|
jellyfin?: null;
|
||||||
|
navidrome?: null;
|
||||||
|
};
|
||||||
|
limit?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
// Genre List
|
||||||
|
|
||||||
|
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
|
||||||
|
|
||||||
|
export type GenresResponse = Genre[];
|
||||||
|
type GenreListSortMap = {
|
||||||
|
jellyfin: Record<GenreListSort, JFGenreListSort | undefined>;
|
||||||
|
navidrome: Record<GenreListSort, NDGenreListSort | undefined>;
|
||||||
|
subsonic: Record<UserListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genreListSortMap: GenreListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
name: JFGenreListSort.NAME,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
name: NDGenreListSort.NAME,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
name: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export enum GenreListSort {
|
||||||
|
NAME = 'name',
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { BaseEndpointArgs, LyricSource } from '/@/shared/types/domain-types';
|
||||||
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
|
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
|
||||||
|
lyrics: LyricsResponse;
|
||||||
|
remote: boolean;
|
||||||
|
source: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InternetProviderLyricResponse = {
|
||||||
|
artist: string;
|
||||||
|
id: string;
|
||||||
|
lyrics: string;
|
||||||
|
name: string;
|
||||||
|
source: LyricSource;
|
||||||
|
};
|
||||||
|
export type InternetProviderLyricSearchResponse = {
|
||||||
|
artist: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
score?: number;
|
||||||
|
source: LyricSource;
|
||||||
|
};
|
||||||
|
export type LyricGetQuery = {
|
||||||
|
remoteSongId: string;
|
||||||
|
remoteSource: LyricSource;
|
||||||
|
song: Song;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
|
export type LyricsArgs = BaseEndpointArgs & {
|
||||||
|
query: LyricsQuery;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LyricSearchQuery = {
|
||||||
|
album?: string;
|
||||||
|
artist?: string;
|
||||||
|
duration?: number;
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
export type LyricsOverride = Omit<FullLyricsMetadata, 'lyrics'> & { id: string };
|
||||||
|
export type LyricsQuery = {
|
||||||
|
songId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LyricsResponse = string | SynchronizedLyricsArray;
|
||||||
|
|
||||||
|
export type StructuredLyric = (StructuredSyncedLyric | StructuredUnsyncedLyric) & {
|
||||||
|
lang: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StructuredLyricsArgs = BaseEndpointArgs & {
|
||||||
|
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]>;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export interface AuthUserPermissions {
|
||||||
|
'jukebox.manage': boolean;
|
||||||
|
'media.download': boolean;
|
||||||
|
'media.share': boolean;
|
||||||
|
'media.stream': boolean;
|
||||||
|
'media.upload': boolean;
|
||||||
|
'playlist.create': boolean;
|
||||||
|
'playlist.delete': boolean;
|
||||||
|
'playlist.edit': boolean;
|
||||||
|
'server.admin': boolean;
|
||||||
|
'user.edit': boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { Genre } from './genre-domain-types';
|
||||||
|
|
||||||
|
import { JFPlaylistListSort } from '/@/shared/api/jellyfin.types';
|
||||||
|
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||||
|
import { NDPlaylistListSort } from '/@/shared/api/navidrome.types';
|
||||||
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
|
import {
|
||||||
|
BaseEndpointArgs,
|
||||||
|
BasePaginatedResponse,
|
||||||
|
BaseQuery,
|
||||||
|
LibraryItem,
|
||||||
|
} from '/@/shared/types/domain-types';
|
||||||
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
|
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||||
|
import { Song, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
|
export enum PlaylistListSortOptions {
|
||||||
|
DURATION = 'duration',
|
||||||
|
NAME = 'name',
|
||||||
|
OWNER = 'owner',
|
||||||
|
PUBLIC = 'public',
|
||||||
|
TRACK_COUNT = 'trackCount',
|
||||||
|
UPDATED_AT = 'updatedAt',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PlaylistListSortOptionsLabels = {
|
||||||
|
[PlaylistListSortOptions.DURATION]: i18n.t('filter.duration'),
|
||||||
|
[PlaylistListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[PlaylistListSortOptions.OWNER]: i18n.t('filter.owner'),
|
||||||
|
[PlaylistListSortOptions.PUBLIC]: i18n.t('filter.public'),
|
||||||
|
[PlaylistListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||||
|
[PlaylistListSortOptions.UPDATED_AT]: i18n.t('filter.updatedAt'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum PlaylistListSort {
|
||||||
|
DURATION = 'duration',
|
||||||
|
NAME = 'name',
|
||||||
|
OWNER = 'owner',
|
||||||
|
PUBLIC = 'public',
|
||||||
|
SONG_COUNT = 'songCount',
|
||||||
|
UPDATED_AT = 'updatedAt',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AddToPlaylistArgs = BaseEndpointArgs & {
|
||||||
|
body: AddToPlaylistBody;
|
||||||
|
query: AddToPlaylistQuery;
|
||||||
|
serverId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddToPlaylistBody = {
|
||||||
|
songId: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddToPlaylistQuery = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddToPlaylistResponse = null | undefined;
|
||||||
|
|
||||||
|
export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody; serverId?: string };
|
||||||
|
|
||||||
|
export type CreatePlaylistBody = {
|
||||||
|
_custom?: {
|
||||||
|
navidrome?: {
|
||||||
|
owner?: string;
|
||||||
|
ownerId?: string;
|
||||||
|
rules?: Record<string, any>;
|
||||||
|
sync?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
comment?: string;
|
||||||
|
name: string;
|
||||||
|
public?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreatePlaylistResponse = undefined | { id: string };
|
||||||
|
|
||||||
|
export type DeletePlaylistArgs = BaseEndpointArgs & {
|
||||||
|
query: DeletePlaylistQuery;
|
||||||
|
serverId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeletePlaylistQuery = { id: string };
|
||||||
|
|
||||||
|
export type DeletePlaylistResponse = null | undefined;
|
||||||
|
|
||||||
|
export type Playlist = {
|
||||||
|
description: null | string;
|
||||||
|
duration: null | number;
|
||||||
|
genres: Genre[];
|
||||||
|
id: string;
|
||||||
|
imagePlaceholderUrl: null | string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
itemType: LibraryItem.PLAYLIST;
|
||||||
|
name: string;
|
||||||
|
owner: null | string;
|
||||||
|
ownerId: null | string;
|
||||||
|
public: boolean | null;
|
||||||
|
rules?: null | Record<string, any>;
|
||||||
|
serverId: string;
|
||||||
|
serverType: ServerType;
|
||||||
|
size: null | number;
|
||||||
|
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>>;
|
||||||
|
navidrome?: Partial<z.infer<typeof ndType._parameters.playlistList>>;
|
||||||
|
};
|
||||||
|
limit?: number;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
|
||||||
|
|
||||||
|
export type RemoveFromPlaylistArgs = BaseEndpointArgs & {
|
||||||
|
query: RemoveFromPlaylistQuery;
|
||||||
|
serverId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RemoveFromPlaylistQuery = {
|
||||||
|
id: string;
|
||||||
|
songId: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RemoveFromPlaylistResponse = null | undefined;
|
||||||
|
|
||||||
|
export type UpdatePlaylistArgs = BaseEndpointArgs & {
|
||||||
|
body: UpdatePlaylistBody;
|
||||||
|
query: UpdatePlaylistQuery;
|
||||||
|
serverId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdatePlaylistBody = {
|
||||||
|
_custom?: {
|
||||||
|
navidrome?: {
|
||||||
|
owner?: string;
|
||||||
|
ownerId?: string;
|
||||||
|
rules?: Record<string, any>;
|
||||||
|
sync?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
comment?: string;
|
||||||
|
genres?: Genre[];
|
||||||
|
name: string;
|
||||||
|
public?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdatePlaylistQuery = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdatePlaylistResponse = null | undefined;
|
||||||
|
|
||||||
|
type PlaylistListSortMap = {
|
||||||
|
jellyfin: Record<PlaylistListSort, JFPlaylistListSort | undefined>;
|
||||||
|
navidrome: Record<PlaylistListSort, NDPlaylistListSort | undefined>;
|
||||||
|
subsonic: Record<PlaylistListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const playlistListSortMap: PlaylistListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
duration: JFPlaylistListSort.DURATION,
|
||||||
|
name: JFPlaylistListSort.NAME,
|
||||||
|
owner: undefined,
|
||||||
|
public: undefined,
|
||||||
|
songCount: JFPlaylistListSort.SONG_COUNT,
|
||||||
|
updatedAt: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
duration: NDPlaylistListSort.DURATION,
|
||||||
|
name: NDPlaylistListSort.NAME,
|
||||||
|
owner: NDPlaylistListSort.OWNER,
|
||||||
|
public: NDPlaylistListSort.PUBLIC,
|
||||||
|
songCount: NDPlaylistListSort.SONG_COUNT,
|
||||||
|
updatedAt: NDPlaylistListSort.UPDATED_AT,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
duration: undefined,
|
||||||
|
name: undefined,
|
||||||
|
owner: undefined,
|
||||||
|
public: undefined,
|
||||||
|
songCount: undefined,
|
||||||
|
updatedAt: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export type PlaylistDetailArgs = BaseEndpointArgs & { query: PlaylistDetailQuery };
|
||||||
|
|
||||||
|
export type PlaylistDetailQuery = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlaylistDetailResponse = Playlist;
|
||||||
|
|
||||||
|
export type PlaylistSongListArgs = BaseEndpointArgs & { query: PlaylistSongListQuery };
|
||||||
|
|
||||||
|
export type PlaylistSongListQuery = {
|
||||||
|
id: string;
|
||||||
|
limit?: number;
|
||||||
|
sortBy?: SongListSort;
|
||||||
|
sortOrder?: ListSortOrder;
|
||||||
|
startIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlaylistSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { BaseEndpointArgs } from '/@/shared/types/domain-types';
|
||||||
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
|
import { AlbumArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
|
export type SearchAlbumArtistsQuery = {
|
||||||
|
albumArtistLimit?: number;
|
||||||
|
albumArtistStartIndex?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
query?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchAlbumsQuery = {
|
||||||
|
albumLimit?: number;
|
||||||
|
albumStartIndex?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
query?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchArgs = BaseEndpointArgs & {
|
||||||
|
query: SearchQuery;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchQuery = {
|
||||||
|
albumArtistLimit?: number;
|
||||||
|
albumArtistStartIndex?: number;
|
||||||
|
albumLimit?: number;
|
||||||
|
albumStartIndex?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
query?: string;
|
||||||
|
songLimit?: number;
|
||||||
|
songStartIndex?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchResponse = {
|
||||||
|
albumArtists: AlbumArtist[];
|
||||||
|
albums: Album[];
|
||||||
|
songs: Song[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchSongsQuery = {
|
||||||
|
musicFolderId?: string;
|
||||||
|
query?: string;
|
||||||
|
songLimit?: number;
|
||||||
|
songStartIndex?: number;
|
||||||
|
};
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
|
||||||
|
import { BaseEndpointArgs, BasePaginatedResponse } from '/@/shared/types/domain-types';
|
||||||
|
import { ServerFeatures } from '/@/shared/types/features-types';
|
||||||
|
|
||||||
|
export enum ServerListSortOptions {
|
||||||
|
CREATED_AT = 'createdAt',
|
||||||
|
NAME = 'name',
|
||||||
|
TYPE = 'type',
|
||||||
|
UPDATED_AT = 'updatedAt',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerListSortOptionsLabels = {
|
||||||
|
[ServerListSortOptions.CREATED_AT]: i18n.t('filter.createdAt'),
|
||||||
|
[ServerListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[ServerListSortOptions.TYPE]: i18n.t('filter.type'),
|
||||||
|
[ServerListSortOptions.UPDATED_AT]: i18n.t('filter.updatedAt'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ServerType {
|
||||||
|
JELLYFIN = 'jellyfin',
|
||||||
|
NAVIDROME = 'navidrome',
|
||||||
|
SUBSONIC = 'subsonic',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ServerInfo = {
|
||||||
|
features: ServerFeatures;
|
||||||
|
id?: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerInfoArgs = BaseEndpointArgs;
|
||||||
|
|
||||||
|
export type ServerListItem = {
|
||||||
|
credential: string;
|
||||||
|
features?: ServerFeatures;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
ndCredential?: string;
|
||||||
|
savePassword?: boolean;
|
||||||
|
type: ServerType;
|
||||||
|
url: string;
|
||||||
|
userId: null | string;
|
||||||
|
username: string;
|
||||||
|
version?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerMusicFolder = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerMusicFolderListArgs = BaseEndpointArgs;
|
||||||
|
|
||||||
|
export type ServerMusicFolderListQuery = null;
|
||||||
|
export type ServerMusicFolderListResponse =
|
||||||
|
| BasePaginatedResponse<ServerMusicFolder[]>
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
|
export type ServerMusicFoldersResponse = ServerMusicFolder[];
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum ListSortOrder {
|
||||||
|
ASC = 'ASC',
|
||||||
|
DESC = 'DESC',
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
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,
|
||||||
|
GainInfo,
|
||||||
|
LibraryItem,
|
||||||
|
Played,
|
||||||
|
} from '/@/shared/types/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';
|
||||||
|
|
||||||
|
export enum SongListSortOptions {
|
||||||
|
ALBUM = 'album',
|
||||||
|
ALBUM_ARTIST = 'albumArtist',
|
||||||
|
ARTIST = 'artist',
|
||||||
|
BPM = 'bpm',
|
||||||
|
CHANNELS = 'channels',
|
||||||
|
COMMENT = 'comment',
|
||||||
|
DURATION = 'duration',
|
||||||
|
GENRE = 'genre',
|
||||||
|
ID = 'id',
|
||||||
|
IS_FAVORITE = 'isFavorite',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
|
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
YEAR = 'year',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SongListSortOptionsLabels = {
|
||||||
|
[SongListSortOptions.ALBUM]: i18n.t('filter.album'),
|
||||||
|
[SongListSortOptions.ALBUM_ARTIST]: i18n.t('filter.albumArtist'),
|
||||||
|
[SongListSortOptions.ARTIST]: i18n.t('filter.artist'),
|
||||||
|
[SongListSortOptions.BPM]: i18n.t('filter.bpm'),
|
||||||
|
[SongListSortOptions.CHANNELS]: i18n.t('filter.channels'),
|
||||||
|
[SongListSortOptions.COMMENT]: i18n.t('filter.comment'),
|
||||||
|
[SongListSortOptions.DURATION]: i18n.t('filter.duration'),
|
||||||
|
[SongListSortOptions.GENRE]: i18n.t('filter.genre'),
|
||||||
|
[SongListSortOptions.ID]: i18n.t('filter.id'),
|
||||||
|
[SongListSortOptions.IS_FAVORITE]: i18n.t('filter.isFavorite'),
|
||||||
|
[SongListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[SongListSortOptions.PLAY_COUNT]: i18n.t('filter.playCount'),
|
||||||
|
[SongListSortOptions.RANDOM]: i18n.t('filter.random'),
|
||||||
|
[SongListSortOptions.RATING]: i18n.t('filter.rating'),
|
||||||
|
[SongListSortOptions.RECENTLY_ADDED]: i18n.t('filter.recentlyAdded'),
|
||||||
|
[SongListSortOptions.RECENTLY_PLAYED]: i18n.t('filter.recentlyPlayed'),
|
||||||
|
[SongListSortOptions.RELEASE_DATE]: i18n.t('filter.releaseDate'),
|
||||||
|
[SongListSortOptions.YEAR]: i18n.t('filter.year'),
|
||||||
|
};
|
||||||
|
export enum SongListSort {
|
||||||
|
ALBUM = 'album',
|
||||||
|
ALBUM_ARTIST = 'albumArtist',
|
||||||
|
ARTIST = 'artist',
|
||||||
|
BPM = 'bpm',
|
||||||
|
CHANNELS = 'channels',
|
||||||
|
COMMENT = 'comment',
|
||||||
|
DURATION = 'duration',
|
||||||
|
FAVORITED = 'favorited',
|
||||||
|
GENRE = 'genre',
|
||||||
|
ID = 'id',
|
||||||
|
NAME = 'name',
|
||||||
|
PLAY_COUNT = 'playCount',
|
||||||
|
RANDOM = 'random',
|
||||||
|
RATING = 'rating',
|
||||||
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
|
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||||
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
YEAR = 'year',
|
||||||
|
}
|
||||||
|
export type Song = {
|
||||||
|
album: null | string;
|
||||||
|
albumArtists: RelatedArtist[];
|
||||||
|
albumId: string;
|
||||||
|
artistName: string;
|
||||||
|
artists: RelatedArtist[];
|
||||||
|
bitRate: number;
|
||||||
|
bpm: null | number;
|
||||||
|
channels: null | number;
|
||||||
|
comment: null | string;
|
||||||
|
compilation: boolean | null;
|
||||||
|
container: null | string;
|
||||||
|
createdAt: string;
|
||||||
|
discNumber: number;
|
||||||
|
discSubtitle: null | string;
|
||||||
|
duration: number;
|
||||||
|
gain: GainInfo | null;
|
||||||
|
genres: Genre[];
|
||||||
|
id: string;
|
||||||
|
imagePlaceholderUrl: null | string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
itemType: LibraryItem.SONG;
|
||||||
|
lastPlayedAt: null | string;
|
||||||
|
lyrics: null | string;
|
||||||
|
name: string;
|
||||||
|
participants: null | Record<string, RelatedArtist[]>;
|
||||||
|
path: null | string;
|
||||||
|
peak: GainInfo | null;
|
||||||
|
playCount: number;
|
||||||
|
playlistItemId?: string;
|
||||||
|
releaseDate: null | string;
|
||||||
|
releaseYear: null | string;
|
||||||
|
serverId: string;
|
||||||
|
serverType: ServerType;
|
||||||
|
size: number;
|
||||||
|
streamUrl: string;
|
||||||
|
tags: null | Record<string, string[]>;
|
||||||
|
trackNumber: number;
|
||||||
|
uniqueId: string;
|
||||||
|
updatedAt: string;
|
||||||
|
userFavorite: boolean;
|
||||||
|
userRating: null | number;
|
||||||
|
};
|
||||||
|
export type SongListArgs = BaseEndpointArgs & { query: SongListQuery };
|
||||||
|
|
||||||
|
export interface SongListQuery extends BaseQuery<SongListSort> {
|
||||||
|
_custom?: {
|
||||||
|
jellyfin?: Partial<z.infer<typeof jfType._parameters.songList>>;
|
||||||
|
navidrome?: Partial<z.infer<typeof ndType._parameters.songList>>;
|
||||||
|
};
|
||||||
|
albumArtistIds?: string[];
|
||||||
|
albumIds?: string[];
|
||||||
|
artistIds?: string[];
|
||||||
|
favorite?: boolean;
|
||||||
|
genreIds?: string[];
|
||||||
|
imageSize?: number;
|
||||||
|
limit?: number;
|
||||||
|
maxYear?: number;
|
||||||
|
minYear?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
role?: string;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||||
|
type SongListSortMap = {
|
||||||
|
jellyfin: Record<SongListSort, JFSongListSort | undefined>;
|
||||||
|
navidrome: Record<SongListSort, NDSongListSort | undefined>;
|
||||||
|
subsonic: Record<SongListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const songListSortMap: SongListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
album: JFSongListSort.ALBUM,
|
||||||
|
albumArtist: JFSongListSort.ALBUM_ARTIST,
|
||||||
|
artist: JFSongListSort.ARTIST,
|
||||||
|
bpm: undefined,
|
||||||
|
channels: undefined,
|
||||||
|
comment: undefined,
|
||||||
|
duration: JFSongListSort.DURATION,
|
||||||
|
favorited: undefined,
|
||||||
|
genre: undefined,
|
||||||
|
id: undefined,
|
||||||
|
name: JFSongListSort.NAME,
|
||||||
|
playCount: JFSongListSort.PLAY_COUNT,
|
||||||
|
random: JFSongListSort.RANDOM,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: JFSongListSort.RECENTLY_ADDED,
|
||||||
|
recentlyPlayed: JFSongListSort.RECENTLY_PLAYED,
|
||||||
|
releaseDate: JFSongListSort.RELEASE_DATE,
|
||||||
|
year: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
album: NDSongListSort.ALBUM_SONGS,
|
||||||
|
albumArtist: NDSongListSort.ALBUM_ARTIST,
|
||||||
|
artist: NDSongListSort.ARTIST,
|
||||||
|
bpm: NDSongListSort.BPM,
|
||||||
|
channels: NDSongListSort.CHANNELS,
|
||||||
|
comment: NDSongListSort.COMMENT,
|
||||||
|
duration: NDSongListSort.DURATION,
|
||||||
|
favorited: NDSongListSort.FAVORITED,
|
||||||
|
genre: NDSongListSort.GENRE,
|
||||||
|
id: NDSongListSort.ID,
|
||||||
|
name: NDSongListSort.TITLE,
|
||||||
|
playCount: NDSongListSort.PLAY_COUNT,
|
||||||
|
random: NDSongListSort.RANDOM,
|
||||||
|
rating: NDSongListSort.RATING,
|
||||||
|
recentlyAdded: NDSongListSort.RECENTLY_ADDED,
|
||||||
|
recentlyPlayed: NDSongListSort.PLAY_DATE,
|
||||||
|
releaseDate: undefined,
|
||||||
|
year: NDSongListSort.YEAR,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
album: undefined,
|
||||||
|
albumArtist: undefined,
|
||||||
|
artist: undefined,
|
||||||
|
bpm: undefined,
|
||||||
|
channels: undefined,
|
||||||
|
comment: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
favorited: undefined,
|
||||||
|
genre: undefined,
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
playCount: undefined,
|
||||||
|
random: undefined,
|
||||||
|
rating: undefined,
|
||||||
|
recentlyAdded: undefined,
|
||||||
|
recentlyPlayed: undefined,
|
||||||
|
releaseDate: undefined,
|
||||||
|
year: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export type RandomSongListArgs = BaseEndpointArgs & {
|
||||||
|
query: RandomSongListQuery;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RandomSongListQuery = {
|
||||||
|
genre?: string;
|
||||||
|
limit?: number;
|
||||||
|
maxYear?: number;
|
||||||
|
minYear?: number;
|
||||||
|
musicFolderId?: string;
|
||||||
|
played: Played;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RandomSongListResponse = SongListResponse;
|
||||||
|
export type SimilarSongsArgs = BaseEndpointArgs & {
|
||||||
|
query: SimilarSongsQuery;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SimilarSongsQuery = {
|
||||||
|
albumArtistIds: string[];
|
||||||
|
count?: number;
|
||||||
|
songId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SongDetailArgs = BaseEndpointArgs & { query: SongDetailQuery };
|
||||||
|
export type SongDetailQuery = { id: string };
|
||||||
|
|
||||||
|
export type SongDetailResponse = null | Song | undefined;
|
||||||
|
export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery };
|
||||||
|
|
||||||
|
export type TopSongListQuery = {
|
||||||
|
artist: string;
|
||||||
|
artistId: string;
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TopSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import i18n from 'src/i18n/i18n';
|
||||||
|
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';
|
||||||
|
|
||||||
|
export enum UserListSortOptions {
|
||||||
|
CREATED_AT = 'createdAt',
|
||||||
|
EMAIL = 'email',
|
||||||
|
NAME = 'name',
|
||||||
|
UPDATED_AT = 'updatedAt',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserListSortOptionsLabels = {
|
||||||
|
[UserListSortOptions.CREATED_AT]: i18n.t('filter.createdAt'),
|
||||||
|
[UserListSortOptions.EMAIL]: i18n.t('filter.email'),
|
||||||
|
[UserListSortOptions.NAME]: i18n.t('filter.name'),
|
||||||
|
[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 RatingQuery = {
|
||||||
|
item: AnyLibraryItems;
|
||||||
|
rating: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
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> {
|
||||||
|
_custom?: {
|
||||||
|
navidrome?: {
|
||||||
|
owner_id?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
limit?: number;
|
||||||
|
searchTerm?: string;
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserListResponse = BasePaginatedResponse<User[]> | null | undefined;
|
||||||
|
type UserListSortMap = {
|
||||||
|
jellyfin: Record<UserListSort, undefined>;
|
||||||
|
navidrome: Record<UserListSort, NDUserListSort | undefined>;
|
||||||
|
subsonic: Record<UserListSort, undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const userListSortMap: UserListSortMap = {
|
||||||
|
jellyfin: {
|
||||||
|
name: undefined,
|
||||||
|
},
|
||||||
|
navidrome: {
|
||||||
|
name: NDUserListSort.NAME,
|
||||||
|
},
|
||||||
|
subsonic: {
|
||||||
|
name: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export type ScrobbleArgs = BaseEndpointArgs & {
|
||||||
|
query: ScrobbleQuery;
|
||||||
|
serverId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScrobbleQuery = {
|
||||||
|
event?: 'pause' | 'start' | 'timeupdate' | 'unpause';
|
||||||
|
id: string;
|
||||||
|
position?: number;
|
||||||
|
submission: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScrobbleResponse = null | undefined;
|
||||||
|
export enum UserListSort {
|
||||||
|
NAME = 'name',
|
||||||
|
}
|
||||||
|
export type User = {
|
||||||
|
createdAt: null | string;
|
||||||
|
email: null | string;
|
||||||
|
id: string;
|
||||||
|
isAdmin: boolean | null;
|
||||||
|
lastLoginAt: null | string;
|
||||||
|
name: string;
|
||||||
|
updatedAt: null | string;
|
||||||
|
};
|
||||||
@@ -2,15 +2,11 @@ import { AppRoute } from '@ts-rest/core';
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Song } from 'src/main/features/core/lyrics/netease';
|
import { Song } from 'src/main/features/core/lyrics/netease';
|
||||||
|
|
||||||
import {
|
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
Album,
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
AlbumArtist,
|
import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
Artist,
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
LibraryItem,
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
Playlist,
|
|
||||||
QueueSong,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
import { ServerFeatures } from '/@/shared/types/features-types';
|
|
||||||
|
|
||||||
export enum ListDisplayType {
|
export enum ListDisplayType {
|
||||||
CARD = 'card',
|
CARD = 'card',
|
||||||
@@ -27,12 +23,6 @@ export enum Platform {
|
|||||||
WINDOWS = 'windows',
|
WINDOWS = 'windows',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ServerType {
|
|
||||||
JELLYFIN = 'jellyfin',
|
|
||||||
NAVIDROME = 'navidrome',
|
|
||||||
SUBSONIC = 'subsonic',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CardRoute = {
|
export type CardRoute = {
|
||||||
route: AppRoute | string;
|
route: AppRoute | string;
|
||||||
slugs?: RouteSlug[];
|
slugs?: RouteSlug[];
|
||||||
@@ -210,20 +200,6 @@ export type QueryBuilderRule = {
|
|||||||
value?: any | Date | null | number | string | undefined;
|
value?: any | Date | null | number | string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ServerListItem = {
|
|
||||||
credential: string;
|
|
||||||
features?: ServerFeatures;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
ndCredential?: string;
|
|
||||||
savePassword?: boolean;
|
|
||||||
type: ServerType;
|
|
||||||
url: string;
|
|
||||||
userId: null | string;
|
|
||||||
username: string;
|
|
||||||
version?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SongState = {
|
export type SongState = {
|
||||||
position?: number;
|
position?: number;
|
||||||
repeat?: PlayerRepeat;
|
repeat?: PlayerRepeat;
|
||||||
|
|||||||
Reference in New Issue
Block a user