diff --git a/src/services/external-api-helper.ts b/src/services/external-api-helper.ts index 95c719a6c..47742f907 100644 --- a/src/services/external-api-helper.ts +++ b/src/services/external-api-helper.ts @@ -2,7 +2,7 @@ import { jaroWinkler } from '@skyra/jaro-winkler'; import * as episodeParser from 'episode-parser'; import * as _ from 'lodash'; import { FlattenMaps, Types } from 'mongoose'; -import { Episode, EpisodeRequest, ExternalId, SearchMovieRequest, SearchTvRequest, SimpleEpisode, TvExternalIdsResponse } from 'moviedb-promise/dist/request-types'; +import { Episode, EpisodeRequest, ExternalId, SearchMovieRequest, SearchTvRequest, SimpleEpisode, TvExternalIdsResponse, TvResult } from 'moviedb-promise/dist/request-types'; import { tmdb } from './tmdb-api'; import { ValidationError } from '../helpers/customErrors'; @@ -29,11 +29,27 @@ const getSeriesTMDBIDFromTMDBAPI = async(imdbID?: string, seriesTitle?: string, if (language) { tmdbQuery.language = language; } + const searchResults = await tmdb.searchTv(tmdbQuery); - if (searchResults?.results && searchResults.results[0] && searchResults.results[0].id) { - const searchResult = searchResults.results[0]; + if (searchResults?.results && searchResults.results[0]) { + let searchResult: TvResult; + let didMatchYear: boolean; if (year) { + const resultWithMatchingYear = _.find(searchResults.results, function(result) { + return result.first_air_date.substring(0, 4) === year.toString(); + }); + if (resultWithMatchingYear?.id) { + searchResult = resultWithMatchingYear; + didMatchYear = true; + } + } + + if (!searchResult) { + searchResult = searchResults.results[0]; + } + + if (didMatchYear) { // a wrong year could cause a wrong result, so also do a similarity check to be sure const resultNameLowerCase = searchResult.name.toLowerCase(); const requestNameLowerCase = seriesTitle.toLowerCase(); diff --git a/test/e2e/media-series.spec.ts b/test/e2e/media-series.spec.ts index 6309da6e0..7d0ce84e2 100644 --- a/test/e2e/media-series.spec.ts +++ b/test/e2e/media-series.spec.ts @@ -149,9 +149,15 @@ describe('Media Metadata endpoints', () => { expect(response.data).toHaveProperty('title', 'Galactica 1980'); }); - it('should return series even when the year is when the episode aired, not the series start year', async() => { - const response = await axios.get(`${appUrl}/api/media/series/v2?title=From&year=2023`) as UmsApiSeriesAxiosResponse; - expect(response.data).toHaveProperty('title', 'FROM'); + // this used to return a result but it was really a workaround for a client bug, it should not return + it('should NOT return series when the year is when the episode aired, not the series start year', async() => { + let error; + try { + await axios.get(`${appUrl}/api/media/series/v2?title=From&year=2023`) as UmsApiSeriesAxiosResponse; + } catch (err) { + error = err; + } + expect(error.message).toBe('Request failed with status code 404'); }); it('should not return series when the year has no overlap with episode air dates', async() => { diff --git a/test/e2e/media-video.spec.ts b/test/e2e/media-video.spec.ts index 80f0b4fe5..1370d4667 100644 --- a/test/e2e/media-video.spec.ts +++ b/test/e2e/media-video.spec.ts @@ -53,6 +53,15 @@ const EPISODE_PRISONBREAK = { 'seriesIMDbID': 'tt0455275', }; +const EPISODE_DOCTORWHO = { + 'episodeTitle': 'Lucky Day', + 'title': 'Doctor Who', + 'season': '2', + 'episode': '4', + "seriesIMDbID": "tt31433814", + 'year': 2024, +}; + const EPISODE_AVATAR = { 'episodeTitle': 'The Boiling Rock (1) & The Boiling Rock (2)', 'osdbHash': 'de334f38f153fb6f', @@ -211,7 +220,7 @@ describe('get by all', () => { expect(response.data.seriesIMDbID).toEqual(EPISODE_LOST.seriesIMDbID); }); - test('should return an episode by when passed all possible params, from source APIs then store', async() => { + test('should return an episode by all possible params, from source APIs then store', async() => { const url = `${appUrl}/api/media/video/v2?`+ `osdbHash=${EPISODE_PRISONBREAK.osdbHash}`+ `&filebytesize=${EPISODE_PRISONBREAK.filebytesize}`+ @@ -232,6 +241,25 @@ describe('get by all', () => { expect(response.data.seriesIMDbID).toEqual(EPISODE_PRISONBREAK.seriesIMDbID); }); + test('should return the episode from the correct series, when multiple series exist with different years, from source APIs then store', async() => { + const url = `${appUrl}/api/media/video/v2?`+ + `title=${EPISODE_DOCTORWHO.title}`+ + `&season=${EPISODE_DOCTORWHO.season}`+ + `&episode=${EPISODE_DOCTORWHO.episode}`+ + `&year=${EPISODE_DOCTORWHO.year}`; + let response = await axios.get(url) as UmsApiMediaAxiosResponse; + expect(response.data.title).toEqual(EPISODE_DOCTORWHO.episodeTitle); + expect(response.data.type).toEqual('episode'); + + expect(response.data.seriesIMDbID).toEqual(EPISODE_DOCTORWHO.seriesIMDbID); + + // subsequent calls should return MongoDB result rather than calling external apis + response = await axios.get(url); + expect(response.data.title).toEqual(EPISODE_DOCTORWHO.episodeTitle); + expect(response.data.type).toEqual('episode'); + expect(response.data.seriesIMDbID).toEqual(EPISODE_DOCTORWHO.seriesIMDbID); + }); + test('should return two episodes when passed all possible params, from source APIs then store', async() => { const url = `${appUrl}/api/media/video/v2?`+ `osdbHash=${EPISODE_AVATAR.osdbHash}`+