Files
channels-seerr/server/api/indexer/tvdb/index.ts
2024-07-27 00:11:22 +02:00

227 lines
5.7 KiB
TypeScript

import ExternalAPI from '@server/api/externalapi';
import type { TvShowIndexer } from '@server/api/indexer';
import TheMovieDb from '@server/api/indexer/themoviedb';
import type {
TmdbSeasonWithEpisodes,
TmdbTvDetails,
} from '@server/api/indexer/themoviedb/interfaces';
import type {
TvdbEpisodeTranslation,
TvdbLoginResponse,
TvdbSeasonDetails,
TvdbTvDetails,
} from '@server/api/indexer/tvdb/interfaces';
import cacheManager from '@server/lib/cache';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
class Tvdb extends ExternalAPI implements TvShowIndexer {
static instance: Tvdb;
private dateTokenExpires?: Date;
private pin?: string;
private constructor(apiKey: string, pin?: string) {
super(
'https://api4.thetvdb.com/v4',
{
apiKey: apiKey,
},
{
nodeCache: cacheManager.getCache('tvdb').data,
rateLimit: {
maxRPS: 50,
id: 'tvdb',
},
}
);
this.pin = pin;
}
public static async getInstance() {
if (!this.instance) {
const settings = getSettings();
if (!settings.tvdb.apiKey) {
throw new Error('TVDB API key is not set');
}
this.instance = new Tvdb(settings.tvdb.apiKey, settings.tvdb.pin);
await this.instance.login();
logger.info(
'Tvdb instance created with token => ' +
this.instance.defaultHeaders.Authorization
);
}
return this.instance;
}
async login() {
try {
const res = await this.post<TvdbLoginResponse>('/login', {
apiKey: this.params.apiKey,
pin: this.pin,
});
this.defaultHeaders.Authorization = `Bearer ${res.data.token}`;
this.dateTokenExpires = new Date();
this.dateTokenExpires.setMonth(this.dateTokenExpires.getMonth() + 1);
return res;
} catch (error) {
throw new Error(`[TVDB] Login failed: ${error.message}`);
}
}
public getTvShow = async ({
tvId,
language = 'en',
}: {
tvId: number;
language?: string;
}): Promise<TmdbTvDetails> => {
try {
const tmdb = new TheMovieDb();
const tmdbTvShow = await tmdb.getTvShow({ tvId: tvId });
const tvdbId = tmdbTvShow.external_ids.tvdb_id;
if (!tvdbId) {
return tmdbTvShow;
}
const data = await this.get<TvdbTvDetails>(
`/series/${tvdbId}/extended`,
{
short: 'true',
},
43200
);
const correctSeasons = data.data.seasons.filter(
(season: TvdbSeasonDetails) =>
season.id && season.number > 0 && season.type.name === 'Aired Order'
);
tmdbTvShow.seasons = [];
for (const season of correctSeasons) {
if (season.id) {
logger.info(`Fetching TV season ${season.id}`);
try {
const tvdbSeason = await this.getTvSeason({
tvId: tvdbId,
seasonNumber: season.id,
language,
});
const seasonData = {
id: season.id,
episode_count: tvdbSeason.episodes.length,
name: tvdbSeason.name,
overview: tvdbSeason.overview,
season_number: season.number,
poster_path: '',
air_date: '',
image: tvdbSeason.poster_path,
};
tmdbTvShow.seasons.push(seasonData);
} catch (error) {
logger.error(
`Failed to get season ${season.id} for TV show ${tvdbId}: ${error.message}`,
{
label: 'Tvdb',
message: `Failed to get season ${season.id} for TV show ${tvdbId}`,
}
);
}
}
}
return tmdbTvShow;
} catch (error) {
throw new Error(
`[TVDB] Failed to fetch TV show details: ${error.message}`
);
}
};
getEpisode = async (
episodeId: number,
language: string
): Promise<TvdbEpisodeTranslation> => {
try {
const tvdbEpisode = await this.get<TvdbEpisodeTranslation>(
`/episodes/${episodeId}/translations/${language}`,
{},
43200
);
return tvdbEpisode;
} catch (error) {
throw new Error(
`[TVDB] Failed to fetch TV episode details: ${error.message}`
);
}
};
public getTvSeason = async ({
tvId,
seasonNumber,
language = 'en',
}: {
tvId: number;
seasonNumber: number;
language?: string;
}): Promise<TmdbSeasonWithEpisodes> => {
if (seasonNumber === 0) {
return {
episodes: [],
external_ids: {
tvdb_id: tvId,
},
name: '',
overview: '',
id: seasonNumber,
air_date: '',
season_number: 0,
};
}
try {
const tvdbSeason = await this.get<TvdbSeasonDetails>(
`/seasons/${seasonNumber}/extended`,
{ lang: language },
43200
);
const episodes = tvdbSeason.data.episodes.map((episode) => ({
id: episode.id,
air_date: episode.aired,
episode_number: episode.number,
name: episode.name,
overview: episode.overview || '',
season_number: episode.seasonNumber,
production_code: '',
show_id: tvId,
still_path: episode.image,
vote_average: 1,
vote_cuont: 1,
}));
return {
episodes: episodes,
external_ids: {
tvdb_id: tvdbSeason.seriesId,
},
name: '',
overview: '',
id: tvdbSeason.id,
air_date: tvdbSeason.year,
season_number: tvdbSeason.number,
};
} catch (error) {
throw new Error(
`[TVDB] Failed to fetch TV season details: ${error.message}`
);
}
};
}
export default Tvdb;