First Working Search Prototype !

pull/3800/merge^2
Anatole Sot 4 months ago
parent 6fa4b36a3e
commit 1afc8d39ef

@ -1,3 +1,11 @@
import logger from '@server/logger';
import type {
ArtistSearchResponse,
RecordingSearchResponse,
ReleaseGroupSearchResponse,
ReleaseSearchResponse,
WorkSearchResponse,
} from 'nodebrainz';
import BaseNodeBrainz from 'nodebrainz';
import type {
Artist,
@ -179,13 +187,13 @@ function convertRelease(release: Release): mbRelease {
media_type: 'release',
id: release.id,
title: release.title,
artist: release['artist-credit'].map(convertArtistCredit),
artist: (release['artist-credit'] ?? []).map(convertArtistCredit),
date:
release['release-events'] && release['release-events'].length > 0
? new Date(String(release['release-events'][0].date))
: undefined,
tracks: release.media.flatMap(convertMedium),
tags: release.tags.map(convertTag),
tracks: (release.media ?? []).flatMap(convertMedium),
tags: (release.tags ?? []).map(convertTag),
};
}
@ -194,12 +202,12 @@ function convertReleaseGroup(releaseGroup: Group): mbReleaseGroup {
media_type: 'release-group',
id: releaseGroup.id,
title: releaseGroup.title,
artist: releaseGroup['artist-credit'].map(convertArtistCredit),
artist: (releaseGroup['artist-credit'] ?? []).map(convertArtistCredit),
type:
(releaseGroup['primary-type'] as mbReleaseGroupType) ||
mbReleaseGroupType.OTHER,
firstReleased: new Date(releaseGroup['first-release-date']),
tags: releaseGroup.tags.map((tag: Tag) => tag.name),
tags: (releaseGroup.tags ?? []).map((tag: Tag) => tag.name),
};
}
@ -208,10 +216,10 @@ function convertRecording(recording: Recording): mbRecording {
media_type: 'recording',
id: recording.id,
title: recording.title,
artist: recording['artist-credit'].map(convertArtistCredit),
artist: (recording['artist-credit'] ?? []).map(convertArtistCredit),
length: recording.length,
firstReleased: new Date(recording['first-release-date']),
tags: recording.tags.map(convertTag),
tags: (recording.tags ?? []).map(convertTag),
};
}
@ -222,11 +230,11 @@ function convertArtist(artist: Artist): mbArtist {
name: artist.name,
sortName: artist['sort-name'],
type: (artist.type as mbArtistType) || mbArtistType.OTHER,
recordings: artist.recordings.map(convertRecording),
releases: artist.releases.map(convertRelease),
releaseGroups: artist['release-groups'].map(convertReleaseGroup),
works: artist.works.map(convertWork),
tags: artist.tags.map(convertTag),
recordings: (artist.recordings ?? []).map(convertRecording),
releases: (artist.releases ?? []).map(convertRelease),
releaseGroups: (artist['release-groups'] ?? []).map(convertReleaseGroup),
works: (artist.works ?? []).map(convertWork),
tags: (artist.tags ?? []).map(convertTag),
};
}
@ -236,8 +244,8 @@ function convertWork(work: Work): mbWork {
id: work.id,
title: work.title,
type: (work.type as mbWorkType) || mbWorkType.OTHER,
artist: work.relations.map(convertRelation),
tags: work.tags.map(convertTag),
artist: (work.relations ?? []).map(convertRelation),
tags: (work.tags ?? []).map(convertTag),
};
}
@ -248,7 +256,7 @@ function convertArtistCredit(artistCredit: ArtistCredit): mbArtist {
name: artistCredit.artist.name,
sortName: artistCredit.artist['sort-name'],
type: (artistCredit.artist.type as mbArtistType) || mbArtistType.OTHER,
tags: artistCredit.artist.tags.map(convertTag),
tags: (artistCredit.artist.tags ?? []).map(convertTag),
};
}
@ -259,7 +267,7 @@ function convertRelation(relation: Relation): mbArtist {
name: relation.artist.name,
sortName: relation.artist['sort-name'],
type: (relation.artist.type as mbArtistType) || mbArtistType.OTHER,
tags: relation.artist.tags.map(convertTag),
tags: (relation.artist.tags ?? []).map(convertTag),
};
}
@ -276,9 +284,9 @@ function convertTrack(track: Track): mbRecording {
media_type: 'recording',
id: track.id,
title: track.title,
artist: track.recording['artist-credit'].map(convertArtistCredit),
artist: (track.recording['artist-credit'] ?? []).map(convertArtistCredit),
length: track.recording.length,
tags: track.recording.tags.map(convertTag),
tags: (track.recording.tags ?? []).map(convertTag),
};
}
@ -290,7 +298,7 @@ class MusicBrainz extends BaseNodeBrainz {
});
}
public searchMulti = (search: SearchOptions) => {
public searchMulti = async (search: SearchOptions) => {
try {
const artistSearch = searchOptionstoArtistSearchOptions(search);
const recordingSearch = searchOptionstoRecordingSearchOptions(search);
@ -298,11 +306,13 @@ class MusicBrainz extends BaseNodeBrainz {
searchOptionstoReleaseGroupSearchOptions(search);
const releaseSearch = searchOptionstoReleaseSearchOptions(search);
const workSearch = searchOptionstoWorkSearchOptions(search);
const artistResults = this.searchArtists(artistSearch);
const recordingResults = this.searchRecordings(recordingSearch);
const releaseGroupResults = this.searchReleaseGroups(releaseGroupSearch);
const releaseResults = this.searchReleases(releaseSearch);
const workResults = this.searchWorks(workSearch);
const artistResults = await this.searchArtists(artistSearch);
const recordingResults = await this.searchRecordings(recordingSearch);
const releaseGroupResults = await this.searchReleaseGroups(
releaseGroupSearch
);
const releaseResults = await this.searchReleases(releaseSearch);
const workResults = await this.searchWorks(workSearch);
const combinedResults = {
status: 'ok',
@ -326,54 +336,122 @@ class MusicBrainz extends BaseNodeBrainz {
}
};
public searchArtists = (search: ArtistSearchOptions) => {
public searchArtists = async (
search: ArtistSearchOptions
): Promise<mbArtist[]> => {
try {
const rawResults = this.search('artist', search) as Artist[];
const results = rawResults.map(convertArtist);
return results;
return await new Promise<mbArtist[]>((resolve, reject) => {
this.search('artist', search, (error, data) => {
if (error) {
reject(error);
} else {
const rawResults = data as unknown as ArtistSearchResponse;
const results = rawResults.artists.map(convertArtist);
resolve(results);
}
});
});
} catch (e) {
return [];
logger.error('Failed to search for artists', {
label: 'MusicBrainz',
message: e.message,
});
return new Promise<mbArtist[]>((resolve) => resolve([]));
}
};
public searchRecordings = (search: RecordingSearchOptions) => {
public searchRecordings = async (
search: RecordingSearchOptions
): Promise<mbRecording[]> => {
try {
const rawResults = this.search('recording', search) as Recording[];
const results = rawResults.map(convertRecording);
return results;
return await new Promise<mbRecording[]>((resolve, reject) => {
this.search('recording', search, (error, data) => {
if (error) {
reject(error);
} else {
const rawResults = data as unknown as RecordingSearchResponse;
const results = rawResults.recordings.map(convertRecording);
resolve(results);
}
});
});
} catch (e) {
return [];
logger.error('Failed to search for recordings', {
label: 'MusicBrainz',
message: e.message,
});
return new Promise<mbRecording[]>((resolve) => resolve([]));
}
};
public searchReleaseGroups = (
search: ReleaseGroupSearchOptions
): mbReleaseGroup[] => {
): Promise<mbReleaseGroup[]> => {
try {
const rawResults = this.search('release-group', search) as Group[];
const results = rawResults.map(convertReleaseGroup);
return results;
return new Promise<mbReleaseGroup[]>((resolve, reject) => {
this.search('release-group', search, (error, data) => {
if (error) {
reject(error);
} else {
const rawResults = data as unknown as ReleaseGroupSearchResponse;
const results =
rawResults['release-groups'].map(convertReleaseGroup);
resolve(results);
}
});
});
} catch (e) {
return [];
logger.error('Failed to search for release groups', {
label: 'MusicBrainz',
message: e.message,
});
return new Promise<mbReleaseGroup[]>((resolve) => resolve([]));
}
};
public searchReleases = (search: ReleaseSearchOptions): mbRelease[] => {
public searchReleases = (
search: ReleaseSearchOptions
): Promise<mbRelease[]> => {
try {
const rawResults = this.search('release', search) as Release[];
const results = rawResults.map(convertRelease);
return results;
return new Promise<mbRelease[]>((resolve, reject) => {
this.search('release', search, (error, data) => {
if (error) {
reject(error);
} else {
const rawResults = data as unknown as ReleaseSearchResponse;
const results = rawResults.releases.map(convertRelease);
resolve(results);
}
});
});
} catch (e) {
return [];
logger.error('Failed to search for releases', {
label: 'MusicBrainz',
message: e.message,
});
return new Promise<mbRelease[]>((resolve) => resolve([]));
}
};
public searchWorks = (search: WorkSearchOptions) => {
public searchWorks = (search: WorkSearchOptions): Promise<mbWork[]> => {
try {
const results = this.search('work', search);
return results;
return new Promise<mbWork[]>((resolve, reject) => {
this.search('work', search, (error, data) => {
if (error) {
reject(error);
} else {
const rawResults = data as unknown as WorkSearchResponse;
const results = rawResults.works.map(convertWork);
resolve(results);
}
});
});
} catch (e) {
return [];
logger.error('Failed to search for works', {
label: 'MusicBrainz',
message: e.message,
});
return new Promise<mbWork[]>((resolve) => resolve([]));
}
};

@ -242,16 +242,18 @@ export const mapPersonResult = (
export const mapReleaseGroupResult = (
releaseGroupResult: mbReleaseGroup,
media?: Media
): ReleaseGroupResult => ({
id: releaseGroupResult.id,
mediaType: releaseGroupResult.media_type,
type: releaseGroupResult.type,
title: releaseGroupResult.title,
artist: releaseGroupResult.artist.map((artist) => mapArtistResult(artist)),
tags: releaseGroupResult.tags,
posterPath: getPosterFromMB(releaseGroupResult),
mediaInfo: media,
});
): ReleaseGroupResult => {
return {
id: releaseGroupResult.id,
mediaType: releaseGroupResult.media_type,
type: releaseGroupResult.type,
title: releaseGroupResult.title,
artist: releaseGroupResult.artist.map((artist) => mapArtistResult(artist)),
tags: releaseGroupResult.tags,
posterPath: getPosterFromMB(releaseGroupResult),
mediaInfo: media ?? undefined,
};
};
export const mapArtistResult = (artist: mbArtist): ArtistResult => ({
id: artist.id,

@ -857,7 +857,7 @@ discoverRoutes.get('/musics', async (req, res, next) => {
try {
const query = QueryFilterOptions.parse(req.query);
const keywords = query.keywords;
const results = mb.searchReleaseGroups({
const results = await mb.searchReleaseGroups({
query: keywords ?? '',
limit: 20,
offset: (Number(query.page) - 1) * 20,
@ -867,14 +867,14 @@ discoverRoutes.get('/musics', async (req, res, next) => {
const media = await Media.getRelatedMedia(tmdbIds, mbIds);
return res.status(200).json({
page: query.page,
results: results.map((result) => {
results: results.map((result) =>
mapReleaseGroupResult(
result,
media.find(
(med) => med.mbId === result.id && med.mediaType === MediaType.MUSIC
)
);
}),
)
),
});
} catch (e) {
logger.debug('Something went wrong retrieving popular series', {

@ -7,6 +7,26 @@ declare module 'nodebrainz' {
SearchOptions,
Work,
} from 'server/api/musicbrainz/interfaces';
interface RawSearchResponse {
created: string;
count: number;
offset: number;
}
export interface ArtistSearchResponse extends RawSearchResponse {
artists: Artist[];
}
export interface ReleaseSearchResponse extends RawSearchResponse {
releases: Release[];
}
export interface RecordingSearchResponse extends RawSearchResponse {
recordings: Recording[];
}
export interface ReleaseGroupSearchResponse extends RawSearchResponse {
'release-groups': Group[];
}
export interface WorkSearchResponse extends RawSearchResponse {
works: Work[];
}
export default class BaseNodeBrainz {
constructor(options: { userAgent: string });
artist(artistId: string, { inc }: { inc: string }): Artist;
@ -16,7 +36,16 @@ declare module 'nodebrainz' {
work(workId: string, { inc }: { inc: string }): Work;
search(
type: string,
search: SearchOptions
): Artist[] | Recording[] | Release[] | Group[] | Work[] | null;
search: SearchOptions,
callback: (
err: Error,
data:
| ArtistSearchResponse
| ReleaseSearchResponse
| RecordingSearchResponse
| ReleaseGroupSearchResponse
| WorkSearchResponse
) => void
): Promise<Artist[] | Release[] | Recording[] | Group[] | Work[]>;
}
}

34244
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save