mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
improve domain types to better match OS, update normalizer functions
This commit is contained in:
@@ -1,42 +1,179 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { components } from './subsonic-schema.d';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { Genre, RelatedGenre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
|
||||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||||
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
import { formatDate } from '/@/shared/utils/format-date';
|
||||||
|
|
||||||
const getCoverArtUrl = (args: {
|
export const normalize = {
|
||||||
baseUrl: string | undefined;
|
album: (
|
||||||
coverArtId?: string;
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
credential: string | undefined;
|
server: ServerListItem,
|
||||||
size: number;
|
): Album => {
|
||||||
}) => {
|
const imageUrl = item.coverArt ? getCoverArtUrl(item.coverArt, server) : null;
|
||||||
const size = args.size ? args.size : 250;
|
|
||||||
|
|
||||||
if (!args.coverArtId || args.coverArtId.match('2a96cbd8b46e442fc41c2b86b821562f')) {
|
return {
|
||||||
return null;
|
_itemType: LibraryItem.ALBUM,
|
||||||
}
|
_serverId: server?.id || 'unknown',
|
||||||
|
_serverType: ServerType.SUBSONIC,
|
||||||
return (
|
artistName: item.artist || null,
|
||||||
`${args.baseUrl}/rest/getCoverArt.view` +
|
artists: getArtistList(item.artists, item.artistId, item.artist),
|
||||||
`?id=${args.coverArtId}` +
|
comment: null,
|
||||||
`&${args.credential}` +
|
createdDate: item.created,
|
||||||
'&v=1.13.0' +
|
discTitles: getDiscTitles(item),
|
||||||
'&c=Feishin' +
|
displayArtist: null,
|
||||||
`&size=${size}`
|
duration: getDuration(item.duration),
|
||||||
);
|
explicit: item.explicitStatus === 'explicit',
|
||||||
|
genres: getGenres(item),
|
||||||
|
id: item.id.toString(),
|
||||||
|
imagePlaceholderUrl: null,
|
||||||
|
imageUrl,
|
||||||
|
isCompilation: item.isCompilation || null,
|
||||||
|
mbzId: item.musicBrainzId || null,
|
||||||
|
mbzReleaseGroupId: null,
|
||||||
|
missing: null,
|
||||||
|
moods: getMoods(item),
|
||||||
|
name: item.name,
|
||||||
|
originalReleaseDate: getOriginalReleaseDate(item),
|
||||||
|
participants: {},
|
||||||
|
recordLabels: getRecordLabels(item),
|
||||||
|
releaseDate: getReleaseDate(item),
|
||||||
|
releaseTypes: getReleaseTypes(item),
|
||||||
|
releaseYear: item.year || null,
|
||||||
|
size: null,
|
||||||
|
songCount: item.songCount,
|
||||||
|
sortName: item.sortName || item.name,
|
||||||
|
tags: {},
|
||||||
|
updatedDate: null,
|
||||||
|
userFavorite: Boolean(item.starred),
|
||||||
|
userFavoriteDate: item.starred || null,
|
||||||
|
userLastPlayedDate: item.played || null,
|
||||||
|
userPlayCount: item.playCount ?? null,
|
||||||
|
userRating: item.userRating || null,
|
||||||
|
version: item.version || null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
albumArtist: (
|
||||||
|
item: components['schemas']['ArtistID3'] & components['schemas']['ArtistInfo2'],
|
||||||
|
server: ServerListItem,
|
||||||
|
): Artist => {
|
||||||
|
return {
|
||||||
|
_serverId: server?.id || 'unknown',
|
||||||
|
_serverType: ServerType.SUBSONIC,
|
||||||
|
albumCount: item.albumCount ? Number(item.albumCount) : 0,
|
||||||
|
biography: item.biography || null,
|
||||||
|
duration: null,
|
||||||
|
genres: [],
|
||||||
|
id: item.id.toString(),
|
||||||
|
imageUrl: item.coverArt ? getCoverArtUrl(item.coverArt.toString(), server) : null,
|
||||||
|
itemType: LibraryItem.ALBUM_ARTIST,
|
||||||
|
mbzId: item.musicBrainzId || null,
|
||||||
|
name: item.name,
|
||||||
|
playCount: null,
|
||||||
|
similarArtists: [],
|
||||||
|
songCount: null,
|
||||||
|
userFavorite: false,
|
||||||
|
userLastPlayedDate: null,
|
||||||
|
userRating: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
genre: (item: components['schemas']['Genre'], server: ServerListItem): Genre => {
|
||||||
|
return {
|
||||||
|
_itemType: LibraryItem.GENRE,
|
||||||
|
_serverId: server.id,
|
||||||
|
_serverType: ServerType.SUBSONIC,
|
||||||
|
albumCount: item.albumCount,
|
||||||
|
id: item.value,
|
||||||
|
imageUrl: null,
|
||||||
|
name: item.value,
|
||||||
|
songCount: item.songCount,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
playlist: (item: components['schemas']['Playlist'], server: ServerListItem): Playlist => {
|
||||||
|
return {
|
||||||
|
_itemType: LibraryItem.PLAYLIST,
|
||||||
|
_serverId: server.id,
|
||||||
|
_serverType: ServerType.SUBSONIC,
|
||||||
|
createdDate: item.created || null,
|
||||||
|
description: item.comment || null,
|
||||||
|
duration: getDuration(item.duration),
|
||||||
|
genres: [],
|
||||||
|
id: item.id.toString(),
|
||||||
|
imageUrl: item.coverArt ? getCoverArtUrl(item.coverArt.toString(), server) : null,
|
||||||
|
name: item.name,
|
||||||
|
owner: item.owner || null,
|
||||||
|
ownerId: item.owner || null,
|
||||||
|
public: item.public || null,
|
||||||
|
size: null,
|
||||||
|
songCount: item.songCount,
|
||||||
|
updatedDate: item.changed,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
song: (item: components['schemas']['Child'], server: ServerListItem): Song => {
|
||||||
|
return {
|
||||||
|
_itemType: LibraryItem.SONG,
|
||||||
|
_serverId: server.id,
|
||||||
|
_serverType: ServerType.SUBSONIC,
|
||||||
|
album: item.album || null,
|
||||||
|
albumArtistName: item.displayAlbumArtist || null,
|
||||||
|
albumArtists: getArtistList(item.albumArtists, item.artistId, item.artist),
|
||||||
|
albumId: item.albumId || null,
|
||||||
|
artistName: item.displayArtist || item.artist || null,
|
||||||
|
artists: getArtistList(item.artists, item.artistId, item.artist),
|
||||||
|
bitDepth: item.bitDepth || null,
|
||||||
|
bitRate: item.bitRate || null,
|
||||||
|
bpm: item.bpm || null,
|
||||||
|
channels: item.channelCount || null,
|
||||||
|
comment: item.comment || null,
|
||||||
|
composer: item.displayComposer || null,
|
||||||
|
container: item.contentType || null,
|
||||||
|
createdDate: item.created || null,
|
||||||
|
discNumber: item.discNumber || 1,
|
||||||
|
discSubtitle: null,
|
||||||
|
duration: getDuration(item.duration),
|
||||||
|
explicit: item.explicitStatus === 'explicit',
|
||||||
|
gain: getGainInfo(item),
|
||||||
|
genres: getGenres(item),
|
||||||
|
id: item.id.toString(),
|
||||||
|
imageUrl: item.coverArt ? getCoverArtUrl(item.coverArt, server) : null,
|
||||||
|
isCompilation: null,
|
||||||
|
isrc: item.isrc || [],
|
||||||
|
lyrics: null,
|
||||||
|
mbzId: item.musicBrainzId || null,
|
||||||
|
missing: false,
|
||||||
|
moods: getMoods(item),
|
||||||
|
name: item.title,
|
||||||
|
participants: getParticipants(item),
|
||||||
|
path: item.path || null,
|
||||||
|
peak: getPeakInfo(item),
|
||||||
|
playCount: item?.playCount || 0,
|
||||||
|
releaseDate: null,
|
||||||
|
releaseYear: item.year ?? null,
|
||||||
|
samplingRate: item.samplingRate || null,
|
||||||
|
size: item.size ?? 0,
|
||||||
|
sortName: item.sortName || item.title,
|
||||||
|
streamUrl: getStreamUrl(item.id, server),
|
||||||
|
tags: {},
|
||||||
|
trackNumber: item.track || 1,
|
||||||
|
updatedDate: null,
|
||||||
|
userFavorite: Boolean(item.starred),
|
||||||
|
userFavoriteDate: item.starred || null,
|
||||||
|
userLastPlayedDate: item.played || null,
|
||||||
|
userRating: item.userRating || null,
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getArtistList = (
|
function getArtistList(
|
||||||
artists?: typeof ssType._response.song._type.artists,
|
artists?: typeof ssType._response.song._type.artists,
|
||||||
artistId?: number | string,
|
artistId?: number | string,
|
||||||
artistName?: string,
|
artistName?: string,
|
||||||
) => {
|
) {
|
||||||
return artists
|
return artists
|
||||||
? artists.map((item) => ({
|
? artists.map((item) => ({
|
||||||
id: item.id.toString(),
|
id: item.id.toString(),
|
||||||
@@ -50,19 +187,94 @@ const getArtistList = (
|
|||||||
name: artistName || '',
|
name: artistName || '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
}
|
||||||
|
|
||||||
const getParticipants = (
|
function getCoverArtUrl(id: string, server: ServerListItem) {
|
||||||
|
return (
|
||||||
|
`${server.url}/rest/getCoverArt.view` +
|
||||||
|
`?id=${id}` +
|
||||||
|
`&${server.credential}` +
|
||||||
|
'&v=1.16.1' +
|
||||||
|
'&c=Feishin'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiscTitles(
|
||||||
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
|
) {
|
||||||
|
return (item.discTitles || []).map((discTitle) => ({
|
||||||
|
disc: discTitle.disc,
|
||||||
|
title: discTitle.title,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDuration(duration?: number) {
|
||||||
|
// Transform from seconds to milliseconds
|
||||||
|
return duration ? duration * 1000 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGainInfo(item: components['schemas']['Child']) {
|
||||||
|
return item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
|
||||||
|
? {
|
||||||
|
album: item.replayGain.albumGain,
|
||||||
|
track: item.replayGain.trackGain,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGenres(
|
||||||
item:
|
item:
|
||||||
| z.infer<typeof ssType._response.album>
|
| components['schemas']['AlbumID3']
|
||||||
| z.infer<typeof ssType._response.albumListEntry>
|
| components['schemas']['AlbumID3WithSongs']
|
||||||
| z.infer<typeof ssType._response.song>,
|
| components['schemas']['Child'],
|
||||||
) => {
|
): RelatedGenre[] {
|
||||||
let participants: null | Record<string, RelatedArtist[]> = null;
|
if (item.genres) {
|
||||||
|
return item.genres.map((genre) => ({
|
||||||
|
id: genre.name,
|
||||||
|
imageUrl: null,
|
||||||
|
name: genre.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.genre) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: item.genre,
|
||||||
|
imageUrl: null,
|
||||||
|
name: item.genre,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMoods(
|
||||||
|
item:
|
||||||
|
| components['schemas']['AlbumID3']
|
||||||
|
| components['schemas']['AlbumID3WithSongs']
|
||||||
|
| components['schemas']['Child'],
|
||||||
|
) {
|
||||||
|
return (item.moods || []).map((mood) => ({
|
||||||
|
id: mood,
|
||||||
|
name: mood,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginalReleaseDate(
|
||||||
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
|
) {
|
||||||
|
return item.originalReleaseDate
|
||||||
|
? formatDate.toUTCDate(
|
||||||
|
`${item.originalReleaseDate.year}-${item.originalReleaseDate.month}-${item.originalReleaseDate.day}`,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParticipants(item: components['schemas']['Child']) {
|
||||||
|
const participants: Record<string, RelatedArtist[]> = {};
|
||||||
|
|
||||||
if (item.contributors) {
|
if (item.contributors) {
|
||||||
participants = {};
|
|
||||||
|
|
||||||
for (const contributor of item.contributors) {
|
for (const contributor of item.contributors) {
|
||||||
const artist = {
|
const artist = {
|
||||||
id: contributor.artist.id?.toString() || '',
|
id: contributor.artist.id?.toString() || '',
|
||||||
@@ -74,248 +286,54 @@ const getParticipants = (
|
|||||||
? `${contributor.role} (${contributor.subRole})`
|
? `${contributor.role} (${contributor.subRole})`
|
||||||
: contributor.role;
|
: contributor.role;
|
||||||
|
|
||||||
if (role in participants) {
|
if (!participants[role]) {
|
||||||
participants[role].push(artist);
|
participants[role] = [];
|
||||||
} else {
|
|
||||||
participants[role] = [artist];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
participants[role].push(artist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return participants;
|
return participants;
|
||||||
};
|
}
|
||||||
|
|
||||||
const getGenres = (
|
function getPeakInfo(item: components['schemas']['Child']) {
|
||||||
item:
|
return item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
|
||||||
| z.infer<typeof ssType._response.album>
|
|
||||||
| z.infer<typeof ssType._response.albumListEntry>
|
|
||||||
| z.infer<typeof ssType._response.song>,
|
|
||||||
): Genre[] => {
|
|
||||||
return item.genres
|
|
||||||
? item.genres.map((genre) => ({
|
|
||||||
id: genre.name,
|
|
||||||
imageUrl: null,
|
|
||||||
itemType: LibraryItem.GENRE,
|
|
||||||
name: genre.name,
|
|
||||||
}))
|
|
||||||
: item.genre
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: item.genre,
|
|
||||||
imageUrl: null,
|
|
||||||
itemType: LibraryItem.GENRE,
|
|
||||||
name: item.genre,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeSong = (
|
|
||||||
item: z.infer<typeof ssType._response.song>,
|
|
||||||
server: null | ServerListItem,
|
|
||||||
size?: number,
|
|
||||||
): QueueSong => {
|
|
||||||
const imageUrl =
|
|
||||||
getCoverArtUrl({
|
|
||||||
baseUrl: server?.url,
|
|
||||||
coverArtId: item.coverArt?.toString(),
|
|
||||||
credential: server?.credential,
|
|
||||||
size: size || 300,
|
|
||||||
}) || null;
|
|
||||||
|
|
||||||
const streamUrl = `${server?.url}/rest/stream.view?id=${item.id}&v=1.13.0&c=Feishin&${server?.credential}`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
album: item.album || '',
|
|
||||||
albumArtists: getArtistList(item.albumArtists, item.artistId, item.artist),
|
|
||||||
albumId: item.albumId?.toString() || '',
|
|
||||||
artistName: item.artist || '',
|
|
||||||
artists: getArtistList(item.artists, item.artistId, item.artist),
|
|
||||||
bitDepth: item.bitDepth || null,
|
|
||||||
bitRate: item.bitRate || 0,
|
|
||||||
bpm: item.bpm || null,
|
|
||||||
channels: item.channelCount || null,
|
|
||||||
comment: null,
|
|
||||||
compilation: null,
|
|
||||||
container: item.contentType,
|
|
||||||
createdAt: item.created,
|
|
||||||
discNumber: item.discNumber || 1,
|
|
||||||
discSubtitle: null,
|
|
||||||
duration: item.duration ? item.duration * 1000 : 0,
|
|
||||||
gain:
|
|
||||||
item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
|
|
||||||
? {
|
|
||||||
album: item.replayGain.albumGain,
|
|
||||||
track: item.replayGain.trackGain,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
genres: getGenres(item),
|
|
||||||
id: item.id.toString(),
|
|
||||||
imagePlaceholderUrl: null,
|
|
||||||
imageUrl,
|
|
||||||
itemType: LibraryItem.SONG,
|
|
||||||
lastPlayedAt: null,
|
|
||||||
lyrics: null,
|
|
||||||
name: item.title,
|
|
||||||
participants: getParticipants(item),
|
|
||||||
path: item.path,
|
|
||||||
peak:
|
|
||||||
item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
|
|
||||||
? {
|
? {
|
||||||
album: item.replayGain.albumPeak,
|
album: item.replayGain.albumPeak,
|
||||||
track: item.replayGain.trackPeak,
|
track: item.replayGain.trackPeak,
|
||||||
}
|
}
|
||||||
: null,
|
: null;
|
||||||
playCount: item?.playCount || 0,
|
}
|
||||||
releaseDate: null,
|
|
||||||
releaseYear: item.year ? String(item.year) : null,
|
|
||||||
sampleRate: item.samplingRate || null,
|
|
||||||
serverId: server?.id || 'unknown',
|
|
||||||
serverType: ServerType.SUBSONIC,
|
|
||||||
size: item.size,
|
|
||||||
streamUrl,
|
|
||||||
tags: null,
|
|
||||||
trackNumber: item.track || 1,
|
|
||||||
uniqueId: nanoid(),
|
|
||||||
updatedAt: '',
|
|
||||||
userFavorite: item.starred || false,
|
|
||||||
userRating: item.userRating || null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeAlbumArtist = (
|
function getRecordLabels(
|
||||||
item:
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
| z.infer<typeof ssType._response.albumArtist>
|
) {
|
||||||
| z.infer<typeof ssType._response.artistListEntry>,
|
return (item.recordLabels || []).map((recordLabel) => ({
|
||||||
server: null | ServerListItem,
|
id: recordLabel.name,
|
||||||
imageSize?: number,
|
name: recordLabel.name,
|
||||||
): AlbumArtist => {
|
}));
|
||||||
const imageUrl =
|
}
|
||||||
getCoverArtUrl({
|
|
||||||
baseUrl: server?.url,
|
|
||||||
coverArtId: item.coverArt?.toString(),
|
|
||||||
credential: server?.credential,
|
|
||||||
size: imageSize || 100,
|
|
||||||
}) || null;
|
|
||||||
|
|
||||||
return {
|
function getReleaseDate(
|
||||||
albumCount: item.albumCount ? Number(item.albumCount) : 0,
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
backgroundImageUrl: null,
|
) {
|
||||||
biography: null,
|
return item.releaseDate
|
||||||
duration: null,
|
? formatDate.toUTCDate(
|
||||||
genres: [],
|
`${item.releaseDate.year}-${item.releaseDate.month}-${item.releaseDate.day}`,
|
||||||
id: item.id.toString(),
|
)
|
||||||
imageUrl,
|
: null;
|
||||||
itemType: LibraryItem.ALBUM_ARTIST,
|
}
|
||||||
lastPlayedAt: null,
|
|
||||||
mbz: null,
|
|
||||||
name: item.name,
|
|
||||||
playCount: null,
|
|
||||||
serverId: server?.id || 'unknown',
|
|
||||||
serverType: ServerType.SUBSONIC,
|
|
||||||
similarArtists: [],
|
|
||||||
songCount: null,
|
|
||||||
userFavorite: false,
|
|
||||||
userRating: null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeAlbum = (
|
function getReleaseTypes(
|
||||||
item: z.infer<typeof ssType._response.album> | z.infer<typeof ssType._response.albumListEntry>,
|
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
|
||||||
server: null | ServerListItem,
|
) {
|
||||||
imageSize?: number,
|
return (item.releaseTypes || []).map((releaseType) => ({
|
||||||
): Album => {
|
id: releaseType,
|
||||||
const imageUrl =
|
name: releaseType,
|
||||||
getCoverArtUrl({
|
}));
|
||||||
baseUrl: server?.url,
|
}
|
||||||
coverArtId: item.coverArt?.toString(),
|
|
||||||
credential: server?.credential,
|
|
||||||
size: imageSize || 300,
|
|
||||||
}) || null;
|
|
||||||
|
|
||||||
return {
|
function getStreamUrl(id: string, server: ServerListItem) {
|
||||||
albumArtist: item.artist,
|
return `${server.url}/rest/stream.view?id=${id}&v=1.16.1&c=Feishin&${server.credential}`;
|
||||||
albumArtists: getArtistList(item.artists, item.artistId, item.artist),
|
}
|
||||||
artists: [],
|
|
||||||
backdropImageUrl: null,
|
|
||||||
comment: null,
|
|
||||||
createdAt: item.created,
|
|
||||||
duration: item.duration * 1000,
|
|
||||||
genres: getGenres(item),
|
|
||||||
id: item.id.toString(),
|
|
||||||
imagePlaceholderUrl: null,
|
|
||||||
imageUrl,
|
|
||||||
isCompilation: null,
|
|
||||||
itemType: LibraryItem.ALBUM,
|
|
||||||
lastPlayedAt: null,
|
|
||||||
mbzId: null,
|
|
||||||
name: item.name,
|
|
||||||
originalDate: null,
|
|
||||||
participants: getParticipants(item),
|
|
||||||
playCount: null,
|
|
||||||
releaseDate: item.year ? new Date(Date.UTC(item.year, 0, 1)).toISOString() : null,
|
|
||||||
releaseYear: item.year ? Number(item.year) : null,
|
|
||||||
serverId: server?.id || 'unknown',
|
|
||||||
serverType: ServerType.SUBSONIC,
|
|
||||||
size: null,
|
|
||||||
songCount: item.songCount,
|
|
||||||
songs:
|
|
||||||
(item as z.infer<typeof ssType._response.album>).song?.map((song) =>
|
|
||||||
normalizeSong(song, server),
|
|
||||||
) || [],
|
|
||||||
tags: null,
|
|
||||||
uniqueId: nanoid(),
|
|
||||||
updatedAt: item.created,
|
|
||||||
userFavorite: item.starred || false,
|
|
||||||
userRating: item.userRating || null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizePlaylist = (
|
|
||||||
item:
|
|
||||||
| z.infer<typeof ssType._response.playlist>
|
|
||||||
| z.infer<typeof ssType._response.playlistListEntry>,
|
|
||||||
server: null | ServerListItem,
|
|
||||||
): Playlist => {
|
|
||||||
return {
|
|
||||||
description: item.comment || null,
|
|
||||||
duration: item.duration * 1000,
|
|
||||||
genres: [],
|
|
||||||
id: item.id.toString(),
|
|
||||||
imagePlaceholderUrl: null,
|
|
||||||
imageUrl: getCoverArtUrl({
|
|
||||||
baseUrl: server?.url,
|
|
||||||
coverArtId: item.coverArt?.toString(),
|
|
||||||
credential: server?.credential,
|
|
||||||
size: 300,
|
|
||||||
}),
|
|
||||||
itemType: LibraryItem.PLAYLIST,
|
|
||||||
name: item.name,
|
|
||||||
owner: item.owner,
|
|
||||||
ownerId: item.owner,
|
|
||||||
public: item.public,
|
|
||||||
serverId: server?.id || 'unknown',
|
|
||||||
serverType: ServerType.SUBSONIC,
|
|
||||||
size: null,
|
|
||||||
songCount: item.songCount,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeGenre = (item: z.infer<typeof ssType._response.genre>): Genre => {
|
|
||||||
return {
|
|
||||||
albumCount: item.albumCount,
|
|
||||||
id: item.value,
|
|
||||||
imageUrl: null,
|
|
||||||
itemType: LibraryItem.GENRE,
|
|
||||||
name: item.value,
|
|
||||||
songCount: item.songCount,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ssNormalize = {
|
|
||||||
album: normalizeAlbum,
|
|
||||||
albumArtist: normalizeAlbumArtist,
|
|
||||||
genre: normalizeGenre,
|
|
||||||
playlist: normalizePlaylist,
|
|
||||||
song: normalizeSong,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,9 +8,17 @@ import { NDAlbumListSort } from '/@/shared/api/navidrome.types';
|
|||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { RelatedGenre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
import {
|
||||||
|
LibraryItem,
|
||||||
|
ListSortOrder,
|
||||||
|
Mood,
|
||||||
|
Participants,
|
||||||
|
RecordLabel,
|
||||||
|
ReleaseType,
|
||||||
|
Tags,
|
||||||
|
} from '/@/shared/types/domain/shared-domain-types';
|
||||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
export enum AlbumListSortOptions {
|
export enum AlbumListSortOptions {
|
||||||
@@ -150,52 +158,45 @@ export const albumListSortMap: AlbumListSortMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Album = {
|
export type Album = {
|
||||||
albumArtist: string;
|
_itemType: LibraryItem.ALBUM;
|
||||||
albumArtists: RelatedArtist[];
|
_serverId: string;
|
||||||
|
_serverType: ServerType;
|
||||||
|
artistName: null | string;
|
||||||
artists: RelatedArtist[];
|
artists: RelatedArtist[];
|
||||||
backdropImageUrl: null | string;
|
|
||||||
comment: null | string;
|
comment: null | string;
|
||||||
createdDate: string;
|
createdDate: null | string;
|
||||||
description: null | string;
|
discTitles: DiscTitle[];
|
||||||
discTitles: {
|
|
||||||
disc: number;
|
|
||||||
title: string;
|
|
||||||
}[];
|
|
||||||
displayArtist: null | string;
|
displayArtist: null | string;
|
||||||
duration: null | number;
|
duration: null | number;
|
||||||
genres: Genre[];
|
explicit: boolean | null;
|
||||||
|
genres: RelatedGenre[];
|
||||||
id: string;
|
id: string;
|
||||||
imagePlaceholderUrl: null | string;
|
imagePlaceholderUrl: null | string;
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
isCompilation: boolean | null;
|
isCompilation: boolean | null;
|
||||||
itemType: LibraryItem.ALBUM;
|
mbzId: null | string;
|
||||||
mbzAlbumId: null | string;
|
|
||||||
mbzReleaseGroupId: null | string;
|
mbzReleaseGroupId: null | string;
|
||||||
missing: boolean;
|
missing: boolean | null;
|
||||||
|
moods: Mood[];
|
||||||
name: string;
|
name: string;
|
||||||
originalReleaseDate: null | string;
|
originalReleaseDate: null | string;
|
||||||
participants: null | Record<string, RelatedArtist[]>;
|
participants: Participants;
|
||||||
|
recordLabels: RecordLabel[];
|
||||||
releaseDate: null | string;
|
releaseDate: null | string;
|
||||||
releaseTypes: {
|
releaseTypes: ReleaseType[];
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}[];
|
|
||||||
releaseYear: null | number;
|
releaseYear: null | number;
|
||||||
serverId: string;
|
|
||||||
serverType: ServerType;
|
|
||||||
size: null | number;
|
size: null | number;
|
||||||
songCount: null | number;
|
songCount: null | number;
|
||||||
songs?: Song[];
|
|
||||||
sortName: string;
|
sortName: string;
|
||||||
tags: Record<string, string[]>;
|
tags: Tags;
|
||||||
uniqueId: string;
|
updatedDate: null | string;
|
||||||
updatedDate: string;
|
|
||||||
userFavorite: boolean;
|
userFavorite: boolean;
|
||||||
userFavoriteDate: null | string;
|
userFavoriteDate: null | string;
|
||||||
userLastPlayedDate: null | string;
|
userLastPlayedDate: null | string;
|
||||||
userPlayCount: null | number;
|
userPlayCount: null | number;
|
||||||
userRating: null | number;
|
userRating: null | number;
|
||||||
} & { songs?: Song[] };
|
version: null | string;
|
||||||
|
};
|
||||||
|
|
||||||
export type AlbumDetailQuery = { id: string };
|
export type AlbumDetailQuery = { id: string };
|
||||||
|
|
||||||
@@ -208,6 +209,13 @@ export type AlbumInfo = {
|
|||||||
notes: null | string;
|
notes: null | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AlbumWithSongs = Album & { songs: Song[] };
|
||||||
|
|
||||||
|
export type DiscTitle = {
|
||||||
|
disc: number;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: ListSortOrder) => {
|
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: ListSortOrder) => {
|
||||||
let results = albums;
|
let results = albums;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
|||||||
import { NDAlbumArtistListSort } from '/@/shared/api/navidrome.types';
|
import { NDAlbumArtistListSort } from '/@/shared/api/navidrome.types';
|
||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { RelatedGenre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||||
|
|
||||||
@@ -31,45 +31,26 @@ export const ArtistListSortOptionsLabels = {
|
|||||||
[ArtistListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
[ArtistListSortOptions.TRACK_COUNT]: i18n.t('filter.trackCount'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AlbumArtist = {
|
export type Artist = {
|
||||||
|
_serverId: string;
|
||||||
|
_serverType: ServerType;
|
||||||
albumCount: null | number;
|
albumCount: null | number;
|
||||||
backgroundImageUrl: null | string;
|
|
||||||
biography: null | string;
|
biography: null | string;
|
||||||
duration: null | number;
|
duration: null | number;
|
||||||
genres: Genre[];
|
genres: RelatedGenre[];
|
||||||
id: string;
|
id: string;
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
itemType: LibraryItem.ALBUM_ARTIST;
|
itemType: LibraryItem.ALBUM_ARTIST;
|
||||||
lastPlayedAt: null | string;
|
mbzId: null | string;
|
||||||
mbz: null | string;
|
|
||||||
name: string;
|
name: string;
|
||||||
playCount: null | number;
|
playCount: null | number;
|
||||||
serverId: string;
|
|
||||||
serverType: ServerType;
|
|
||||||
similarArtists: null | RelatedArtist[];
|
similarArtists: null | RelatedArtist[];
|
||||||
songCount: null | number;
|
songCount: null | number;
|
||||||
userFavorite: boolean;
|
userFavorite: boolean;
|
||||||
|
userLastPlayedDate: null | string;
|
||||||
userRating: null | number;
|
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 = {
|
export type RelatedArtist = {
|
||||||
id: string;
|
id: string;
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
@@ -142,7 +123,7 @@ export type AlbumArtistDetailQuery = { id: string };
|
|||||||
|
|
||||||
export type AlbumArtistDetailRequest = { query: AlbumArtistDetailQuery };
|
export type AlbumArtistDetailRequest = { query: AlbumArtistDetailQuery };
|
||||||
|
|
||||||
export type AlbumArtistDetailResponse = AlbumArtist | null;
|
export type AlbumArtistDetailResponse = Artist | null;
|
||||||
|
|
||||||
export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
|
export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
|
||||||
_custom?: {
|
_custom?: {
|
||||||
@@ -158,7 +139,7 @@ export interface ArtistListQuery extends BaseQuery<ArtistListSort> {
|
|||||||
|
|
||||||
export type ArtistListRequest = { query: ArtistListQuery };
|
export type ArtistListRequest = { query: ArtistListQuery };
|
||||||
|
|
||||||
export type ArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
|
export type ArtistListResponse = BasePaginatedResponse<Artist[]> | null | undefined;
|
||||||
type ArtistListSortMap = {
|
type ArtistListSortMap = {
|
||||||
jellyfin: Record<ArtistListSort, JFArtistListSort | undefined>;
|
jellyfin: Record<ArtistListSort, JFArtistListSort | undefined>;
|
||||||
navidrome: Record<ArtistListSort, undefined>;
|
navidrome: Record<ArtistListSort, undefined>;
|
||||||
@@ -233,7 +214,7 @@ export interface AlbumArtistListQuery extends BaseQuery<AlbumArtistListSort> {
|
|||||||
|
|
||||||
export type AlbumArtistListRequest = { query: AlbumArtistListQuery };
|
export type AlbumArtistListRequest = { query: AlbumArtistListQuery };
|
||||||
|
|
||||||
export type AlbumArtistListResponse = BasePaginatedResponse<AlbumArtist[]> | null | undefined;
|
export type AlbumArtistListResponse = BasePaginatedResponse<Artist[]> | null | undefined;
|
||||||
|
|
||||||
export type ArtistInfoQuery = {
|
export type ArtistInfoQuery = {
|
||||||
artistId: string;
|
artistId: string;
|
||||||
@@ -244,7 +225,7 @@ export type ArtistInfoQuery = {
|
|||||||
export type ArtistInfoRequest = { query: ArtistInfoQuery };
|
export type ArtistInfoRequest = { query: ArtistInfoQuery };
|
||||||
|
|
||||||
export const sortAlbumArtistList = (
|
export const sortAlbumArtistList = (
|
||||||
artists: AlbumArtist[],
|
artists: Artist[],
|
||||||
sortBy: AlbumArtistListSort | ArtistListSort,
|
sortBy: AlbumArtistListSort | ArtistListSort,
|
||||||
sortOrder: ListSortOrder,
|
sortOrder: ListSortOrder,
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import i18n from '/@/i18n/i18n';
|
|||||||
import { JFGenreListSort } from '/@/shared/api/jellyfin.types';
|
import { JFGenreListSort } from '/@/shared/api/jellyfin.types';
|
||||||
import { NDGenreListSort } from '/@/shared/api/navidrome.types';
|
import { NDGenreListSort } from '/@/shared/api/navidrome.types';
|
||||||
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
||||||
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||||
import { UserListSort } from '/@/shared/types/domain/user-domain-types';
|
import { UserListSort } from '/@/shared/types/domain/user-domain-types';
|
||||||
|
|
||||||
@@ -18,12 +19,14 @@ export const GenreListSortOptionsLabels = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Genre = {
|
export type Genre = {
|
||||||
albumCount?: number;
|
_itemType: LibraryItem.GENRE;
|
||||||
|
_serverId: string;
|
||||||
|
_serverType: ServerType;
|
||||||
|
albumCount: null | number;
|
||||||
id: string;
|
id: string;
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
itemType: LibraryItem.GENRE;
|
|
||||||
name: string;
|
name: string;
|
||||||
songCount?: number;
|
songCount: null | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GenreListQuery extends BaseQuery<GenreListSort> {
|
export interface GenreListQuery extends BaseQuery<GenreListSort> {
|
||||||
@@ -41,6 +44,12 @@ export type GenreListRequest = { query: GenreListQuery };
|
|||||||
|
|
||||||
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
|
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
|
||||||
|
|
||||||
|
export type RelatedGenre = {
|
||||||
|
id: string;
|
||||||
|
imageUrl: null | string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
type GenreListSortMap = {
|
type GenreListSortMap = {
|
||||||
jellyfin: Record<GenreListSort, JFGenreListSort | undefined>;
|
jellyfin: Record<GenreListSort, JFGenreListSort | undefined>;
|
||||||
navidrome: Record<GenreListSort, NDGenreListSort | undefined>;
|
navidrome: Record<GenreListSort, NDGenreListSort | undefined>;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
BasePaginatedResponse,
|
BasePaginatedResponse,
|
||||||
BaseQuery,
|
BaseQuery,
|
||||||
} from '/@/shared/types/adapter/api-controller-types';
|
} from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { Genre, RelatedGenre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||||
import { Song, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
import { Song, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||||
@@ -89,24 +89,26 @@ export type DeletePlaylistRequest = {
|
|||||||
export type DeletePlaylistResponse = null | undefined;
|
export type DeletePlaylistResponse = null | undefined;
|
||||||
|
|
||||||
export type Playlist = {
|
export type Playlist = {
|
||||||
|
_itemType: LibraryItem.PLAYLIST;
|
||||||
|
_serverId: string;
|
||||||
|
_serverType: ServerType;
|
||||||
|
createdDate: null | string;
|
||||||
description: null | string;
|
description: null | string;
|
||||||
duration: null | number;
|
duration: null | number;
|
||||||
genres: Genre[];
|
genres: RelatedGenre[];
|
||||||
id: string;
|
id: string;
|
||||||
imagePlaceholderUrl: null | string;
|
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
itemType: LibraryItem.PLAYLIST;
|
|
||||||
name: string;
|
name: string;
|
||||||
owner: null | string;
|
owner: null | string;
|
||||||
ownerId: null | string;
|
ownerId: null | string;
|
||||||
public: boolean | null;
|
public: boolean | null;
|
||||||
rules?: null | Record<string, any>;
|
rules?: null | Record<string, any>;
|
||||||
serverId: string;
|
|
||||||
serverType: ServerType;
|
|
||||||
size: null | number;
|
size: null | number;
|
||||||
songCount: null | number;
|
songCount: null | number;
|
||||||
sync?: boolean | null;
|
sync?: boolean | null;
|
||||||
|
updatedDate: null | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface PlaylistListQuery extends BaseQuery<PlaylistListSort> {
|
export interface PlaylistListQuery extends BaseQuery<PlaylistListSort> {
|
||||||
_custom?: {
|
_custom?: {
|
||||||
jellyfin?: Partial<z.infer<typeof jfType._parameters.playlistList>>;
|
jellyfin?: Partial<z.infer<typeof jfType._parameters.playlistList>>;
|
||||||
@@ -121,6 +123,10 @@ export type PlaylistListRequest = { query: PlaylistListQuery };
|
|||||||
|
|
||||||
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
|
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
|
||||||
|
|
||||||
|
export type PlaylistSong = Song & {
|
||||||
|
playlistItemId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type RemoveFromPlaylistQuery = {
|
export type RemoveFromPlaylistQuery = {
|
||||||
id: string;
|
id: string;
|
||||||
songId: string[];
|
songId: string[];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
|
|
||||||
export type SearchAlbumArtistsQuery = {
|
export type SearchAlbumArtistsQuery = {
|
||||||
@@ -30,7 +30,7 @@ export type SearchQuery = {
|
|||||||
export type SearchRequest = { query: SearchQuery };
|
export type SearchRequest = { query: SearchQuery };
|
||||||
|
|
||||||
export type SearchResponse = {
|
export type SearchResponse = {
|
||||||
albumArtists: AlbumArtist[];
|
albumArtists: Artist[];
|
||||||
albums: Album[];
|
albums: Album[];
|
||||||
songs: Song[];
|
songs: Song[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { JFSortOrder } from '/@/shared/api/jellyfin.types';
|
import { JFSortOrder } from '/@/shared/api/jellyfin.types';
|
||||||
import { NDSortOrder } from '/@/shared/api/navidrome.types';
|
import { NDSortOrder } from '/@/shared/api/navidrome.types';
|
||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist, Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
||||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import { Song } from '/@/shared/types/domain/song-domain-types';
|
import { Song } from '/@/shared/types/domain/song-domain-types';
|
||||||
@@ -19,15 +19,9 @@ export enum ListSortOrder {
|
|||||||
DESC = 'DESC',
|
DESC = 'DESC',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AnyLibraryItem = Album | AlbumArtist | Artist | Playlist | QueueSong | Song;
|
export type AnyLibraryItem = Album | Artist | Artist | Playlist | QueueSong | Song;
|
||||||
|
|
||||||
export type AnyLibraryItems =
|
export type AnyLibraryItems = Album[] | Artist[] | Artist[] | Playlist[] | QueueSong[] | Song[];
|
||||||
| Album[]
|
|
||||||
| AlbumArtist[]
|
|
||||||
| Artist[]
|
|
||||||
| Playlist[]
|
|
||||||
| QueueSong[]
|
|
||||||
| Song[];
|
|
||||||
type SortOrderMap = {
|
type SortOrderMap = {
|
||||||
jellyfin: Record<ListSortOrder, JFSortOrder>;
|
jellyfin: Record<ListSortOrder, JFSortOrder>;
|
||||||
navidrome: Record<ListSortOrder, NDSortOrder>;
|
navidrome: Record<ListSortOrder, NDSortOrder>;
|
||||||
@@ -56,3 +50,22 @@ export type FontData = {
|
|||||||
postscriptName: string;
|
postscriptName: string;
|
||||||
style: string;
|
style: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Mood = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Participants = Record<string, RelatedArtist[]>;
|
||||||
|
|
||||||
|
export type RecordLabel = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReleaseType = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Tags = Record<string, string[]>;
|
||||||
|
|||||||
@@ -8,10 +8,16 @@ import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
|||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||||
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
import { BasePaginatedResponse, BaseQuery } from '/@/shared/types/adapter/api-controller-types';
|
||||||
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { Genre } from '/@/shared/types/domain/genre-domain-types';
|
import { RelatedGenre } from '/@/shared/types/domain/genre-domain-types';
|
||||||
import { Played, QueueSong } from '/@/shared/types/domain/player-domain-types';
|
import { Played, QueueSong } from '/@/shared/types/domain/player-domain-types';
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
import {
|
||||||
|
LibraryItem,
|
||||||
|
ListSortOrder,
|
||||||
|
Mood,
|
||||||
|
Participants,
|
||||||
|
Tags,
|
||||||
|
} from '/@/shared/types/domain/shared-domain-types';
|
||||||
|
|
||||||
export enum SongListSortOptions {
|
export enum SongListSortOptions {
|
||||||
ALBUM = 'album',
|
ALBUM = 'album',
|
||||||
@@ -77,46 +83,54 @@ export enum SongListSort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Song = {
|
export type Song = {
|
||||||
|
_itemType: LibraryItem.SONG;
|
||||||
|
_serverId: string;
|
||||||
|
_serverType: ServerType;
|
||||||
album: null | string;
|
album: null | string;
|
||||||
|
albumArtistName: null | string;
|
||||||
albumArtists: RelatedArtist[];
|
albumArtists: RelatedArtist[];
|
||||||
albumId: string;
|
albumId: null | string;
|
||||||
artistName: string;
|
artistName: null | string;
|
||||||
artists: RelatedArtist[];
|
artists: RelatedArtist[];
|
||||||
bitRate: number;
|
bitDepth: null | number;
|
||||||
|
bitRate: null | number;
|
||||||
bpm: null | number;
|
bpm: null | number;
|
||||||
channels: null | number;
|
channels: null | number;
|
||||||
comment: null | string;
|
comment: null | string;
|
||||||
compilation: boolean | null;
|
composer: null | string;
|
||||||
container: null | string;
|
container: null | string;
|
||||||
createdAt: string;
|
createdDate: null | string;
|
||||||
discNumber: number;
|
discNumber: number;
|
||||||
discSubtitle: null | string;
|
discSubtitle: null | string;
|
||||||
duration: number;
|
duration: number;
|
||||||
|
explicit: boolean | null;
|
||||||
gain: GainInfo | null;
|
gain: GainInfo | null;
|
||||||
genres: Genre[];
|
genres: RelatedGenre[];
|
||||||
id: string;
|
id: string;
|
||||||
imagePlaceholderUrl: null | string;
|
|
||||||
imageUrl: null | string;
|
imageUrl: null | string;
|
||||||
itemType: LibraryItem.SONG;
|
isCompilation: boolean | null;
|
||||||
lastPlayedAt: null | string;
|
isrc: string[];
|
||||||
lyrics: null | string;
|
lyrics: null | string;
|
||||||
|
mbzId: null | string;
|
||||||
|
missing: boolean | null;
|
||||||
|
moods: Mood[];
|
||||||
name: string;
|
name: string;
|
||||||
participants: null | Record<string, RelatedArtist[]>;
|
participants: Participants;
|
||||||
path: null | string;
|
path: null | string;
|
||||||
peak: GainInfo | null;
|
peak: GainInfo | null;
|
||||||
playCount: number;
|
playCount: number;
|
||||||
playlistItemId?: string;
|
|
||||||
releaseDate: null | string;
|
releaseDate: null | string;
|
||||||
releaseYear: null | string;
|
releaseYear: null | number;
|
||||||
serverId: string;
|
samplingRate: null | number;
|
||||||
serverType: ServerType;
|
|
||||||
size: number;
|
size: number;
|
||||||
|
sortName: string;
|
||||||
streamUrl: string;
|
streamUrl: string;
|
||||||
tags: null | Record<string, string[]>;
|
tags: Tags;
|
||||||
trackNumber: number;
|
trackNumber: number;
|
||||||
uniqueId: string;
|
updatedDate: null | string;
|
||||||
updatedAt: string;
|
|
||||||
userFavorite: boolean;
|
userFavorite: boolean;
|
||||||
|
userFavoriteDate: null | string;
|
||||||
|
userLastPlayedDate: null | string;
|
||||||
userRating: null | number;
|
userRating: null | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ReactNode } from 'react';
|
|||||||
import { Song } from 'src/main/features/core/lyrics/netease';
|
import { Song } from 'src/main/features/core/lyrics/netease';
|
||||||
|
|
||||||
import { Album } from '/@/shared/types/domain/album-domain-types';
|
import { Album } from '/@/shared/types/domain/album-domain-types';
|
||||||
import { AlbumArtist, Artist } from '/@/shared/types/domain/artist-domain-types';
|
import { Artist } from '/@/shared/types/domain/artist-domain-types';
|
||||||
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
|
||||||
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
|
||||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||||
@@ -159,7 +159,7 @@ export enum TableColumn {
|
|||||||
|
|
||||||
export type GridCardData = {
|
export type GridCardData = {
|
||||||
cardControls: any;
|
cardControls: any;
|
||||||
cardRows: CardRow<Album | AlbumArtist | Artist | Playlist | Song>[];
|
cardRows: CardRow<Album | Artist | Artist | Playlist | Song>[];
|
||||||
columnCount: number;
|
columnCount: number;
|
||||||
display: ListDisplayType;
|
display: ListDisplayType;
|
||||||
handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
|
handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user