diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 3eb362f0a..29ab25830 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -227,7 +227,11 @@ "songCount": "song count", "title": "title", "toYear": "to year", - "trackNumber": "track" + "trackNumber": "track", + "createdAt": "created at", + "updatedAt": "updated at", + "type": "type", + "email": "email" }, "form": { "addServer": { diff --git a/src/main/features/core/lyrics/index.ts b/src/main/features/core/lyrics/index.ts index ab38c8b2d..e20d19902 100644 --- a/src/main/features/core/lyrics/index.ts +++ b/src/main/features/core/lyrics/index.ts @@ -17,7 +17,7 @@ import { getSearchResults as searchNetease, } from './netease'; -import { Song } from '/@/shared/types/domain-types'; +import { Song } from '/@/shared/types/domain/song-domain-types'; export enum LyricSource { GENIUS = 'Genius', diff --git a/src/main/features/core/lyrics/shared.ts b/src/main/features/core/lyrics/shared.ts index 190aabc28..d3b575497 100644 --- a/src/main/features/core/lyrics/shared.ts +++ b/src/main/features/core/lyrics/shared.ts @@ -1,9 +1,7 @@ import Fuse from 'fuse.js'; -import { - InternetProviderLyricSearchResponse, - LyricSearchQuery, -} from '/@/shared/types/domain-types'; +import { InternetProviderLyricSearchResponse } from '/@/shared/types/domain/lyric-domain-types'; +import { LyricSearchQuery } from '/@/shared/types/domain/lyric-domain-types'; export const orderSearchResults = (args: { params: LyricSearchQuery; diff --git a/src/shared/api/jellyfin/jellyfin-normalize.ts b/src/shared/api/jellyfin/jellyfin-normalize.ts index cb522f632..7af54bbf1 100644 --- a/src/shared/api/jellyfin/jellyfin-normalize.ts +++ b/src/shared/api/jellyfin/jellyfin-normalize.ts @@ -3,17 +3,17 @@ import { z } from 'zod'; import { JFAlbum, JFGenre, JFMusicFolder, JFPlaylist } from '/@/shared/api/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 { - Album, - AlbumArtist, - Genre, - LibraryItem, - MusicFolder, - Playlist, - RelatedArtist, - Song, -} from '/@/shared/types/domain-types'; -import { ServerListItem, ServerType } from '/@/shared/types/types'; + ServerListItem, + ServerMusicFolder, + ServerType, +} from '/@/shared/types/domain/server-domain-types'; +import { Song } from '/@/shared/types/domain/song-domain-types'; const getStreamUrl = (args: { container?: string; @@ -432,7 +432,7 @@ const normalizePlaylist = ( }; }; -const normalizeMusicFolder = (item: JFMusicFolder): MusicFolder => { +const normalizeMusicFolder = (item: JFMusicFolder): ServerMusicFolder => { return { id: item.Id, name: item.Name, diff --git a/src/shared/api/navidrome/navidrome-normalize.ts b/src/shared/api/navidrome/navidrome-normalize.ts index af5f9e61b..1fb768add 100644 --- a/src/shared/api/navidrome/navidrome-normalize.ts +++ b/src/shared/api/navidrome/navidrome-normalize.ts @@ -4,17 +4,14 @@ import z from 'zod'; import { NDGenre } from '/@/shared/api/navidrome.types'; import { ndType } from '/@/shared/api/navidrome/navidrome-types'; import { ssType } from '/@/shared/api/subsonic/subsonic-types'; -import { - Album, - AlbumArtist, - Genre, - LibraryItem, - Playlist, - RelatedArtist, - Song, - User, -} from '/@/shared/types/domain-types'; -import { ServerListItem, ServerType } from '/@/shared/types/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 { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types'; +import { Song } from '/@/shared/types/domain/song-domain-types'; +import { User } from '/@/shared/types/domain/user-domain-types'; const getImageUrl = (args: { url: null | string }) => { const { url } = args; diff --git a/src/shared/api/subsonic/subsonic-normalize.ts b/src/shared/api/subsonic/subsonic-normalize.ts index 83098c0ac..267d12bcf 100644 --- a/src/shared/api/subsonic/subsonic-normalize.ts +++ b/src/shared/api/subsonic/subsonic-normalize.ts @@ -2,17 +2,12 @@ import { nanoid } from 'nanoid'; import { z } from 'zod'; import { ssType } from '/@/shared/api/subsonic/subsonic-types'; -import { - Album, - AlbumArtist, - Genre, - LibraryItem, - Playlist, - QueueSong, - RelatedArtist, - ServerListItem, - ServerType, -} from '/@/shared/types/domain-types'; +import { LibraryItem, QueueSong } 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 { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types'; const getCoverArtUrl = (args: { baseUrl: string | undefined; diff --git a/src/shared/api/utils.ts b/src/shared/api/utils.ts index 308299ff7..568b84b20 100644 --- a/src/shared/api/utils.ts +++ b/src/shared/api/utils.ts @@ -4,7 +4,7 @@ import semverCoerce from 'semver/functions/coerce'; import semverGte from 'semver/functions/gte'; 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'; // Since ts-rest client returns a strict response type, we need to add the headers to the body object diff --git a/src/shared/types/domain-types.ts b/src/shared/types/domain-types.ts index 137d72e7c..aa1c884a3 100644 --- a/src/shared/types/domain-types.ts +++ b/src/shared/types/domain-types.ts @@ -1,30 +1,86 @@ import orderBy from 'lodash/orderBy'; import reverse from 'lodash/reverse'; import shuffle from 'lodash/shuffle'; -import { z } from 'zod'; import { - JFAlbumArtistListSort, - JFAlbumListSort, - JFArtistListSort, - JFGenreListSort, - JFPlaylistListSort, - JFSongListSort, - JFSortOrder, -} from '/@/shared/api/jellyfin.types'; -import { jfType } from '/@/shared/api/jellyfin/jellyfin-types'; + 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 { - NDAlbumArtistListSort, - NDAlbumListSort, - NDGenreListSort, - NDOrder, - NDPlaylistListSort, - NDSongListSort, - NDSortOrder, - NDUserListSort, -} from '/@/shared/api/navidrome.types'; -import { ndType } from '/@/shared/api/navidrome/navidrome-types'; -import { ServerFeatures } from '/@/shared/types/features-types'; + 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 { @@ -36,17 +92,6 @@ export enum LibraryItem { SONG = 'song', } -export enum ServerType { - JELLYFIN = 'jellyfin', - NAVIDROME = 'navidrome', - SUBSONIC = 'subsonic', -} - -export enum SortOrder { - ASC = 'ASC', - DESC = 'DESC', -} - export type AnyLibraryItem = Album | AlbumArtist | Artist | Playlist | QueueSong | Song; export type AnyLibraryItems = @@ -83,34 +128,10 @@ export type QueueSong = Song & { uniqueId: string; }; -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 User = { - createdAt: null | string; - email: null | string; - id: string; - isAdmin: boolean | null; - lastLoginAt: null | string; - name: string; - updatedAt: null | string; -}; - type SortOrderMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; + jellyfin: Record; + navidrome: Record; + subsonic: Record; }; export const sortOrderMap: SortOrderMap = { @@ -140,10 +161,6 @@ export enum ExternalType { LINK = 'LINK', } -export enum GenreListSort { - NAME = 'name', -} - export enum ImageType { BACKDROP = 'BACKDROP', LOGO = 'LOGO', @@ -151,73 +168,11 @@ export enum ImageType { SCREENSHOT = 'SCREENSHOT', } -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; - playCount: null | number; - releaseDate: null | string; - releaseYear: null | number; - serverId: string; - serverType: ServerType; - size: null | number; - songCount: null | number; - songs?: Song[]; - tags: null | Record; - uniqueId: string; - updatedAt: string; - userFavorite: boolean; - userRating: null | number; -} & { songs?: Song[] }; - -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 enum Played { + All = 'all', + Never = 'never', + Played = 'played', +} export type AuthenticationResponse = { credential: string; @@ -226,6 +181,13 @@ export type AuthenticationResponse = { username: string; }; +export type BaseEndpointArgs = { + apiClientProps: { + server: null | ServerListItem; + signal?: AbortSignal; + }; +}; + export interface BasePaginatedResponse { error?: any | string; items: T; @@ -235,7 +197,7 @@ export interface BasePaginatedResponse { export interface BaseQuery { sortBy: T; - sortOrder: SortOrder; + sortOrder: ListSortOrder; } export type EndpointDetails = { @@ -247,649 +209,6 @@ export type GainInfo = { track?: number; }; -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 { - _custom?: { - jellyfin?: null; - navidrome?: null; - }; - limit?: number; - musicFolderId?: string; - searchTerm?: string; - startIndex: number; -} - -// Genre List -export type GenreListResponse = BasePaginatedResponse | null | undefined; - -export type GenresResponse = Genre[]; - -export type ListSortOrder = JFSortOrder | NDOrder; - -export type MusicFolder = { - id: string; - name: string; -}; - -export type MusicFoldersResponse = MusicFolder[]; - -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; - serverId: string; - serverType: ServerType; - size: null | number; - songCount: null | number; - sync?: boolean | null; -}; - -export type RelatedAlbumArtist = { - id: string; - name: string; -}; - -export type RelatedArtist = { - id: string; - imageUrl: null | string; - name: string; -}; - -export type Song = { - album: null | string; - albumArtists: RelatedArtist[]; - albumId: string; - artistName: string; - artists: RelatedArtist[]; - bitDepth: null | number; - 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; - path: null | string; - peak: GainInfo | null; - playCount: number; - playlistItemId?: string; - releaseDate: null | string; - releaseYear: null | string; - sampleRate: null | number; - serverId: string; - serverType: ServerType; - size: number; - streamUrl: string; - tags: null | Record; - trackNumber: number; - uniqueId: string; - updatedAt: string; - userFavorite: boolean; - userRating: null | number; -}; - -type BaseEndpointArgs = { - apiClientProps: { - server: null | ServerListItem; - signal?: AbortSignal; - }; -}; - -type GenreListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -export const genreListSortMap: GenreListSortMap = { - jellyfin: { - name: JFGenreListSort.NAME, - }, - navidrome: { - name: NDGenreListSort.NAME, - }, - subsonic: { - name: undefined, - }, -}; - -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 { - _custom?: { - jellyfin?: Partial>; - navidrome?: Partial>; - }; - 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 | null | undefined; - -type AlbumListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -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 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 AlbumDetailArgs = BaseEndpointArgs & { query: AlbumDetailQuery }; - -export type AlbumDetailQuery = { id: string }; - -// Album Detail -export type AlbumDetailResponse = Album | null | undefined; - -export type AlbumInfo = { - imageUrl: null | string; - notes: null | string; -}; - -export type SongListArgs = BaseEndpointArgs & { query: SongListQuery }; - -export interface SongListQuery extends BaseQuery { - _custom?: { - jellyfin?: Partial>; - navidrome?: Partial>; - }; - 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; -} - -// Song List -export type SongListResponse = BasePaginatedResponse | null | undefined; - -type SongListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -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 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 { - _custom?: { - jellyfin?: Partial>; - navidrome?: Partial>; - }; - limit?: number; - musicFolderId?: string; - searchTerm?: string; - startIndex: number; -} - -// Album Artist List -export type AlbumArtistListResponse = BasePaginatedResponse | null | undefined; - -export type SongDetailArgs = BaseEndpointArgs & { query: SongDetailQuery }; - -export type SongDetailQuery = { id: string }; - -// Song Detail -export type SongDetailResponse = null | Song | undefined; - -type AlbumArtistListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -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, - }, -}; - -// Album Artist Detail - -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 { - _custom?: { - jellyfin?: Partial>; - navidrome?: Partial>; - }; - limit?: number; - musicFolderId?: string; - role?: string; - searchTerm?: string; - startIndex: number; -} - -// Artist List -export type ArtistListResponse = BasePaginatedResponse | null | undefined; - -type ArtistListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -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, - }, -}; - -// Artist Detail - -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; -}; - -// Add to playlist -export type AddToPlaylistResponse = null | undefined; - -export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody; serverId?: string }; - -export type CreatePlaylistBody = { - _custom?: { - navidrome?: { - owner?: string; - ownerId?: string; - rules?: Record; - sync?: boolean; - }; - }; - comment?: string; - name: string; - public?: boolean; -}; - -// Create Playlist -export type CreatePlaylistResponse = undefined | { id: string }; - -export type DeletePlaylistArgs = BaseEndpointArgs & { - query: DeletePlaylistQuery; - serverId?: string; -}; - -export type DeletePlaylistQuery = { id: string }; - -// Delete Playlist -export type DeletePlaylistResponse = null | undefined; - -export type FavoriteArgs = BaseEndpointArgs & { query: FavoriteQuery; serverId?: string }; - -export type FavoriteQuery = { - id: string[]; - type: LibraryItem; -}; - -// Favorite -export type FavoriteResponse = null | undefined; - -export type PlaylistListArgs = BaseEndpointArgs & { query: PlaylistListQuery }; - -export interface PlaylistListQuery extends BaseQuery { - _custom?: { - jellyfin?: Partial>; - navidrome?: Partial>; - }; - limit?: number; - searchTerm?: string; - startIndex: number; -} - -// Playlist List -export type PlaylistListResponse = BasePaginatedResponse | null | undefined; - -export type RatingQuery = { - item: AnyLibraryItems; - rating: number; -}; - -// Rating -export type RatingResponse = null | undefined; - -export type RemoveFromPlaylistArgs = BaseEndpointArgs & { - query: RemoveFromPlaylistQuery; - serverId?: string; -}; - -export type RemoveFromPlaylistQuery = { - id: string; - songId: string[]; -}; - -// Remove from playlist -export type RemoveFromPlaylistResponse = null | undefined; - -export type SetRatingArgs = BaseEndpointArgs & { query: RatingQuery; serverId?: string }; - export type ShareItemArgs = BaseEndpointArgs & { body: ShareItemBody; serverId?: string }; export type ShareItemBody = { @@ -900,272 +219,8 @@ export type ShareItemBody = { resourceType: string; }; -// Sharing export type ShareItemResponse = undefined | { id: string }; -export type UpdatePlaylistArgs = BaseEndpointArgs & { - body: UpdatePlaylistBody; - query: UpdatePlaylistQuery; - serverId?: string; -}; - -export type UpdatePlaylistBody = { - _custom?: { - navidrome?: { - owner?: string; - ownerId?: string; - rules?: Record; - sync?: boolean; - }; - }; - comment?: string; - genres?: Genre[]; - name: string; - public?: boolean; -}; - -export type UpdatePlaylistQuery = { - id: string; -}; - -// Update Playlist -export type UpdatePlaylistResponse = null | undefined; - -type PlaylistListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -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 enum UserListSort { - NAME = 'name', -} - -export type MusicFolderListArgs = BaseEndpointArgs; - -export type MusicFolderListQuery = null; - -// Music Folder List -export type MusicFolderListResponse = BasePaginatedResponse | null | undefined; - -export type PlaylistDetailArgs = BaseEndpointArgs & { query: PlaylistDetailQuery }; - -export type PlaylistDetailQuery = { - id: string; -}; - -// Playlist Detail -export type PlaylistDetailResponse = Playlist; - -export type PlaylistSongListArgs = BaseEndpointArgs & { query: PlaylistSongListQuery }; - -export type PlaylistSongListQuery = { - id: string; - limit?: number; - sortBy?: SongListSort; - sortOrder?: SortOrder; - startIndex: number; -}; - -// Playlist Songs -export type PlaylistSongListResponse = BasePaginatedResponse | null | undefined; - -export type UserListArgs = BaseEndpointArgs & { query: UserListQuery }; - -export interface UserListQuery extends BaseQuery { - _custom?: { - navidrome?: { - owner_id?: string; - }; - }; - limit?: number; - searchTerm?: string; - startIndex: number; -} - -// User list -// Playlist List -export type UserListResponse = BasePaginatedResponse | null | undefined; - -type UserListSortMap = { - jellyfin: Record; - navidrome: Record; - subsonic: Record; -}; - -export const userListSortMap: UserListSortMap = { - jellyfin: { - name: undefined, - }, - navidrome: { - name: NDUserListSort.NAME, - }, - subsonic: { - name: undefined, - }, -}; - -export enum Played { - All = 'all', - Never = 'never', - Played = 'played', -} - -export type ArtistInfoArgs = BaseEndpointArgs & { query: ArtistInfoQuery }; - -// Artist Info -export type ArtistInfoQuery = { - artistId: string; - limit: number; - musicFolderId?: string; -}; - -export type FullLyricsMetadata = Omit & { - 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 LyricOverride = Omit; - -export type LyricsArgs = BaseEndpointArgs & { - query: LyricsQuery; -}; - -export type LyricsQuery = { - songId: string; -}; - -export type LyricsResponse = string | SynchronizedLyricsArray; - -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 ScrobbleArgs = BaseEndpointArgs & { - query: ScrobbleQuery; - serverId?: string; -}; - -export type ScrobbleQuery = { - event?: 'pause' | 'start' | 'timeupdate' | 'unpause'; - id: string; - position?: number; - submission: boolean; -}; - -// Scrobble -export type ScrobbleResponse = null | undefined; - -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; -}; - -export type SynchronizedLyricsArray = Array<[number, string]>; - -export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery }; - -export type TopSongListQuery = { - artist: string; - artistId: string; - limit?: number; -}; - -// Top Songs List -export type TopSongListResponse = BasePaginatedResponse | null | undefined; - export const instanceOfCancellationError = (error: any) => { return 'revert' in error; }; @@ -1199,7 +254,7 @@ export type ControllerEndpoint = { getDownloadUrl: (args: DownloadArgs) => string; getGenreList: (args: GenreListArgs) => Promise; getLyrics?: (args: LyricsArgs) => Promise; - getMusicFolderList: (args: MusicFolderListArgs) => Promise; + getMusicFolderList: (args: ServerMusicFolderListArgs) => Promise; getPlaylistDetail: (args: PlaylistDetailArgs) => Promise; getPlaylistList: (args: PlaylistListArgs) => Promise; getPlaylistListCount: (args: PlaylistListArgs) => Promise; @@ -1242,21 +297,6 @@ export type FontData = { style: string; }; -export type LyricGetQuery = { - remoteSongId: string; - remoteSource: LyricSource; - song: Song; -}; - -export type LyricSearchQuery = { - album?: string; - artist?: string; - duration?: number; - name?: string; -}; - -export type LyricsOverride = Omit & { id: string }; - export type MoveItemArgs = BaseEndpointArgs & { query: MoveItemQuery; }; @@ -1268,42 +308,6 @@ export type MoveItemQuery = { trackId: string; }; -export type ServerInfo = { - features: ServerFeatures; - id?: string; - version: string; -}; - -export type ServerInfoArgs = BaseEndpointArgs; - -export type SimilarSongsArgs = BaseEndpointArgs & { - query: SimilarSongsQuery; -}; - -export type SimilarSongsQuery = { - albumArtistIds: string[]; - count?: number; - songId: string; -}; - -export type StructuredLyric = (StructuredSyncedLyric | StructuredUnsyncedLyric) & { - lang: string; -}; - -export type StructuredLyricsArgs = BaseEndpointArgs & { - query: LyricsQuery; -}; - -export type StructuredSyncedLyric = Omit & { - lyrics: SynchronizedLyricsArray; - synced: true; -}; - -export type StructuredUnsyncedLyric = Omit & { - lyrics: string; - synced: false; -}; - export type Tag = { name: string; options: string[]; @@ -1333,10 +337,10 @@ export type TranscodingQuery = { format?: string; }; -export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: SortOrder) => { +export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: ListSortOrder) => { let results = albums; - const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc'; + const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc'; switch (sortBy) { case AlbumListSort.ALBUM_ARTIST: @@ -1383,10 +387,14 @@ export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: return results; }; -export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder: SortOrder) => { +export const sortSongList = ( + songs: QueueSong[], + sortBy: SongListSort, + sortOrder: ListSortOrder, +) => { let results = songs; - const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc'; + const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc'; switch (sortBy) { case SongListSort.ALBUM: @@ -1478,9 +486,9 @@ export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder export const sortAlbumArtistList = ( artists: AlbumArtist[], sortBy: AlbumArtistListSort | ArtistListSort, - sortOrder: SortOrder, + sortOrder: ListSortOrder, ) => { - const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc'; + const order = sortOrder === ListSortOrder.ASC ? 'asc' : 'desc'; let results = artists; diff --git a/src/shared/types/domain/album-domain-types.ts b/src/shared/types/domain/album-domain-types.ts new file mode 100644 index 000000000..f05034fb0 --- /dev/null +++ b/src/shared/types/domain/album-domain-types.ts @@ -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 { + _custom?: { + jellyfin?: Partial>; + navidrome?: Partial>; + }; + 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 | null | undefined; +type AlbumListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +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; + playCount: null | number; + releaseDate: null | string; + releaseYear: null | number; + serverId: string; + serverType: ServerType; + size: null | number; + songCount: null | number; + songs?: Song[]; + tags: null | Record; + 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; +}; diff --git a/src/shared/types/domain/artist-domain-types.ts b/src/shared/types/domain/artist-domain-types.ts new file mode 100644 index 000000000..eda4eec67 --- /dev/null +++ b/src/shared/types/domain/artist-domain-types.ts @@ -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; + navidrome: Record; + subsonic: Record; +}; + +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 { + _custom?: { + jellyfin?: Partial>; + navidrome?: Partial>; + }; + limit?: number; + musicFolderId?: string; + role?: string; + searchTerm?: string; + startIndex: number; +} + +export type ArtistListResponse = BasePaginatedResponse | null | undefined; +type ArtistListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +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 { + _custom?: { + jellyfin?: Partial>; + navidrome?: Partial>; + }; + limit?: number; + musicFolderId?: string; + searchTerm?: string; + startIndex: number; +} +export type AlbumArtistListResponse = BasePaginatedResponse | null | undefined; +export type ArtistInfoArgs = BaseEndpointArgs & { query: ArtistInfoQuery }; + +export type ArtistInfoQuery = { + artistId: string; + limit: number; + musicFolderId?: string; +}; diff --git a/src/shared/types/domain/genre-domain-types.ts b/src/shared/types/domain/genre-domain-types.ts new file mode 100644 index 000000000..a43e35ec2 --- /dev/null +++ b/src/shared/types/domain/genre-domain-types.ts @@ -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 { + _custom?: { + jellyfin?: null; + navidrome?: null; + }; + limit?: number; + musicFolderId?: string; + searchTerm?: string; + startIndex: number; +} +// Genre List + +export type GenreListResponse = BasePaginatedResponse | null | undefined; + +export type GenresResponse = Genre[]; +type GenreListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +export const genreListSortMap: GenreListSortMap = { + jellyfin: { + name: JFGenreListSort.NAME, + }, + navidrome: { + name: NDGenreListSort.NAME, + }, + subsonic: { + name: undefined, + }, +}; +export enum GenreListSort { + NAME = 'name', +} diff --git a/src/shared/types/domain/lyric-domain-types.ts b/src/shared/types/domain/lyric-domain-types.ts new file mode 100644 index 000000000..33424dce1 --- /dev/null +++ b/src/shared/types/domain/lyric-domain-types.ts @@ -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 & { + 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; + +export type LyricsArgs = BaseEndpointArgs & { + query: LyricsQuery; +}; + +export type LyricSearchQuery = { + album?: string; + artist?: string; + duration?: number; + name?: string; +}; +export type LyricsOverride = Omit & { 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 & { + lyrics: SynchronizedLyricsArray; + synced: true; +}; + +export type StructuredUnsyncedLyric = Omit & { + lyrics: string; + synced: false; +}; +export type SynchronizedLyricsArray = Array<[number, string]>; diff --git a/src/shared/types/domain/permissions-domain-types.ts b/src/shared/types/domain/permissions-domain-types.ts new file mode 100644 index 000000000..624f4d465 --- /dev/null +++ b/src/shared/types/domain/permissions-domain-types.ts @@ -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; +} diff --git a/src/shared/types/domain/playlist-domain-types.ts b/src/shared/types/domain/playlist-domain-types.ts new file mode 100644 index 000000000..01b3b4e74 --- /dev/null +++ b/src/shared/types/domain/playlist-domain-types.ts @@ -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; + 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; + serverId: string; + serverType: ServerType; + size: null | number; + songCount: null | number; + sync?: boolean | null; +}; +export type PlaylistListArgs = BaseEndpointArgs & { query: PlaylistListQuery }; + +export interface PlaylistListQuery extends BaseQuery { + _custom?: { + jellyfin?: Partial>; + navidrome?: Partial>; + }; + limit?: number; + searchTerm?: string; + startIndex: number; +} + +export type PlaylistListResponse = BasePaginatedResponse | 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; + sync?: boolean; + }; + }; + comment?: string; + genres?: Genre[]; + name: string; + public?: boolean; +}; + +export type UpdatePlaylistQuery = { + id: string; +}; + +export type UpdatePlaylistResponse = null | undefined; + +type PlaylistListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +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 | null | undefined; diff --git a/src/shared/types/domain/search-domain-types.ts b/src/shared/types/domain/search-domain-types.ts new file mode 100644 index 000000000..bd73b0d18 --- /dev/null +++ b/src/shared/types/domain/search-domain-types.ts @@ -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; +}; diff --git a/src/shared/types/domain/server-domain-types.ts b/src/shared/types/domain/server-domain-types.ts new file mode 100644 index 000000000..49e44fc6b --- /dev/null +++ b/src/shared/types/domain/server-domain-types.ts @@ -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 + | null + | undefined; +export type ServerMusicFoldersResponse = ServerMusicFolder[]; diff --git a/src/shared/types/domain/shared-domain-types.ts b/src/shared/types/domain/shared-domain-types.ts new file mode 100644 index 000000000..9d167ec09 --- /dev/null +++ b/src/shared/types/domain/shared-domain-types.ts @@ -0,0 +1,4 @@ +export enum ListSortOrder { + ASC = 'ASC', + DESC = 'DESC', +} diff --git a/src/shared/types/domain/song-domain-types.ts b/src/shared/types/domain/song-domain-types.ts new file mode 100644 index 000000000..180bc402e --- /dev/null +++ b/src/shared/types/domain/song-domain-types.ts @@ -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; + 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; + trackNumber: number; + uniqueId: string; + updatedAt: string; + userFavorite: boolean; + userRating: null | number; +}; +export type SongListArgs = BaseEndpointArgs & { query: SongListQuery }; + +export interface SongListQuery extends BaseQuery { + _custom?: { + jellyfin?: Partial>; + navidrome?: Partial>; + }; + 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 | null | undefined; +type SongListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +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 | null | undefined; diff --git a/src/shared/types/domain/user-domain-types.ts b/src/shared/types/domain/user-domain-types.ts new file mode 100644 index 000000000..14e28bfbd --- /dev/null +++ b/src/shared/types/domain/user-domain-types.ts @@ -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 { + _custom?: { + navidrome?: { + owner_id?: string; + }; + }; + limit?: number; + searchTerm?: string; + startIndex: number; +} + +export type UserListResponse = BasePaginatedResponse | null | undefined; +type UserListSortMap = { + jellyfin: Record; + navidrome: Record; + subsonic: Record; +}; + +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; +}; diff --git a/src/shared/types/types.ts b/src/shared/types/types.ts index 5beb700aa..c0eac335e 100644 --- a/src/shared/types/types.ts +++ b/src/shared/types/types.ts @@ -2,15 +2,11 @@ import { AppRoute } from '@ts-rest/core'; import { ReactNode } from 'react'; import { Song } from 'src/main/features/core/lyrics/netease'; -import { - Album, - AlbumArtist, - Artist, - LibraryItem, - Playlist, - QueueSong, -} from '/@/shared/types/domain-types'; -import { ServerFeatures } from '/@/shared/types/features-types'; +import { LibraryItem, QueueSong } from '/@/shared/types/domain-types'; +import { Album } from '/@/shared/types/domain/album-domain-types'; +import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types'; +import { Playlist } from '/@/shared/types/domain/playlist-domain-types'; +import { ServerType } from '/@/shared/types/domain/server-domain-types'; export enum ListDisplayType { CARD = 'card', @@ -27,12 +23,6 @@ export enum Platform { WINDOWS = 'windows', } -export enum ServerType { - JELLYFIN = 'jellyfin', - NAVIDROME = 'navidrome', - SUBSONIC = 'subsonic', -} - export type CardRoute = { route: AppRoute | string; slugs?: RouteSlug[]; @@ -210,20 +200,6 @@ export type QueryBuilderRule = { 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 = { position?: number; repeat?: PlayerRepeat;