progress on subsonic api

This commit is contained in:
jeffvli
2025-07-21 00:08:58 -07:00
parent 96221c8fa7
commit 98e8bda45d
94 changed files with 3083 additions and 798 deletions
+210 -55
View File
@@ -1,18 +1,49 @@
import { components } from './subsonic-schema.d';
import { components, paths } from './subsonic-schema.d';
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Album, AlbumListSortOptions } from '/@/shared/types/domain/album-domain-types';
import { Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
import { Genre, RelatedGenre } 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 { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { User } from '/@/shared/types/domain/user-domain-types';
import { formatDate } from '/@/shared/utils/format-date';
type AlbumSortType = paths['/rest/getAlbumList2']['get']['parameters']['query']['type'];
const defaultSortType: AlbumSortType = 'alphabeticalByName';
const albumSortByMap: Record<AlbumListSortOptions, AlbumSortType> = {
[AlbumListSortOptions.ALBUM_ARTIST]: 'alphabeticalByArtist',
[AlbumListSortOptions.ARTIST]: defaultSortType,
[AlbumListSortOptions.COMMUNITY_RATING]: defaultSortType,
[AlbumListSortOptions.CRITIC_RATING]: defaultSortType,
[AlbumListSortOptions.DATE_ADDED]: 'newest',
[AlbumListSortOptions.DATE_PLAYED]: 'recent',
[AlbumListSortOptions.DURATION]: defaultSortType,
[AlbumListSortOptions.IS_FAVORITE]: defaultSortType,
[AlbumListSortOptions.NAME]: 'alphabeticalByName',
[AlbumListSortOptions.PLAY_COUNT]: 'frequent',
[AlbumListSortOptions.RANDOM]: defaultSortType,
[AlbumListSortOptions.RATING]: 'highest',
[AlbumListSortOptions.RELEASE_DATE]: defaultSortType,
[AlbumListSortOptions.TRACK_COUNT]: defaultSortType,
[AlbumListSortOptions.YEAR]: defaultSortType,
};
export const normalize = {
_sort: {
album: (option: AlbumListSortOptions) => {
return albumSortByMap[option] || defaultSortType;
},
},
album: (
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
server: ServerListItem,
): Album => {
const imageUrl = item.coverArt ? getCoverArtUrl(item.coverArt, server) : null;
@@ -24,7 +55,7 @@ export const normalize = {
artistName: item.artist || null,
artists: getArtistList(item.artists, item.artistId, item.artist),
comment: null,
createdDate: item.created,
createdDate: item.created || null,
discTitles: getDiscTitles(item),
displayArtist: null,
duration: getDuration(item.duration),
@@ -33,12 +64,12 @@ export const normalize = {
id: item.id.toString(),
imagePlaceholderUrl: null,
imageUrl,
isCompilation: item.isCompilation || null,
isCompilation: getIsCompilation(item),
mbzId: item.musicBrainzId || null,
mbzReleaseGroupId: null,
missing: null,
moods: getMoods(item),
name: item.name,
name: getName(item),
originalReleaseDate: getOriginalReleaseDate(item),
participants: {},
recordLabels: getRecordLabels(item),
@@ -46,8 +77,8 @@ export const normalize = {
releaseTypes: getReleaseTypes(item),
releaseYear: item.year || null,
size: null,
songCount: item.songCount,
sortName: item.sortName || item.name,
songCount: 'songCount' in item ? item.songCount : null,
sortName: getSortName(item),
tags: {},
updatedDate: null,
userFavorite: Boolean(item.starred),
@@ -55,7 +86,7 @@ export const normalize = {
userLastPlayedDate: item.played || null,
userPlayCount: item.playCount ?? null,
userRating: item.userRating || null,
version: item.version || null,
version: 'version' in item ? item.version || null : null,
};
},
albumArtist: (
@@ -167,6 +198,28 @@ export const normalize = {
userRating: item.userRating || null,
};
},
user: (item: components['schemas']['User'], server: ServerListItem): User => {
return {
_itemType: LibraryItem.USER,
_serverId: server.id,
_serverType: ServerType.SUBSONIC,
id: item.username,
permissions: {
'jukebox.manage': item.jukeboxRole,
'media.download': item.downloadRole,
'media.folder': item.folder?.map((folder) => folder.toString()) || [],
'media.share': item.shareRole,
'media.stream': item.streamRole,
'media.upload': item.uploadRole,
'playlist.create': item.playlistRole,
'playlist.delete': item.playlistRole,
'playlist.edit': item.playlistRole,
'server.admin': item.adminRole,
'user.edit': item.settingsRole,
},
username: item.username,
};
},
};
function getArtistList(
@@ -200,12 +253,19 @@ function getCoverArtUrl(id: string, server: ServerListItem) {
}
function getDiscTitles(
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return (item.discTitles || []).map((discTitle) => ({
disc: discTitle.disc,
title: discTitle.title,
}));
if ('discTitles' in item) {
return (item.discTitles || []).map((discTitle) => ({
disc: discTitle.disc,
title: discTitle.title,
}));
}
return [];
}
function getDuration(duration?: number) {
@@ -214,12 +274,16 @@ function getDuration(duration?: number) {
}
function getGainInfo(item: components['schemas']['Child']) {
return item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
? {
album: item.replayGain.albumGain,
track: item.replayGain.trackGain,
}
: null;
if ('replayGain' in item) {
return item.replayGain && (item.replayGain.albumGain || item.replayGain.trackGain)
? {
album: item.replayGain.albumGain,
track: item.replayGain.trackGain,
}
: null;
}
return null;
}
function getGenres(
@@ -228,15 +292,19 @@ function getGenres(
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
): RelatedGenre[] {
if (item.genres) {
return item.genres.map((genre) => ({
if ('genres' in item) {
return (item.genres || []).map((genre) => ({
id: genre.name,
imageUrl: null,
name: genre.name,
}));
}
if (item.genre) {
if ('genre' in item) {
if (!item.genre) {
return [];
}
return [
{
id: item.genre,
@@ -249,26 +317,67 @@ function getGenres(
return [];
}
function getIsCompilation(
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
if ('isCompilation' in item) {
return item.isCompilation || null;
}
return null;
}
function getMoods(
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return (item.moods || []).map((mood) => ({
id: mood,
name: mood,
}));
if ('moods' in item) {
return (item.moods || []).map((mood) => ({
id: mood,
name: mood,
}));
}
return [];
}
function getName(
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
if ('name' in item) {
return item.name || '';
}
if ('title' in item) {
return item.title || '';
}
return '';
}
function getOriginalReleaseDate(
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return item.originalReleaseDate
? formatDate.toUTCDate(
`${item.originalReleaseDate.year}-${item.originalReleaseDate.month}-${item.originalReleaseDate.day}`,
)
: null;
if ('originalReleaseDate' in item) {
return item.originalReleaseDate
? formatDate.toUTCDate(
`${item.originalReleaseDate.year}-${item.originalReleaseDate.month}-${item.originalReleaseDate.day}`,
)
: null;
}
return null;
}
function getParticipants(item: components['schemas']['Child']) {
@@ -298,40 +407,86 @@ function getParticipants(item: components['schemas']['Child']) {
}
function getPeakInfo(item: components['schemas']['Child']) {
return item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
? {
album: item.replayGain.albumPeak,
track: item.replayGain.trackPeak,
}
: null;
if ('replayGain' in item) {
return item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
? {
album: item.replayGain.albumPeak,
track: item.replayGain.trackPeak,
}
: null;
}
return null;
}
function getRecordLabels(
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return (item.recordLabels || []).map((recordLabel) => ({
id: recordLabel.name,
name: recordLabel.name,
}));
if ('recordLabels' in item) {
return (item.recordLabels || []).map((recordLabel) => ({
id: recordLabel.name,
name: recordLabel.name,
}));
}
return [];
}
function getReleaseDate(
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return item.releaseDate
? formatDate.toUTCDate(
`${item.releaseDate.year}-${item.releaseDate.month}-${item.releaseDate.day}`,
)
: null;
if ('releaseDate' in item) {
return item.releaseDate
? formatDate.toUTCDate(
`${item.releaseDate.year}-${item.releaseDate.month}-${item.releaseDate.day}`,
)
: null;
}
return null;
}
function getReleaseTypes(
item: components['schemas']['AlbumID3'] | components['schemas']['AlbumID3WithSongs'],
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
return (item.releaseTypes || []).map((releaseType) => ({
id: releaseType,
name: releaseType,
}));
if ('releaseTypes' in item) {
return (item.releaseTypes || []).map((releaseType) => ({
id: releaseType,
name: releaseType,
}));
}
return [];
}
function getSortName(
item:
| components['schemas']['AlbumID3']
| components['schemas']['AlbumID3WithSongs']
| components['schemas']['Child'],
) {
if ('sortName' in item) {
return item.sortName || '';
}
if ('name' in item) {
return item.name || '';
}
if ('title' in item) {
return item.title || '';
}
return '';
}
function getStreamUrl(id: string, server: ServerListItem) {