2023-04-24 01:21:29 -07:00
|
|
|
import { nanoid } from 'nanoid';
|
|
|
|
|
import { z } from 'zod';
|
|
|
|
|
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
|
2024-02-17 00:57:10 -08:00
|
|
|
import {
|
|
|
|
|
QueueSong,
|
|
|
|
|
LibraryItem,
|
|
|
|
|
AlbumArtist,
|
|
|
|
|
Album,
|
|
|
|
|
ServerListItem,
|
|
|
|
|
ServerType,
|
2024-09-26 04:23:08 +00:00
|
|
|
Playlist,
|
|
|
|
|
Genre,
|
2024-02-17 00:57:10 -08:00
|
|
|
} from '/@/renderer/api/types';
|
2023-04-24 01:21:29 -07:00
|
|
|
|
|
|
|
|
const getCoverArtUrl = (args: {
|
2023-07-01 19:10:05 -07:00
|
|
|
baseUrl: string | undefined;
|
|
|
|
|
coverArtId?: string;
|
|
|
|
|
credential: string | undefined;
|
|
|
|
|
size: number;
|
2023-04-24 01:21:29 -07:00
|
|
|
}) => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const size = args.size ? args.size : 250;
|
2023-04-24 01:21:29 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
if (!args.coverArtId || args.coverArtId.match('2a96cbd8b46e442fc41c2b86b821562f')) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2023-04-24 01:21:29 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
return (
|
|
|
|
|
`${args.baseUrl}/rest/getCoverArt.view` +
|
|
|
|
|
`?id=${args.coverArtId}` +
|
|
|
|
|
`&${args.credential}` +
|
|
|
|
|
'&v=1.13.0' +
|
|
|
|
|
'&c=feishin' +
|
|
|
|
|
`&size=${size}`
|
|
|
|
|
);
|
2023-04-24 01:21:29 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const normalizeSong = (
|
2023-07-01 19:10:05 -07:00
|
|
|
item: z.infer<typeof ssType._response.song>,
|
|
|
|
|
server: ServerListItem | null,
|
|
|
|
|
deviceId: string,
|
2024-09-26 04:23:08 +00:00
|
|
|
size?: number,
|
2023-04-24 01:21:29 -07:00
|
|
|
): QueueSong => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const imageUrl =
|
|
|
|
|
getCoverArtUrl({
|
|
|
|
|
baseUrl: server?.url,
|
2024-10-14 19:36:51 -07:00
|
|
|
coverArtId: item.coverArt?.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
credential: server?.credential,
|
2024-09-26 04:23:08 +00:00
|
|
|
size: size || 300,
|
2023-07-01 19:10:05 -07:00
|
|
|
}) || null;
|
2023-04-24 01:21:29 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
const streamUrl = `${server?.url}/rest/stream.view?id=${item.id}&v=1.13.0&c=feishin_${deviceId}&${server?.credential}`;
|
2023-04-24 01:21:29 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
return {
|
|
|
|
|
album: item.album || '',
|
|
|
|
|
albumArtists: [
|
|
|
|
|
{
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.artistId?.toString() || '',
|
2023-07-01 19:10:05 -07:00
|
|
|
imageUrl: null,
|
|
|
|
|
name: item.artist || '',
|
|
|
|
|
},
|
|
|
|
|
],
|
2024-10-01 17:21:28 -07:00
|
|
|
albumId: item.albumId?.toString() || '',
|
2023-07-01 19:10:05 -07:00
|
|
|
artistName: item.artist || '',
|
|
|
|
|
artists: [
|
|
|
|
|
{
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.artistId?.toString() || '',
|
2023-07-01 19:10:05 -07:00
|
|
|
imageUrl: null,
|
|
|
|
|
name: item.artist || '',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
bitRate: item.bitRate || 0,
|
2024-09-26 04:23:08 +00:00
|
|
|
bpm: item.bpm || null,
|
2023-07-01 19:10:05 -07:00
|
|
|
channels: null,
|
|
|
|
|
comment: null,
|
|
|
|
|
compilation: null,
|
|
|
|
|
container: item.contentType,
|
|
|
|
|
createdAt: item.created,
|
|
|
|
|
discNumber: item.discNumber || 1,
|
2023-09-22 15:12:23 -07:00
|
|
|
discSubtitle: null,
|
2023-09-21 17:22:42 -07:00
|
|
|
duration: item.duration ? item.duration * 1000 : 0,
|
2024-04-27 22:20:42 -07:00
|
|
|
gain:
|
|
|
|
|
item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
|
|
|
|
|
? {
|
|
|
|
|
album: item.replayGain.albumGain,
|
|
|
|
|
track: item.replayGain.trackGain,
|
|
|
|
|
}
|
|
|
|
|
: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
genres: item.genre
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
id: item.genre,
|
2023-08-04 10:32:35 -07:00
|
|
|
imageUrl: null,
|
|
|
|
|
itemType: LibraryItem.GENRE,
|
2023-07-01 19:10:05 -07:00
|
|
|
name: item.genre,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: [],
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.id.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
imagePlaceholderUrl: null,
|
|
|
|
|
imageUrl,
|
|
|
|
|
itemType: LibraryItem.SONG,
|
|
|
|
|
lastPlayedAt: null,
|
|
|
|
|
lyrics: null,
|
|
|
|
|
name: item.title,
|
|
|
|
|
path: item.path,
|
2024-04-27 22:20:42 -07:00
|
|
|
peak:
|
|
|
|
|
item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
|
|
|
|
|
? {
|
|
|
|
|
album: item.replayGain.albumPeak,
|
|
|
|
|
track: item.replayGain.trackPeak,
|
|
|
|
|
}
|
|
|
|
|
: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
playCount: item?.playCount || 0,
|
|
|
|
|
releaseDate: null,
|
|
|
|
|
releaseYear: item.year ? String(item.year) : null,
|
|
|
|
|
serverId: server?.id || 'unknown',
|
|
|
|
|
serverType: ServerType.SUBSONIC,
|
|
|
|
|
size: item.size,
|
|
|
|
|
streamUrl,
|
|
|
|
|
trackNumber: item.track || 1,
|
|
|
|
|
uniqueId: nanoid(),
|
|
|
|
|
updatedAt: '',
|
|
|
|
|
userFavorite: item.starred || false,
|
|
|
|
|
userRating: item.userRating || null,
|
|
|
|
|
};
|
2023-04-24 01:21:29 -07:00
|
|
|
};
|
|
|
|
|
|
2023-05-19 00:14:41 -07:00
|
|
|
const normalizeAlbumArtist = (
|
2024-09-26 04:23:08 +00:00
|
|
|
item:
|
|
|
|
|
| z.infer<typeof ssType._response.albumArtist>
|
|
|
|
|
| z.infer<typeof ssType._response.artistListEntry>,
|
2023-07-01 19:10:05 -07:00
|
|
|
server: ServerListItem | null,
|
2024-09-26 04:23:08 +00:00
|
|
|
imageSize?: number,
|
2023-05-19 00:14:41 -07:00
|
|
|
): AlbumArtist => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const imageUrl =
|
|
|
|
|
getCoverArtUrl({
|
|
|
|
|
baseUrl: server?.url,
|
2024-10-14 19:36:51 -07:00
|
|
|
coverArtId: item.coverArt?.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
credential: server?.credential,
|
2024-09-26 04:23:08 +00:00
|
|
|
size: imageSize || 100,
|
2023-07-01 19:10:05 -07:00
|
|
|
}) || null;
|
2023-05-19 00:14:41 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
return {
|
|
|
|
|
albumCount: item.albumCount ? Number(item.albumCount) : 0,
|
|
|
|
|
backgroundImageUrl: null,
|
|
|
|
|
biography: null,
|
|
|
|
|
duration: null,
|
|
|
|
|
genres: [],
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.id.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
imageUrl,
|
|
|
|
|
itemType: LibraryItem.ALBUM_ARTIST,
|
|
|
|
|
lastPlayedAt: null,
|
2024-01-15 22:10:50 -08:00
|
|
|
mbz: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
name: item.name,
|
|
|
|
|
playCount: null,
|
|
|
|
|
serverId: server?.id || 'unknown',
|
|
|
|
|
serverType: ServerType.SUBSONIC,
|
|
|
|
|
similarArtists: [],
|
|
|
|
|
songCount: null,
|
|
|
|
|
userFavorite: false,
|
|
|
|
|
userRating: null,
|
|
|
|
|
};
|
2023-05-19 00:14:41 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const normalizeAlbum = (
|
2024-09-26 04:23:08 +00:00
|
|
|
item: z.infer<typeof ssType._response.album> | z.infer<typeof ssType._response.albumListEntry>,
|
2023-07-01 19:10:05 -07:00
|
|
|
server: ServerListItem | null,
|
2024-09-26 04:23:08 +00:00
|
|
|
imageSize?: number,
|
2023-05-19 00:14:41 -07:00
|
|
|
): Album => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const imageUrl =
|
|
|
|
|
getCoverArtUrl({
|
|
|
|
|
baseUrl: server?.url,
|
2024-10-14 19:36:51 -07:00
|
|
|
coverArtId: item.coverArt?.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
credential: server?.credential,
|
2024-09-26 04:23:08 +00:00
|
|
|
size: imageSize || 300,
|
2023-07-01 19:10:05 -07:00
|
|
|
}) || null;
|
2023-05-19 00:14:41 -07:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
return {
|
2024-01-15 22:10:50 -08:00
|
|
|
albumArtist: item.artist,
|
2023-07-01 19:10:05 -07:00
|
|
|
albumArtists: item.artistId
|
2024-10-01 17:21:28 -07:00
|
|
|
? [{ id: item.artistId.toString(), imageUrl: null, name: item.artist }]
|
|
|
|
|
: [],
|
|
|
|
|
artists: item.artistId
|
|
|
|
|
? [{ id: item.artistId.toString(), imageUrl: null, name: item.artist }]
|
2023-07-01 19:10:05 -07:00
|
|
|
: [],
|
|
|
|
|
backdropImageUrl: null,
|
2024-01-15 20:46:06 -08:00
|
|
|
comment: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
createdAt: item.created,
|
2024-09-26 04:23:08 +00:00
|
|
|
duration: item.duration * 1000,
|
2023-08-04 10:32:35 -07:00
|
|
|
genres: item.genre
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
id: item.genre,
|
|
|
|
|
imageUrl: null,
|
|
|
|
|
itemType: LibraryItem.GENRE,
|
|
|
|
|
name: item.genre,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: [],
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.id.toString(),
|
2023-07-01 19:10:05 -07:00
|
|
|
imagePlaceholderUrl: null,
|
|
|
|
|
imageUrl,
|
|
|
|
|
isCompilation: null,
|
|
|
|
|
itemType: LibraryItem.ALBUM,
|
|
|
|
|
lastPlayedAt: null,
|
2024-01-15 22:10:50 -08:00
|
|
|
mbzId: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
name: item.name,
|
2024-08-25 19:52:44 -07:00
|
|
|
originalDate: null,
|
2023-07-01 19:10:05 -07:00
|
|
|
playCount: null,
|
2024-10-15 03:15:59 -07:00
|
|
|
releaseDate: item.year ? new Date(Date.UTC(item.year, 0, 1)).toISOString() : null,
|
2023-07-01 19:10:05 -07:00
|
|
|
releaseYear: item.year ? Number(item.year) : null,
|
|
|
|
|
serverId: server?.id || 'unknown',
|
|
|
|
|
serverType: ServerType.SUBSONIC,
|
|
|
|
|
size: null,
|
|
|
|
|
songCount: item.songCount,
|
2024-09-26 04:23:08 +00:00
|
|
|
songs:
|
|
|
|
|
(item as z.infer<typeof ssType._response.album>).song?.map((song) =>
|
|
|
|
|
normalizeSong(song, server, ''),
|
|
|
|
|
) || [],
|
2023-07-01 19:10:05 -07:00
|
|
|
uniqueId: nanoid(),
|
|
|
|
|
updatedAt: item.created,
|
|
|
|
|
userFavorite: item.starred || false,
|
|
|
|
|
userRating: item.userRating || null,
|
|
|
|
|
};
|
2023-05-19 00:14:41 -07:00
|
|
|
};
|
|
|
|
|
|
2024-09-26 04:23:08 +00:00
|
|
|
const normalizePlaylist = (
|
|
|
|
|
item:
|
|
|
|
|
| z.infer<typeof ssType._response.playlist>
|
|
|
|
|
| z.infer<typeof ssType._response.playlistListEntry>,
|
|
|
|
|
server: ServerListItem | null,
|
|
|
|
|
): Playlist => {
|
|
|
|
|
return {
|
|
|
|
|
description: item.comment || null,
|
|
|
|
|
duration: item.duration,
|
|
|
|
|
genres: [],
|
2024-10-01 17:21:28 -07:00
|
|
|
id: item.id.toString(),
|
2024-09-26 04:23:08 +00:00
|
|
|
imagePlaceholderUrl: null,
|
|
|
|
|
imageUrl: getCoverArtUrl({
|
|
|
|
|
baseUrl: server?.url,
|
2024-10-14 19:36:51 -07:00
|
|
|
coverArtId: item.coverArt?.toString(),
|
2024-09-26 04:23:08 +00:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-24 01:21:29 -07:00
|
|
|
export const ssNormalize = {
|
2023-07-01 19:10:05 -07:00
|
|
|
album: normalizeAlbum,
|
|
|
|
|
albumArtist: normalizeAlbumArtist,
|
2024-09-26 04:23:08 +00:00
|
|
|
genre: normalizeGenre,
|
|
|
|
|
playlist: normalizePlaylist,
|
2023-07-01 19:10:05 -07:00
|
|
|
song: normalizeSong,
|
2023-04-24 01:21:29 -07:00
|
|
|
};
|