add selector to convert musicbrainz releases to Album type

This commit is contained in:
jeffvli
2026-01-08 01:18:12 -08:00
parent 4b509951a5
commit 10d02087d0
2 changed files with 138 additions and 4 deletions
@@ -1,9 +1,17 @@
import { queryOptions } from '@tanstack/react-query';
import { MusicBrainzApi } from 'musicbrainz-api';
import memoize from 'lodash/memoize';
import { IArtist, IRelease, IReleaseGroup, MusicBrainzApi } from 'musicbrainz-api';
import packageJson from '../../../../../package.json';
import { queryKeys } from '/@/renderer/api/query-keys';
import {
Album,
AlbumArtist,
LibraryItem,
RelatedArtist,
ServerType,
} from '/@/shared/types/domain-types';
export const musicbrainzApi = new MusicBrainzApi({
appContactInfo: packageJson.homepage,
@@ -14,20 +22,145 @@ export const musicbrainzApi = new MusicBrainzApi({
// Cache all musicbrainz api results for 5 minutes
const CACHE_TIME = 1000 * 60 * 5;
const artistSelect = memoize(
({ data, meta }: { data: IArtist; meta: { albumArtist: AlbumArtist } }) => {
const releaseGroups =
data['release-groups']?.reduce(
(
acc: Record<
string,
{
originalDate: null | string;
primaryReleaseType: null | string;
secondaryReleaseTypes: string[];
}
>,
releaseGroup: IReleaseGroup,
) => {
const primaryReleaseType = releaseGroup['primary-type'].toLowerCase();
const secondaryReleaseTypes = releaseGroup['secondary-types'].map((type) =>
type.toLowerCase(),
);
const originalDate = releaseGroup['first-release-date'];
acc[releaseGroup.title] = {
originalDate: originalDate,
primaryReleaseType: primaryReleaseType,
secondaryReleaseTypes: secondaryReleaseTypes,
};
return acc;
},
{} as Record<
string,
{
originalDate: null | string;
primaryReleaseType: null | string;
secondaryReleaseTypes: string[];
}
>,
) || {};
const albumArtist: RelatedArtist = {
id: meta.albumArtist.id,
imageId: meta.albumArtist.imageId,
imageUrl: meta.albumArtist.imageUrl,
name: meta.albumArtist.name,
userFavorite: meta.albumArtist.userFavorite,
userRating: meta.albumArtist.userRating,
};
const albumArtistName = meta.albumArtist.name;
const albums: Album[] = (data['releases'] || [])
.map((release: IRelease) => {
const releaseGroup = releaseGroups[release.title];
if (!releaseGroup) {
return null;
}
const releaseType = releaseGroup.primaryReleaseType;
const secondaryReleaseTypes = releaseGroup.secondaryReleaseTypes || [];
const releaseTypes = [releaseType, ...secondaryReleaseTypes].filter(
(type) => type !== null,
) as string[];
const isCompilation = releaseTypes.includes('compilation');
const originalDate = releaseGroup.originalDate;
const originalYear = originalDate ? Number(originalDate.split('-')[0]) : null;
const releaseDate = release.date ? release.date : null;
const releaseYear = release.date ? Number(release.date.split('-')[0]) : null;
const imageUrl = release.media.length > 0 ? getImageUrl(release.id) : null;
const album: Album = {
_itemType: LibraryItem.ALBUM,
_serverId: '',
_serverType: ServerType.EXTERNAL,
albumArtistName: albumArtistName,
albumArtists: [albumArtist],
artists: [],
comment: null,
createdAt: '',
duration: null,
explicitStatus: null,
genres: [],
id: `musicbrainz-${release.id}`,
imageId: null,
imageUrl: imageUrl,
isCompilation: isCompilation,
lastPlayedAt: null,
mbzId: release.id,
name: release.title,
originalDate: originalDate,
originalYear: originalYear,
participants: {},
playCount: null,
recordLabels: [],
releaseDate: releaseDate,
releaseType: releaseType,
releaseTypes: releaseTypes,
releaseYear: releaseYear,
size: null,
songCount: null,
tags: {},
updatedAt: '',
userFavorite: false,
userRating: null,
version: null,
};
return album;
})
.filter((album): album is Album => album !== null);
return albums;
},
);
export const musicbrainzQueries = {
artist: (args: { mbzArtistId: string }) => {
return queryOptions({
gcTime: CACHE_TIME,
queryFn: () =>
musicbrainzApi.lookup('artist', args.mbzArtistId, [
queryFn: async ({ meta }) => {
const data = await musicbrainzApi.lookup('artist', args.mbzArtistId, [
'releases',
'release-rels',
'recordings',
'release-groups',
'release-group-rels',
'works',
'media',
]),
]);
return { data, meta: meta as { albumArtist: AlbumArtist } };
},
queryKey: queryKeys.musicbrainz.artist(args.mbzArtistId),
select: artistSelect,
staleTime: CACHE_TIME,
});
},
};
function getImageUrl(releaseId: string): string {
return `https://coverartarchive.org/release/${releaseId}/front-250.jpg`;
}
+1
View File
@@ -34,6 +34,7 @@ export enum LibraryItem {
}
export enum ServerType {
EXTERNAL = 'external', // This is not an actual server type. This is used when fetching from external sources (e.g. musicbrainz)
JELLYFIN = 'jellyfin',
NAVIDROME = 'navidrome',
SUBSONIC = 'subsonic',