Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 36 additions & 23 deletions src/controllers/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MediaMetadata, { MediaMetadataInterface } from '../models/MediaMetadata';
import { SeasonMetadataInterface } from '../models/SeasonMetadata';
import { SeriesMetadataInterface } from '../models/SeriesMetadata';
import * as externalAPIHelper from '../services/external-api-helper';
import { traceLog } from '../helpers/logging';

const THIRTY_DAYS_IN_MILLISECONDS = 24 * 60 * 60 * 30 * 1000;

Expand Down Expand Up @@ -258,15 +259,40 @@ export const getVideoV2 = async(ctx): Promise<MediaMetadataInterface> => {
if (title) {
searchMatch = language ? language + '@' + title : title;
const titleQuery: GetVideoFilter = { searchMatches: { $in: [searchMatch] } };
const titleFailedQuery: FailedLookupsInterface = { title };

// there will be a way to make this automatic but I cbf rn
const titleFailedQuery: {
episode?: string;
failedValidation?: boolean;
imdbID?: string;
language?: string | { $exists: boolean };
season?: string;
startYear?: string;
title?: string;
tmdbID?: number;
type?: string;
year?: string | { $exists: boolean };
count?: number;
reason?: string;

// Added automatically:
createdAt?: string;
updatedAt?: string;
} = { title };

if (language) {
titleFailedQuery.language = language;
} else {
titleFailedQuery.language = { $exists: false };
}

if (year) {
titleQuery.year = year;
titleFailedQuery.year = year;
} else {
titleFailedQuery.year = { $exists: false };
}

if (episode) {
titleQuery.episode = episode;
titleFailedQuery.episode = episode;
Expand All @@ -281,39 +307,32 @@ export const getVideoV2 = async(ctx): Promise<MediaMetadataInterface> => {

const existingResult = await MediaMetadata.findOne({ $or: query }, null, { lean: true }).exec();
if (existingResult) {
if (process.env.VERBOSE === 'true') {
console.trace('found existingResult', query, existingResult);
}
traceLog('found existingResult', { query, existingResult });

// we have an existing metadata record, so return it
return ctx.body = existingResult;
}

const existingFailedResult = await FailedLookups.findOne({ $or: failedQuery }, null, { lean: true }).exec();
if (existingFailedResult) {
// we have an existing failure record, so increment it, and throw not found error
if (process.env.VERBOSE === 'true') {
console.trace('found existingFailedResult', existingFailedResult, failedQuery);
}
traceLog('found existingFailedResult', { existingFailedResult, failedQuery });
await FailedLookups.updateOne({ _id: existingFailedResult._id }, { $inc: { count: 1 } }).exec();
throw new MediaNotFoundError();
}

// the database does not have a record of this file, so begin search for metadata on TMDB.

const failedLookupQuery: FailedLookupsInterface = { episode, imdbID, season, title, year };
const failedLookupQuery: FailedLookupsInterface = { episode, imdbID, season, title, year, language };

let tmdbData: MediaMetadataInterface;
try {
tmdbData = await externalAPIHelper.getFromTMDBAPI(title, language, imdbIdToSearch, yearNumber, seasonNumber, episodeNumbers);
imdbIdToSearch = imdbIdToSearch || tmdbData?.imdbID;
if (process.env.VERBOSE === 'true') {
console.trace('found tmdbData and imdbIdToSearch', query, tmdbData, imdbIdToSearch);
}
traceLog('found tmdbData and imdbIdToSearch', { query, tmdbData, imdbIdToSearch });
} catch (err) {
if (err instanceof RateLimitError) {
if (process.env.VERBOSE === 'true') {
console.trace(err);
}
traceLog(err);
throw err;
}

Expand All @@ -329,17 +348,13 @@ export const getVideoV2 = async(ctx): Promise<MediaMetadataInterface> => {
if (!imdbID && imdbIdToSearch) {
const existingResult = await MediaMetadata.findOne({ imdbID: imdbIdToSearch }, null, { lean: true }).exec();
if (existingResult) {
if (process.env.VERBOSE === 'true') {
console.trace('found existingResult from IMDb ID from TMDB', existingResult, imdbIdToSearch);
}
traceLog('found existingResult from IMDb ID from TMDB', { existingResult, imdbIdToSearch });
return ctx.body = await addSearchMatchByIMDbID(imdbIdToSearch, searchMatch);
}
}

if (!tmdbData || _.isEmpty(tmdbData)) {
if (process.env.VERBOSE === 'true') {
console.trace('No data was found on TMDB for this query', title, language, imdbIdToSearch, yearNumber, seasonNumber, episodeNumbers);
}
traceLog('No data was found on TMDB for this query', { title, language, imdbIdToSearch, yearNumber, seasonNumber, episodeNumbers });
const reason = `getVideoV2 got no tmdb data for ${title}, ${language}, ${imdbIdToSearch}, ${yearNumber}, ${seasonNumber}, ${episodeNumbers}`;
await FailedLookups.updateOne(failedLookupQuery, { $inc: { count: 1 }, reason }, { upsert: true, setDefaultsOnInsert: true }).exec();
throw new MediaNotFoundError();
Expand Down Expand Up @@ -395,9 +410,7 @@ export const getVideoV2 = async(ctx): Promise<MediaMetadataInterface> => {
return ctx.body = leanMeta;
} catch (e) {
console.error(e, tmdbData);
if (process.env.VERBOSE === 'true') {
console.trace('No data was found on TMDB for this query in final getVideoV2 catch', e);
}
traceLog('No data was found on TMDB for this query in final getVideoV2 catch', e);
const reason = `getVideoV2 caught an exception ${e}, for ${tmdbData}`;
await FailedLookups.updateOne(failedLookupQuery, { $inc: { count: 1 }, reason }, { upsert: true, setDefaultsOnInsert: true }).exec();
throw new MediaNotFoundError();
Expand Down
8 changes: 8 additions & 0 deletions src/helpers/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Log text in trace mode if VERBOSE env var is true
*/
export const traceLog = (text: string | Error, extraInfo: unknown = null) => {
if (process.env.VERBOSE === 'true') {
console.trace(text, extraInfo);
}
}
87 changes: 32 additions & 55 deletions src/services/external-api-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Episode, EpisodeRequest, ExternalId, SearchMovieRequest, SearchTvReques

import { tmdb } from './tmdb-api';
import { ValidationError } from '../helpers/customErrors';
import { traceLog } from '../helpers/logging';
import CollectionMetadata, { CollectionMetadataInterface } from '../models/CollectionMetadata';
import FailedLookups, { FailedLookupsInterface } from '../models/FailedLookups';
import LocalizeMetadata, { LocalizeMetadataInterface } from '../models/LocalizeMetadata';
Expand Down Expand Up @@ -106,7 +107,7 @@ export const getSeriesMetadata = async(
title?: string;
tmdbID?: number;
type?: string;
year?: string;
year?: string | { $exists: boolean };
count?: number;
reason?: string;

Expand Down Expand Up @@ -175,9 +176,7 @@ export const getSeriesMetadata = async(

// Return early for previously-failed lookups
if (await FailedLookups.findOne(failedLookupQuery, '_id', { lean: true }).exec()) {
if (process.env.VERBOSE === 'true') {
console.trace('Found previously-failed lookup', failedLookupQuery);
}
traceLog('Found previously-failed lookup', failedLookupQuery);
await FailedLookups.updateOne(failedLookupQuery, { $inc: { count: 1 } }).exec();

// Also store a failed result for the title that the client sent
Expand All @@ -189,9 +188,7 @@ export const getSeriesMetadata = async(
}

// Return any previous match
if (process.env.VERBOSE === 'true') {
console.trace('Looking for TV series in db', parsedTitle);
}
traceLog('Looking for TV series in db', { parsedTitle });
const seriesMetadata = await SeriesMetadata.findOne(titleQuery, null, { lean: true }).sort(sortBy).exec();
if (seriesMetadata) {
// Also cache the result for the title that the client sent, if this is an automatic re-attempt with an appended year (see below)
Expand All @@ -202,37 +199,31 @@ export const getSeriesMetadata = async(
{ returnDocument: 'after', lean: true },
).exec();
}
if (process.env.VERBOSE === 'true') {
console.trace('Found TV series', seriesMetadata);
}

traceLog('Found TV series', seriesMetadata);

return seriesMetadata;
}

// Start TMDB lookups
if (process.env.VERBOSE === 'true') {
console.trace('Looking for TV seriesTMDBID on TMDB', parsedTitle);
}
traceLog('Looking for TV seriesTMDBID on TMDB', { parsedTitle });

const seriesTMDBID = await getSeriesTMDBIDFromTMDBAPI(null, parsedTitle, language, yearNumber);
if (seriesTMDBID) {
if (process.env.VERBOSE === 'true') {
console.trace('Found TV seriesTMDBID for parsedTitle', seriesTMDBID);
}
traceLog('Found TV seriesTMDBID for parsedTitle', { seriesTMDBID });

// See if we have an existing record for the now-known media.
const existingResult = await SeriesMetadata.findOne({ tmdbID: seriesTMDBID }, null, { lean: true }).exec();
if (existingResult) {
if (process.env.VERBOSE === 'true') {
console.trace('Found existingResult for parsedTitle', existingResult);
}
traceLog('Found existingResult for parsedTitle', existingResult);

return await SeriesMetadata.findOneAndUpdate(
{ tmdbID: seriesTMDBID },
{ $addToSet: { searchMatches: searchMatch } },
{ returnDocument: 'after', lean: true },
).exec();
} else {
if (process.env.VERBOSE === 'true') {
console.trace('No existingResult for parsedTitle', existingResult);
}
traceLog('No existingResult for parsedTitle', existingResult);
}

// We do not have an existing record for that series, get the full result from the TMDB API
Expand All @@ -241,13 +232,12 @@ export const getSeriesMetadata = async(
id: seriesTMDBID,
};

if (process.env.VERBOSE === 'true') {
console.trace('Looking for series on TMDB', parsedTitle);
}
traceLog('Looking for series on TMDB', { parsedTitle });

const tmdbResponse = await tmdb.tvInfo(seriesRequest);
if (process.env.VERBOSE === 'true') {
console.trace('Found series on TMDB', tmdbResponse);
}

traceLog('Found series on TMDB', tmdbResponse);

tmdbData = mapper.parseTMDBAPISeriesResponse(tmdbResponse);
}
// End TMDB lookups
Expand Down Expand Up @@ -402,44 +392,35 @@ export const getFromTMDBAPI = async(movieOrSeriesTitle?: string, language?: stri
const episodeIMDbID = movieOrEpisodeIMDbID;
let seriesTMDBID: string | number;
if (episodeIMDbID) {
if (process.env.VERBOSE === 'true') {
console.trace('Looking for an episode with the IMDb ID', episodeIMDbID);
}
traceLog('Looking for an episode with the IMDb ID', { episodeIMDbID });

const findResult = await tmdb.find({ id: episodeIMDbID, external_source: ExternalId.ImdbId });
if (findResult?.tv_episode_results && findResult?.tv_episode_results[0]) {
const tvEpisodeResult = findResult.tv_episode_results[0] as SimpleEpisode;
seriesTMDBID = tvEpisodeResult?.show_id;
if (process.env.VERBOSE === 'true') {
console.trace('Found tvEpisodeResult and seriesTMDBID', tvEpisodeResult, seriesTMDBID);
}

traceLog('Found tvEpisodeResult and seriesTMDBID', { tvEpisodeResult, seriesTMDBID });
} else {
if (process.env.VERBOSE === 'true') {
console.trace('Did not find an episode with the IMDb ID', episodeIMDbID);
}
traceLog('Did not find an episode with the IMDb ID', { episodeIMDbID });
}
} else {
if (process.env.VERBOSE === 'true') {
console.trace('Looking for seriesTMDBID with', movieOrSeriesTitle, language, yearString);
}
traceLog('Looking for seriesTMDBID with', { movieOrSeriesTitle, language, yearString });

const seriesMetadata = await getSeriesMetadata(null, movieOrSeriesTitle, language, yearString);
seriesTMDBID = seriesMetadata?.tmdbID;
}

if (!seriesTMDBID) {
if (process.env.VERBOSE === 'true') {
console.trace('Did not find seriesTMDBID with', movieOrSeriesTitle, language, yearString);
}
traceLog('Did not find seriesTMDBID with', { movieOrSeriesTitle, language, yearString });

return null;
} else {
if (process.env.VERBOSE === 'true') {
console.trace('Found seriesTMDBID ' + seriesTMDBID + 'with', movieOrSeriesTitle, language, yearString);
}
traceLog('Found seriesTMDBID ' + seriesTMDBID + 'with', { movieOrSeriesTitle, language, yearString });
}

for (let i = 0; i < episodeNumbers.length; i++) {
if (process.env.VERBOSE === 'true') {
console.trace('Looking for episode number ' + episodeNumbers[i] + 'with', seriesTMDBID, seasonNumber);
}
traceLog('Looking for episode number ' + episodeNumbers[i] + 'with', { seriesTMDBID, seasonNumber });

const episodeRequest: EpisodeRequest = {
append_to_response: 'images,external_ids,credits',
episode_number: episodeNumbers[i],
Expand All @@ -461,16 +442,12 @@ export const getFromTMDBAPI = async(movieOrSeriesTitle?: string, language?: stri
if (tmdbSeriesData?.imdb_id) {
metadata.seriesIMDbID = tmdbSeriesData.imdb_id;
}
if (process.env.VERBOSE === 'true') {
console.trace('Found metadata', metadata);
}
traceLog('Found metadata', metadata);
} else {
metadata.title = metadata.title ? metadata.title + ' & ' + tmdbData.name : tmdbData.name;
}
} else {
if (process.env.VERBOSE === 'true') {
console.trace('Did not find tmdbData from', episodeRequest);
}
traceLog('Did not find tmdbData from', episodeRequest);
}
}
} else {
Expand Down
Loading