refactor: replace Promise.all with Promise.allSettled to handle external API failures more gracefully
This commit is contained in:
@@ -83,8 +83,7 @@ artistRoutes.get('/:id', async (req, res, next) => {
|
||||
|
||||
const mbIds = releaseGroupsToProcess.map((rg) => rg.mbid);
|
||||
|
||||
const [artistWikipedia, artistImages, relatedMedia, albumMetadata] =
|
||||
await Promise.all([
|
||||
const responses = await Promise.allSettled([
|
||||
musicbrainz
|
||||
.getArtistWikipediaExtract({
|
||||
artistMbid: req.params.id,
|
||||
@@ -101,6 +100,15 @@ artistRoutes.get('/:id', async (req, res, next) => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const artistWikipedia =
|
||||
responses[0].status === 'fulfilled' ? responses[0].value : null;
|
||||
const artistImages =
|
||||
responses[1].status === 'fulfilled' ? responses[1].value : null;
|
||||
const relatedMedia =
|
||||
responses[2].status === 'fulfilled' ? responses[2].value : [];
|
||||
const albumMetadata =
|
||||
responses[3].status === 'fulfilled' ? responses[3].value : [];
|
||||
|
||||
const metadataMap = new Map(
|
||||
albumMetadata.map((metadata) => [metadata.mbAlbumId, metadata])
|
||||
);
|
||||
|
||||
@@ -1294,17 +1294,18 @@ discoverRoutes.get('/music/artists', async (req, res, next) => {
|
||||
};
|
||||
}
|
||||
|
||||
const [artistImageResults] = await Promise.all([
|
||||
const responses = await Promise.allSettled([
|
||||
artistsNeedingImages.length > 0
|
||||
? (theAudioDb.batchGetArtistImages(
|
||||
artistsNeedingImages
|
||||
) as Promise<ArtistImageResults>)
|
||||
: ({} as ArtistImageResults),
|
||||
? theAudioDb.batchGetArtistImages(artistsNeedingImages)
|
||||
: Promise.resolve({} as ArtistImageResults),
|
||||
artistsForPersonMapping.length > 0
|
||||
? personMapper.batchGetMappings(artistsForPersonMapping)
|
||||
: {},
|
||||
: Promise.resolve({}),
|
||||
]);
|
||||
|
||||
const artistImageResults =
|
||||
responses[0].status === 'fulfilled' ? responses[0].value : {};
|
||||
|
||||
let updatedArtistMetadata = artistMetadata;
|
||||
if (artistsForPersonMapping.length > 0 || artistsNeedingImages.length > 0) {
|
||||
updatedArtistMetadata = await getRepository(MetadataArtist).find({
|
||||
|
||||
@@ -59,7 +59,7 @@ musicRoutes.get('/:id', async (req, res, next) => {
|
||||
metadataArtist,
|
||||
trackArtistMetadata,
|
||||
artistWikipedia,
|
||||
] = await Promise.all([
|
||||
] = await Promise.allSettled([
|
||||
getRepository(MetadataAlbum).findOne({
|
||||
where: { mbAlbumId: req.params.id },
|
||||
}),
|
||||
@@ -81,6 +81,17 @@ musicRoutes.get('/:id', async (req, res, next) => {
|
||||
: Promise.resolve(null),
|
||||
]);
|
||||
|
||||
const resolvedMetadataAlbum =
|
||||
metadataAlbum.status === 'fulfilled' ? metadataAlbum.value : null;
|
||||
const resolvedMetadataArtist =
|
||||
metadataArtist.status === 'fulfilled' ? metadataArtist.value : undefined;
|
||||
const resolvedTrackArtistMetadata =
|
||||
trackArtistMetadata.status === 'fulfilled'
|
||||
? trackArtistMetadata.value
|
||||
: [];
|
||||
const resolvedArtistWikipedia =
|
||||
artistWikipedia.status === 'fulfilled' ? artistWikipedia.value : null;
|
||||
|
||||
const trackArtistsToMap = albumDetails.mediums
|
||||
.flatMap((medium) => medium.tracks)
|
||||
.flatMap((track) =>
|
||||
@@ -88,7 +99,7 @@ musicRoutes.get('/:id', async (req, res, next) => {
|
||||
.filter((artist) => artist.artist_mbid)
|
||||
.filter(
|
||||
(artist) =>
|
||||
!trackArtistMetadata.some(
|
||||
!resolvedTrackArtistMetadata.some(
|
||||
(m) => m.mbArtistId === artist.artist_mbid && m.tmdbPersonId
|
||||
)
|
||||
)
|
||||
@@ -98,12 +109,13 @@ musicRoutes.get('/:id', async (req, res, next) => {
|
||||
}))
|
||||
);
|
||||
|
||||
const [artistImages, personMappingResult, updatedArtistMetadata] =
|
||||
await Promise.all([
|
||||
artistId && !metadataArtist?.tadbThumb && !metadataArtist?.tadbCover
|
||||
const responses = await Promise.allSettled([
|
||||
artistId &&
|
||||
!resolvedMetadataArtist?.tadbThumb &&
|
||||
!resolvedMetadataArtist?.tadbCover
|
||||
? theAudioDb.getArtistImages(artistId)
|
||||
: Promise.resolve(null),
|
||||
artistId && isPerson && !metadataArtist?.tmdbPersonId
|
||||
artistId && isPerson && !resolvedMetadataArtist?.tmdbPersonId
|
||||
? personMapper
|
||||
.getMapping(
|
||||
artistId,
|
||||
@@ -117,25 +129,34 @@ musicRoutes.get('/:id', async (req, res, next) => {
|
||||
where: { mbArtistId: In(trackArtistIds) },
|
||||
})
|
||||
)
|
||||
: Promise.resolve(trackArtistMetadata),
|
||||
: Promise.resolve(resolvedTrackArtistMetadata),
|
||||
]);
|
||||
|
||||
const artistImages =
|
||||
responses[0].status === 'fulfilled' ? responses[0].value : null;
|
||||
const personMappingResult =
|
||||
responses[1].status === 'fulfilled' ? responses[1].value : null;
|
||||
const updatedArtistMetadata =
|
||||
responses[2].status === 'fulfilled'
|
||||
? responses[2].value
|
||||
: resolvedTrackArtistMetadata;
|
||||
|
||||
const updatedMetadataArtist =
|
||||
personMappingResult && artistId
|
||||
? await getRepository(MetadataArtist).findOne({
|
||||
where: { mbArtistId: artistId },
|
||||
})
|
||||
: metadataArtist;
|
||||
: resolvedMetadataArtist;
|
||||
|
||||
const mappedDetails = mapMusicDetails(albumDetails, media, onUserWatchlist);
|
||||
const finalTrackArtistMetadata =
|
||||
updatedArtistMetadata || trackArtistMetadata;
|
||||
updatedArtistMetadata || resolvedTrackArtistMetadata;
|
||||
|
||||
return res.status(200).json({
|
||||
...mappedDetails,
|
||||
posterPath: metadataAlbum?.caaUrl ?? null,
|
||||
needsCoverArt: !metadataAlbum?.caaUrl,
|
||||
artistWikipedia,
|
||||
posterPath: resolvedMetadataAlbum?.caaUrl ?? null,
|
||||
needsCoverArt: !resolvedMetadataAlbum?.caaUrl,
|
||||
artistWikipedia: resolvedArtistWikipedia,
|
||||
artistThumb:
|
||||
updatedMetadataArtist?.tmdbThumb ??
|
||||
updatedMetadataArtist?.tadbThumb ??
|
||||
@@ -202,15 +223,20 @@ musicRoutes.get('/:id/artist', async (req, res, next) => {
|
||||
});
|
||||
}
|
||||
|
||||
const [artistDetails, cachedTheAudioDb, metadataArtist] = await Promise.all(
|
||||
[
|
||||
const responses = await Promise.allSettled([
|
||||
listenbrainzApi.getArtist(artistData.artist_mbid),
|
||||
theAudioDb.getArtistImagesFromCache(artistData.artist_mbid),
|
||||
metadataArtistRepository.findOne({
|
||||
where: { mbArtistId: artistData.artist_mbid },
|
||||
}),
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
const artistDetails =
|
||||
responses[0].status === 'fulfilled' ? responses[0].value : null;
|
||||
const cachedTheAudioDb =
|
||||
responses[1].status === 'fulfilled' ? responses[1].value : null;
|
||||
const metadataArtist =
|
||||
responses[2].status === 'fulfilled' ? responses[2].value : null;
|
||||
|
||||
if (!artistDetails) {
|
||||
return res.status(404).json({ status: 404, message: 'Artist not found' });
|
||||
@@ -229,8 +255,7 @@ musicRoutes.get('/:id/artist', async (req, res, next) => {
|
||||
const similarArtistIds =
|
||||
artistDetails.similarArtists?.artists?.map((a) => a.artist_mbid) ?? [];
|
||||
|
||||
const [relatedMedia, albumMetadata, similarArtistMetadata] =
|
||||
await Promise.all([
|
||||
const mediaResponses = await Promise.allSettled([
|
||||
Media.getRelatedMedia(req.user, releaseGroupIds),
|
||||
metadataAlbumRepository.find({
|
||||
where: { mbAlbumId: In(releaseGroupIds) },
|
||||
@@ -242,6 +267,13 @@ musicRoutes.get('/:id/artist', async (req, res, next) => {
|
||||
: Promise.resolve([]),
|
||||
]);
|
||||
|
||||
const relatedMedia =
|
||||
mediaResponses[0].status === 'fulfilled' ? mediaResponses[0].value : [];
|
||||
const albumMetadata =
|
||||
mediaResponses[1].status === 'fulfilled' ? mediaResponses[1].value : [];
|
||||
const similarArtistMetadata =
|
||||
mediaResponses[2].status === 'fulfilled' ? mediaResponses[2].value : [];
|
||||
|
||||
const albumMetadataMap = new Map(
|
||||
albumMetadata.map((metadata) => [metadata.mbAlbumId, metadata])
|
||||
);
|
||||
@@ -272,8 +304,7 @@ musicRoutes.get('/:id/artist', async (req, res, next) => {
|
||||
{ artistThumb: string | null; artistBackground: string | null }
|
||||
>;
|
||||
|
||||
const [artistImageResults, updatedArtistMetadata, artistImagesResult] =
|
||||
await Promise.all([
|
||||
const artistResponses = await Promise.allSettled([
|
||||
artistsNeedingImages.length > 0
|
||||
? theAudioDb.batchGetArtistImages(artistsNeedingImages)
|
||||
: ({} as ArtistImageResults),
|
||||
@@ -291,6 +322,17 @@ musicRoutes.get('/:id/artist', async (req, res, next) => {
|
||||
: Promise.resolve(null),
|
||||
]);
|
||||
|
||||
const artistImageResults =
|
||||
artistResponses[0].status === 'fulfilled' ? artistResponses[0].value : {};
|
||||
const updatedArtistMetadata =
|
||||
artistResponses[1].status === 'fulfilled'
|
||||
? artistResponses[1].value
|
||||
: similarArtistMetadata;
|
||||
const artistImagesResult =
|
||||
artistResponses[2].status === 'fulfilled'
|
||||
? artistResponses[2].value
|
||||
: null;
|
||||
|
||||
const relatedMediaMap = new Map(
|
||||
relatedMedia.map((media) => [media.mbId, media])
|
||||
);
|
||||
|
||||
@@ -96,8 +96,7 @@ personRoutes.get('/:id', async (req, res, next) => {
|
||||
|
||||
const allReleaseGroupIds = releaseGroupsToProcess.map((rg) => rg.mbid);
|
||||
|
||||
const [artistImagesPromise, relatedMedia, albumMetadata] =
|
||||
await Promise.all([
|
||||
const responses = await Promise.allSettled([
|
||||
!existingMetadata.tadbThumb && !existingMetadata.tadbCover
|
||||
? theAudioDb.getArtistImages(existingMetadata.mbArtistId)
|
||||
: Promise.resolve(null),
|
||||
@@ -109,6 +108,13 @@ personRoutes.get('/:id', async (req, res, next) => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const artistImagesPromise =
|
||||
responses[0].status === 'fulfilled' ? responses[0].value : null;
|
||||
const relatedMedia =
|
||||
responses[1].status === 'fulfilled' ? responses[1].value : [];
|
||||
const albumMetadata =
|
||||
responses[2].status === 'fulfilled' ? responses[2].value : [];
|
||||
|
||||
if (artistImagesPromise) {
|
||||
existingMetadata.tadbThumb = artistImagesPromise.artistThumb;
|
||||
existingMetadata.tadbCover = artistImagesPromise.artistBackground;
|
||||
|
||||
@@ -17,7 +17,6 @@ import { In } from 'typeorm';
|
||||
|
||||
const searchRoutes = Router();
|
||||
|
||||
const ITEMS_PER_PAGE = 20;
|
||||
searchRoutes.get('/', async (req, res, next) => {
|
||||
const queryString = req.query.query as string;
|
||||
const page = Number(req.query.page) || 1;
|
||||
@@ -42,7 +41,7 @@ searchRoutes.get('/', async (req, res, next) => {
|
||||
const theAudioDb = TheAudioDb.getInstance();
|
||||
const personMapper = TmdbPersonMapper.getInstance();
|
||||
|
||||
const [tmdbResults, albumResults, artistResults] = await Promise.all([
|
||||
const responses = await Promise.allSettled([
|
||||
tmdb.searchMulti({
|
||||
query: queryString,
|
||||
page,
|
||||
@@ -50,14 +49,23 @@ searchRoutes.get('/', async (req, res, next) => {
|
||||
}),
|
||||
musicbrainz.searchAlbum({
|
||||
query: queryString,
|
||||
limit: ITEMS_PER_PAGE,
|
||||
limit: 20,
|
||||
}),
|
||||
musicbrainz.searchArtist({
|
||||
query: queryString,
|
||||
limit: ITEMS_PER_PAGE,
|
||||
limit: 20,
|
||||
}),
|
||||
]);
|
||||
|
||||
const tmdbResults =
|
||||
responses[0].status === 'fulfilled'
|
||||
? responses[0].value
|
||||
: { page: 1, results: [], total_pages: 1, total_results: 0 };
|
||||
const albumResults =
|
||||
responses[1].status === 'fulfilled' ? responses[1].value : [];
|
||||
const artistResults =
|
||||
responses[2].status === 'fulfilled' ? responses[2].value : [];
|
||||
|
||||
const personIds = tmdbResults.results
|
||||
.filter(
|
||||
(result) => result.media_type === 'person' && !result.profile_path
|
||||
@@ -162,7 +170,7 @@ searchRoutes.get('/', async (req, res, next) => {
|
||||
{ artistThumb: string | null; artistBackground: string | null }
|
||||
>;
|
||||
|
||||
const [personMappingResults, artistImageResults] = await Promise.all([
|
||||
const externalApiResponses = await Promise.allSettled([
|
||||
artistsNeedingMapping.length > 0
|
||||
? personMapper.batchGetMappings(artistsNeedingMapping)
|
||||
: ({} as PersonMappingResult),
|
||||
@@ -171,6 +179,15 @@ searchRoutes.get('/', async (req, res, next) => {
|
||||
: ({} as ArtistImageResult),
|
||||
]);
|
||||
|
||||
const personMappingResults =
|
||||
externalApiResponses[0].status === 'fulfilled'
|
||||
? externalApiResponses[0].value
|
||||
: ({} as PersonMappingResult);
|
||||
const artistImageResults =
|
||||
externalApiResponses[1].status === 'fulfilled'
|
||||
? externalApiResponses[1].value
|
||||
: ({} as ArtistImageResult);
|
||||
|
||||
let updatedArtistsMetadataMap = artistsMetadataMap;
|
||||
if (
|
||||
(artistsNeedingMapping.length > 0 || artistsNeedingImages.length > 0) &&
|
||||
@@ -244,7 +261,7 @@ searchRoutes.get('/', async (req, res, next) => {
|
||||
const totalItems = tmdbResults.total_results + musicResults.length;
|
||||
const totalPages = Math.max(
|
||||
tmdbResults.total_pages,
|
||||
Math.ceil(totalItems / ITEMS_PER_PAGE)
|
||||
Math.ceil(totalItems / 20)
|
||||
);
|
||||
|
||||
const combinedResults =
|
||||
|
||||
Reference in New Issue
Block a user