diff --git a/discovery/anilist.js b/discovery/anilist.js index cad2d02..e4ea4c7 100644 --- a/discovery/anilist.js +++ b/discovery/anilist.js @@ -136,7 +136,9 @@ function createClient({ endpoint = process.env.DISCOVERY_ANILIST_URL || 'https:/ }, async recs(id) { const nodes = mediaOf(await post(QUERIES.recs, { id })).recommendations?.nodes; - return entries(nodes, node => node.mediaRecommendation).filter(({ item }) => item.rating > 0) + // AniList returns a null mediaRecommendation when the recommended media was removed: valid, just drop it. + const present = need(nodes).filter(node => !isObject(node) || node.mediaRecommendation !== null); + return entries(present, node => node.mediaRecommendation).filter(({ item }) => item.rating > 0) .map(({ item, record }) => ({ anilistId: record.anilistId, rating: item.rating, record })); }, async creator(staffId) { diff --git a/test/discovery-anilist.test.js b/test/discovery-anilist.test.js index 7a88776..57f3ce3 100644 --- a/test/discovery-anilist.test.js +++ b/test/discovery-anilist.test.js @@ -75,6 +75,13 @@ test('requests refuse redirects', async () => { await assert.rejects(clientFor(redirected).title(1), e => e instanceof AniListSkip && e.reason === 'upstream'); }); +test('recs drops recommendations whose media was removed', async () => { + const recs = fixture('recs'); + recs.data.Media.recommendations.nodes = [recs.data.Media.recommendations.nodes[0], { rating: 10, mediaRecommendation: null }]; + const out = await clientFor(() => json(recs)).recs(101); + assert.deepEqual(out.map(e => e.anilistId), [201]); +}); + test('one malformed item invalidates a list response', async () => { const invalid = e => e instanceof AniListSkip && e.reason === 'invalid'; const recs = fixture('recs');