|
|
|
@ -1,17 +1,21 @@
|
|
|
|
|
import { BaseNodeBrainz } from 'nodebrainz';
|
|
|
|
|
import type { mbArtist, mbRecording, mbReleaseGroup, mbRelease, mbWork} from './interfaces';
|
|
|
|
|
import {mbArtistType, mbReleaseGroupType, mbWorkType} from './interfaces';
|
|
|
|
|
|
|
|
|
|
interface SearchOptions {
|
|
|
|
|
query: string;
|
|
|
|
|
page?: number;
|
|
|
|
|
limit?: number;
|
|
|
|
|
keywords?: string;
|
|
|
|
|
artistname?: string;
|
|
|
|
|
albumname?: string;
|
|
|
|
|
recordingname?: string;
|
|
|
|
|
tag?: string;
|
|
|
|
|
}
|
|
|
|
|
import BaseNodeBrainz from 'nodebrainz';
|
|
|
|
|
import type {
|
|
|
|
|
ArtistCredit,
|
|
|
|
|
Group,
|
|
|
|
|
mbArtist,
|
|
|
|
|
mbRecording,
|
|
|
|
|
mbRelease,
|
|
|
|
|
mbReleaseGroup,
|
|
|
|
|
mbWork,
|
|
|
|
|
Medium,
|
|
|
|
|
Recording,
|
|
|
|
|
Relation,
|
|
|
|
|
Release,
|
|
|
|
|
SearchOptions,
|
|
|
|
|
Tag,
|
|
|
|
|
Work,
|
|
|
|
|
} from './interfaces';
|
|
|
|
|
import { mbArtistType, mbReleaseGroupType, mbWorkType } from './interfaces';
|
|
|
|
|
|
|
|
|
|
interface ArtistSearchOptions {
|
|
|
|
|
query: string;
|
|
|
|
@ -53,123 +57,32 @@ interface WorkSearchOptions {
|
|
|
|
|
offset?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Tag {
|
|
|
|
|
name: string;
|
|
|
|
|
count: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Area {
|
|
|
|
|
"sort-name": string
|
|
|
|
|
"type-id": string
|
|
|
|
|
"iso-3166-1-codes": string[]
|
|
|
|
|
type: string
|
|
|
|
|
disambiguation: string
|
|
|
|
|
name: string
|
|
|
|
|
id: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Media {
|
|
|
|
|
position: number
|
|
|
|
|
"track-count": number
|
|
|
|
|
format: string
|
|
|
|
|
"format-id": string
|
|
|
|
|
title: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ReleaseEvent {
|
|
|
|
|
area: Area
|
|
|
|
|
date: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RawArtist {
|
|
|
|
|
"sort-name": string
|
|
|
|
|
disambiguation: string
|
|
|
|
|
id: string
|
|
|
|
|
name: string
|
|
|
|
|
"type-id": string
|
|
|
|
|
type: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RawRecording {
|
|
|
|
|
length: number
|
|
|
|
|
video: boolean
|
|
|
|
|
title: string
|
|
|
|
|
id: string
|
|
|
|
|
disambiguation: string
|
|
|
|
|
tags: Tag[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RawReleaseGroup {
|
|
|
|
|
tags: Tag[],
|
|
|
|
|
"primary-type": string
|
|
|
|
|
"secondary-types": string[]
|
|
|
|
|
disambiguation: string
|
|
|
|
|
"first-release-date": string
|
|
|
|
|
"secondary-type-ids": string[]
|
|
|
|
|
releases: any[]
|
|
|
|
|
"primary-type-id": string
|
|
|
|
|
id: string
|
|
|
|
|
title: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RawRelease {
|
|
|
|
|
barcode: string
|
|
|
|
|
tags: Tag[]
|
|
|
|
|
disambiguation: string
|
|
|
|
|
packaging: string
|
|
|
|
|
"packaging-id": string
|
|
|
|
|
"release-events": ReleaseEvent[]
|
|
|
|
|
title: string
|
|
|
|
|
status: string
|
|
|
|
|
"text-representation": {
|
|
|
|
|
language: string
|
|
|
|
|
script: string
|
|
|
|
|
}
|
|
|
|
|
"status-id": string
|
|
|
|
|
"release-group": any
|
|
|
|
|
country: string
|
|
|
|
|
quality: string
|
|
|
|
|
date: string
|
|
|
|
|
id: string
|
|
|
|
|
media: Media[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RawWork {
|
|
|
|
|
disambiguation: string
|
|
|
|
|
attributes: any[]
|
|
|
|
|
id: string
|
|
|
|
|
"type-id": string
|
|
|
|
|
languages: string[]
|
|
|
|
|
type: string
|
|
|
|
|
tags: Tag[]
|
|
|
|
|
iswcs: string[]
|
|
|
|
|
title: string
|
|
|
|
|
language: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchOptionstoArtistSearchOptions(options: SearchOptions): ArtistSearchOptions {
|
|
|
|
|
const data : ArtistSearchOptions = {
|
|
|
|
|
query: options.query
|
|
|
|
|
}
|
|
|
|
|
function searchOptionstoArtistSearchOptions(
|
|
|
|
|
options: SearchOptions
|
|
|
|
|
): ArtistSearchOptions {
|
|
|
|
|
const data: ArtistSearchOptions = {
|
|
|
|
|
query: options.query,
|
|
|
|
|
};
|
|
|
|
|
if (options.tag) {
|
|
|
|
|
data.tag = options.tag;
|
|
|
|
|
}
|
|
|
|
|
if (options.limit) {
|
|
|
|
|
data.limit = options.limit;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
data.limit = 25;
|
|
|
|
|
}
|
|
|
|
|
if (options.page) {
|
|
|
|
|
data.offset = (options.page-1)*data.limit;
|
|
|
|
|
data.offset = (options.page - 1) * data.limit;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchOptionstoRecordingSearchOptions(options: SearchOptions): RecordingSearchOptions {
|
|
|
|
|
const data : RecordingSearchOptions = {
|
|
|
|
|
query: options.query
|
|
|
|
|
}
|
|
|
|
|
function searchOptionstoRecordingSearchOptions(
|
|
|
|
|
options: SearchOptions
|
|
|
|
|
): RecordingSearchOptions {
|
|
|
|
|
const data: RecordingSearchOptions = {
|
|
|
|
|
query: options.query,
|
|
|
|
|
};
|
|
|
|
|
if (options.tag) {
|
|
|
|
|
data.tag = options.tag;
|
|
|
|
|
}
|
|
|
|
@ -181,20 +94,21 @@ function searchOptionstoRecordingSearchOptions(options: SearchOptions): Recordin
|
|
|
|
|
}
|
|
|
|
|
if (options.limit) {
|
|
|
|
|
data.limit = options.limit;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
data.limit = 25;
|
|
|
|
|
}
|
|
|
|
|
if (options.page) {
|
|
|
|
|
data.offset = (options.page-1)*data.limit;
|
|
|
|
|
data.offset = (options.page - 1) * data.limit;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchOptionstoReleaseSearchOptions(options: SearchOptions): ReleaseSearchOptions {
|
|
|
|
|
const data : ReleaseSearchOptions = {
|
|
|
|
|
query: options.query
|
|
|
|
|
}
|
|
|
|
|
function searchOptionstoReleaseSearchOptions(
|
|
|
|
|
options: SearchOptions
|
|
|
|
|
): ReleaseSearchOptions {
|
|
|
|
|
const data: ReleaseSearchOptions = {
|
|
|
|
|
query: options.query,
|
|
|
|
|
};
|
|
|
|
|
if (options.artistname) {
|
|
|
|
|
data.artistname = options.artistname;
|
|
|
|
|
}
|
|
|
|
@ -203,20 +117,21 @@ function searchOptionstoReleaseSearchOptions(options: SearchOptions): ReleaseSea
|
|
|
|
|
}
|
|
|
|
|
if (options.limit) {
|
|
|
|
|
data.limit = options.limit;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
data.limit = 25;
|
|
|
|
|
}
|
|
|
|
|
if (options.page) {
|
|
|
|
|
data.offset = (options.page-1)*data.limit;
|
|
|
|
|
data.offset = (options.page - 1) * data.limit;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchOptionstoReleaseGroupSearchOptions(options: SearchOptions): ReleaseGroupSearchOptions {
|
|
|
|
|
const data : ReleaseGroupSearchOptions = {
|
|
|
|
|
query: options.query
|
|
|
|
|
}
|
|
|
|
|
function searchOptionstoReleaseGroupSearchOptions(
|
|
|
|
|
options: SearchOptions
|
|
|
|
|
): ReleaseGroupSearchOptions {
|
|
|
|
|
const data: ReleaseGroupSearchOptions = {
|
|
|
|
|
query: options.query,
|
|
|
|
|
};
|
|
|
|
|
if (options.artistname) {
|
|
|
|
|
data.artistname = options.artistname;
|
|
|
|
|
}
|
|
|
|
@ -225,20 +140,21 @@ function searchOptionstoReleaseGroupSearchOptions(options: SearchOptions): Relea
|
|
|
|
|
}
|
|
|
|
|
if (options.limit) {
|
|
|
|
|
data.limit = options.limit;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
data.limit = 25;
|
|
|
|
|
}
|
|
|
|
|
if (options.page) {
|
|
|
|
|
data.offset = (options.page-1)*data.limit;
|
|
|
|
|
data.offset = (options.page - 1) * data.limit;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchOptionstoWorkSearchOptions(options: SearchOptions): WorkSearchOptions {
|
|
|
|
|
const data : WorkSearchOptions = {
|
|
|
|
|
query: options.query
|
|
|
|
|
}
|
|
|
|
|
function searchOptionstoWorkSearchOptions(
|
|
|
|
|
options: SearchOptions
|
|
|
|
|
): WorkSearchOptions {
|
|
|
|
|
const data: WorkSearchOptions = {
|
|
|
|
|
query: options.query,
|
|
|
|
|
};
|
|
|
|
|
if (options.artistname) {
|
|
|
|
|
data.artist = options.artistname;
|
|
|
|
|
}
|
|
|
|
@ -247,31 +163,36 @@ function searchOptionstoWorkSearchOptions(options: SearchOptions): WorkSearchOpt
|
|
|
|
|
}
|
|
|
|
|
if (options.limit) {
|
|
|
|
|
data.limit = options.limit;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
data.limit = 25;
|
|
|
|
|
}
|
|
|
|
|
if (options.page) {
|
|
|
|
|
data.offset = (options.page-1)*data.limit;
|
|
|
|
|
data.offset = (options.page - 1) * data.limit;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MusicBrainz extends BaseNodeBrainz {
|
|
|
|
|
constructor() {
|
|
|
|
|
super({userAgent:'Overseer-with-lidar-support/0.0.1 ( https://github.com/ano0002/overseerr )'});
|
|
|
|
|
super({
|
|
|
|
|
userAgent:
|
|
|
|
|
'Overseer-with-lidar-support/0.0.1 ( https://github.com/ano0002/overseerr )',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public searchMulti = async (search: SearchOptions) => {
|
|
|
|
|
try {
|
|
|
|
|
const artistSearch = searchOptionstoArtistSearchOptions(search);
|
|
|
|
|
const recordingSearch = searchOptionstoRecordingSearchOptions(search);
|
|
|
|
|
const releaseGroupSearch = searchOptionstoReleaseGroupSearchOptions(search);
|
|
|
|
|
const releaseGroupSearch =
|
|
|
|
|
searchOptionstoReleaseGroupSearchOptions(search);
|
|
|
|
|
const releaseSearch = searchOptionstoReleaseSearchOptions(search);
|
|
|
|
|
const workSearch = searchOptionstoWorkSearchOptions(search);
|
|
|
|
|
const artistResults = await this.searchArtists(artistSearch);
|
|
|
|
|
const recordingResults = await this.searchRecordings(recordingSearch);
|
|
|
|
|
const releaseGroupResults = await this.searchReleaseGroups(releaseGroupSearch);
|
|
|
|
|
const releaseGroupResults = await this.searchReleaseGroups(
|
|
|
|
|
releaseGroupSearch
|
|
|
|
|
);
|
|
|
|
|
const releaseResults = await this.searchReleases(releaseSearch);
|
|
|
|
|
const workResults = await this.searchWorks(workSearch);
|
|
|
|
|
|
|
|
|
@ -281,7 +202,7 @@ class MusicBrainz extends BaseNodeBrainz {
|
|
|
|
|
recordingResults,
|
|
|
|
|
releaseGroupResults,
|
|
|
|
|
releaseResults,
|
|
|
|
|
workResults
|
|
|
|
|
workResults,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return combinedResults;
|
|
|
|
@ -292,7 +213,7 @@ class MusicBrainz extends BaseNodeBrainz {
|
|
|
|
|
recordingResults: [],
|
|
|
|
|
releaseGroupResults: [],
|
|
|
|
|
releaseResults: [],
|
|
|
|
|
workResults: []
|
|
|
|
|
workResults: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
@ -342,228 +263,245 @@ class MusicBrainz extends BaseNodeBrainz {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public getArtist = async (artistId : string): Promise<mbArtist> => {
|
|
|
|
|
public getArtist = async (artistId: string): Promise<mbArtist> => {
|
|
|
|
|
try {
|
|
|
|
|
const rawData = this.artist(artistId, {inc: 'tags+recordings+releases+release-groups+works'});
|
|
|
|
|
const artist : mbArtist = {
|
|
|
|
|
const rawData = this.artist(artistId, {
|
|
|
|
|
inc: 'tags+recordings+releases+release-groups+works',
|
|
|
|
|
});
|
|
|
|
|
const artist: mbArtist = {
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData["sort-name"],
|
|
|
|
|
sortName: rawData['sort-name'],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
recordings: rawData.recordings.map((recording: RawRecording): mbRecording => {
|
|
|
|
|
return {
|
|
|
|
|
id: recording.id,
|
|
|
|
|
artist: [{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData["sort-name"],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name)
|
|
|
|
|
}],
|
|
|
|
|
title: recording.title,
|
|
|
|
|
length: recording.length,
|
|
|
|
|
tags: recording.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
recordings: rawData.recordings.map(
|
|
|
|
|
(recording: Recording): mbRecording => {
|
|
|
|
|
return {
|
|
|
|
|
id: recording.id,
|
|
|
|
|
artist: [
|
|
|
|
|
{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData['sort-name'],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: recording.title,
|
|
|
|
|
length: recording.length,
|
|
|
|
|
tags: recording.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
releases: rawData.releases.map((release: RawRelease): mbRelease => {
|
|
|
|
|
),
|
|
|
|
|
releases: rawData.releases.map((release: Release): mbRelease => {
|
|
|
|
|
return {
|
|
|
|
|
id: release.id,
|
|
|
|
|
artist: [{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData["sort-name"],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name)
|
|
|
|
|
}],
|
|
|
|
|
artist: [
|
|
|
|
|
{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData['sort-name'],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: release.title,
|
|
|
|
|
date: new Date(release.date),
|
|
|
|
|
date: new Date(String(release.date)),
|
|
|
|
|
tags: release.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
releaseGroups: rawData["release-groups"].map((releaseGroup: RawReleaseGroup): mbReleaseGroup => {
|
|
|
|
|
return {
|
|
|
|
|
id: releaseGroup.id,
|
|
|
|
|
artist: [{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData["sort-name"],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name)
|
|
|
|
|
}],
|
|
|
|
|
title: releaseGroup.title,
|
|
|
|
|
type: (releaseGroup["primary-type"] as mbReleaseGroupType) || mbReleaseGroupType.OTHER,
|
|
|
|
|
firstReleased: new Date(releaseGroup["first-release-date"]),
|
|
|
|
|
tags: releaseGroup.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
releaseGroups: rawData['release-groups'].map(
|
|
|
|
|
(releaseGroup: Group): mbReleaseGroup => {
|
|
|
|
|
return {
|
|
|
|
|
id: releaseGroup.id,
|
|
|
|
|
artist: [
|
|
|
|
|
{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData['sort-name'],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: releaseGroup.title,
|
|
|
|
|
type:
|
|
|
|
|
(releaseGroup['primary-type'] as mbReleaseGroupType) ||
|
|
|
|
|
mbReleaseGroupType.OTHER,
|
|
|
|
|
firstReleased: new Date(releaseGroup['first-release-date']),
|
|
|
|
|
tags: releaseGroup.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
works: rawData.works.map((work: RawWork): mbWork => {
|
|
|
|
|
),
|
|
|
|
|
works: rawData.works.map((work: Work): mbWork => {
|
|
|
|
|
return {
|
|
|
|
|
id: work.id,
|
|
|
|
|
title: work.title,
|
|
|
|
|
type: (work.type as mbWorkType) || mbWorkType.OTHER,
|
|
|
|
|
artist: [{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData["sort-name"],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name)
|
|
|
|
|
}],
|
|
|
|
|
artist: [
|
|
|
|
|
{
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
name: rawData.name,
|
|
|
|
|
sortName: rawData['sort-name'],
|
|
|
|
|
type: (rawData.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
tags: work.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
return artist;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new Error(`[MusicBrainz] Failed to fetch artist details: ${e.message}`);
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[MusicBrainz] Failed to fetch artist details: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public getRecording = async (recordingId : string): Promise<mbRecording> => {
|
|
|
|
|
public getRecording = async (recordingId: string): Promise<mbRecording> => {
|
|
|
|
|
try {
|
|
|
|
|
const rawData = this.recording(recordingId, {inc: 'tags+artists+releases'});
|
|
|
|
|
const recording : mbRecording = {
|
|
|
|
|
const rawData = this.recording(recordingId, {
|
|
|
|
|
inc: 'tags+artists+releases',
|
|
|
|
|
});
|
|
|
|
|
const recording: mbRecording = {
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
title: rawData.title,
|
|
|
|
|
artist: rawData["artist-credit"].map((artist: {artist: RawArtist}) => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist["sort-name"],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER
|
|
|
|
|
artist: rawData['artist-credit'].map(
|
|
|
|
|
(artist: ArtistCredit): mbArtist => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist['sort-name'],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: artist.artist.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
length: rawData.length,
|
|
|
|
|
firstReleased: new Date(rawData["first-release-date"]),
|
|
|
|
|
firstReleased: new Date(rawData['first-release-date']),
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
return recording;
|
|
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new Error(`[MusicBrainz] Failed to fetch recording details: ${e.message}`);
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[MusicBrainz] Failed to fetch recording details: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public async getReleaseGroup(releaseGroupId : string): Promise<mbReleaseGroup> {
|
|
|
|
|
public async getReleaseGroup(
|
|
|
|
|
releaseGroupId: string
|
|
|
|
|
): Promise<mbReleaseGroup> {
|
|
|
|
|
try {
|
|
|
|
|
const rawData = this.releaseGroup(releaseGroupId, {inc: 'tags+artists+releases'});
|
|
|
|
|
const releaseGroup : mbReleaseGroup = {
|
|
|
|
|
const rawData = this.releaseGroup(releaseGroupId, {
|
|
|
|
|
inc: 'tags+artists+releases',
|
|
|
|
|
});
|
|
|
|
|
const releaseGroup: mbReleaseGroup = {
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
title: rawData.title,
|
|
|
|
|
artist: rawData["artist-credit"].map((artist: {artist: RawArtist}) => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist["sort-name"],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER
|
|
|
|
|
artist: rawData['artist-credit'].map(
|
|
|
|
|
(artist: ArtistCredit): mbArtist => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist['sort-name'],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: artist.artist.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
type: (rawData["primary-type"] as mbReleaseGroupType) || mbReleaseGroupType.OTHER,
|
|
|
|
|
firstReleased: new Date(rawData["first-release-date"]),
|
|
|
|
|
),
|
|
|
|
|
type:
|
|
|
|
|
(rawData['primary-type'] as mbReleaseGroupType) ||
|
|
|
|
|
mbReleaseGroupType.OTHER,
|
|
|
|
|
firstReleased: new Date(rawData['first-release-date']),
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
return releaseGroup;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new Error(`[MusicBrainz] Failed to fetch release group details: ${e.message}`);
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[MusicBrainz] Failed to fetch release group details: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getRelease(releaseId : string): Promise<mbRelease> {
|
|
|
|
|
public async getRelease(releaseId: string): Promise<mbRelease> {
|
|
|
|
|
try {
|
|
|
|
|
const rawData = this.release(releaseId, {inc: 'tags+artists+recordings'});
|
|
|
|
|
const release : mbRelease = {
|
|
|
|
|
const rawData = this.release(releaseId, {
|
|
|
|
|
inc: 'tags+artists+recordings',
|
|
|
|
|
});
|
|
|
|
|
const release: mbRelease = {
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
title: rawData.title,
|
|
|
|
|
artist: rawData["artist-credit"].map((artist: {artist: RawArtist}) => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist["sort-name"],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER
|
|
|
|
|
artist: rawData['artist-credit'].map(
|
|
|
|
|
(artist: ArtistCredit): mbArtist => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist['sort-name'],
|
|
|
|
|
type: (artist.artist.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: artist.artist.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
date:
|
|
|
|
|
rawData['release-events'] && rawData['release-events'].length > 0
|
|
|
|
|
? new Date(String(rawData['release-events'][0].date))
|
|
|
|
|
: undefined,
|
|
|
|
|
tracks: rawData.media.flatMap((media: Medium): mbRecording[] => {
|
|
|
|
|
return (media.tracks ?? []).map((track): mbRecording => {
|
|
|
|
|
return {
|
|
|
|
|
id: track.id,
|
|
|
|
|
title: track.title,
|
|
|
|
|
artist: track.recording['artist-credit'].map((artist) => {
|
|
|
|
|
return {
|
|
|
|
|
id: artist.artist.id,
|
|
|
|
|
name: artist.artist.name,
|
|
|
|
|
sortName: artist.artist['sort-name'],
|
|
|
|
|
type:
|
|
|
|
|
(artist.artist.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: artist.artist.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
length: track.recording.length,
|
|
|
|
|
tags: track.recording.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
date: new Date(rawData["release-events"][0].date),
|
|
|
|
|
tracks: rawData.media.map((media: {
|
|
|
|
|
"track-count": number
|
|
|
|
|
title: string
|
|
|
|
|
format: string
|
|
|
|
|
position: number
|
|
|
|
|
"track-offset": number
|
|
|
|
|
tracks: {
|
|
|
|
|
title: string
|
|
|
|
|
position: number
|
|
|
|
|
id: string
|
|
|
|
|
length: number
|
|
|
|
|
recording: {
|
|
|
|
|
disambiguation: string
|
|
|
|
|
"first-release-date": string
|
|
|
|
|
title: string
|
|
|
|
|
id: string
|
|
|
|
|
length: number
|
|
|
|
|
tags: Tag[]
|
|
|
|
|
video: boolean
|
|
|
|
|
}
|
|
|
|
|
number: string
|
|
|
|
|
}[];
|
|
|
|
|
"format-id": string
|
|
|
|
|
}) => {
|
|
|
|
|
return media.tracks.map((track: {
|
|
|
|
|
title: string
|
|
|
|
|
position: number
|
|
|
|
|
id: string
|
|
|
|
|
length: number
|
|
|
|
|
recording: {
|
|
|
|
|
disambiguation: string
|
|
|
|
|
"first-release-date": string
|
|
|
|
|
title: string
|
|
|
|
|
id: string
|
|
|
|
|
length: number
|
|
|
|
|
tags: Tag[]
|
|
|
|
|
video: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
number: string
|
|
|
|
|
}) => {
|
|
|
|
|
return {
|
|
|
|
|
id: track.id,
|
|
|
|
|
title: track.title,
|
|
|
|
|
length: track.recording.length,
|
|
|
|
|
tags: track.recording.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}).flat(),
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
return release;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new Error(`[MusicBrainz] Failed to fetch release details: ${e.message}`);
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[MusicBrainz] Failed to fetch release details: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getWork(workId : string): Promise<mbWork> {
|
|
|
|
|
public async getWork(workId: string): Promise<mbWork> {
|
|
|
|
|
try {
|
|
|
|
|
const rawData = this.work(workId, {inc: 'tags+artist-rels'});
|
|
|
|
|
const work : mbWork = {
|
|
|
|
|
const rawData = this.work(workId, { inc: 'tags+artist-rels' });
|
|
|
|
|
const work: mbWork = {
|
|
|
|
|
id: rawData.id,
|
|
|
|
|
title: rawData.title,
|
|
|
|
|
type: (rawData.type as mbWorkType) || mbWorkType.OTHER,
|
|
|
|
|
artist: rawData.relations.map((relation: {artist: RawArtist}) => {
|
|
|
|
|
artist: rawData.relations.map((relation: Relation): mbArtist => {
|
|
|
|
|
return {
|
|
|
|
|
id: relation.artist.id,
|
|
|
|
|
name: relation.artist.name,
|
|
|
|
|
sortName: relation.artist["sort-name"],
|
|
|
|
|
type: (relation.artist.type as mbArtistType) || mbArtistType.OTHER
|
|
|
|
|
}
|
|
|
|
|
sortName: relation.artist['sort-name'],
|
|
|
|
|
type: (relation.artist.type as mbArtistType) || mbArtistType.OTHER,
|
|
|
|
|
tags: relation.artist.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
tags: rawData.tags.map((tag: Tag) => tag.name),
|
|
|
|
|
};
|
|
|
|
|
return work;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new Error(`[MusicBrainz] Failed to fetch work details: ${e.message}`);
|
|
|
|
|
throw new Error(
|
|
|
|
|
`[MusicBrainz] Failed to fetch work details: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default MusicBrainz;
|
|
|
|
|