fix: error during get episodes
This commit is contained in:
@@ -22,4 +22,6 @@ export interface TvShowIndexer {
|
||||
seasonNumber: number;
|
||||
language?: string;
|
||||
}): Promise<TmdbSeasonWithEpisodes>;
|
||||
|
||||
getSeasonIdentifier(req: any): number;
|
||||
}
|
||||
|
||||
@@ -198,6 +198,10 @@ class TheMovieDb extends ExternalAPI implements TvShowIndexer {
|
||||
}
|
||||
};
|
||||
|
||||
getSeasonIdentifier(req: any): number {
|
||||
return req.params.seasonNumber;
|
||||
}
|
||||
|
||||
public searchTvShows = async ({
|
||||
query,
|
||||
page = 1,
|
||||
@@ -342,6 +346,12 @@ class TheMovieDb extends ExternalAPI implements TvShowIndexer {
|
||||
}
|
||||
);
|
||||
|
||||
data.episodes = data.episodes.map((episode) => {
|
||||
if (episode.still_path !== '') {
|
||||
episode.still_path = `https://image.tmdb.org/t/p/original/${episode.still_path}`;
|
||||
}
|
||||
return episode;
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(`[TMDB] Failed to fetch TV show details: ${e.message}`);
|
||||
|
||||
@@ -13,9 +13,9 @@ import cacheManager from '@server/lib/cache';
|
||||
import logger from '@server/logger';
|
||||
|
||||
class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
static instance: Tvdb;
|
||||
private tmdb: TheMovieDb = new TheMovieDb();
|
||||
|
||||
private constructor() {
|
||||
public constructor() {
|
||||
super(
|
||||
'https://skyhook.sonarr.tv/v1/tvdb/shows',
|
||||
{},
|
||||
@@ -29,18 +29,6 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
);
|
||||
}
|
||||
|
||||
public static async getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new Tvdb();
|
||||
await this.instance.login();
|
||||
logger.info(
|
||||
'Tvdb instance created with token => ' +
|
||||
this.instance.defaultHeaders.Authorization
|
||||
);
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
async login() {
|
||||
try {
|
||||
return await this.get<TvdbLoginResponse>('/en/445009', {});
|
||||
@@ -51,65 +39,66 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
|
||||
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 tmdbTvShow = await this.tmdb.getTvShow({ tvId: tvId });
|
||||
const tvdbId = this.getTvdbIdFromTmdb(tmdbTvShow);
|
||||
|
||||
const tvdbId = tmdbTvShow.external_ids.tvdb_id;
|
||||
|
||||
if (!tvdbId) {
|
||||
if (tvdbId === -1) {
|
||||
return tmdbTvShow;
|
||||
}
|
||||
|
||||
const data = await this.get<TvdbTvShowDetail>(
|
||||
`/${language}/${tvdbId}`,
|
||||
{},
|
||||
43200
|
||||
);
|
||||
try {
|
||||
const data = await this.get<TvdbTvShowDetail>(
|
||||
`/en/${tvdbId}`,
|
||||
{},
|
||||
43200
|
||||
);
|
||||
|
||||
const correctSeasons = data.seasons.filter((value) => {
|
||||
return value.seasonNumber !== 0;
|
||||
});
|
||||
const correctSeasons = data.seasons.filter((value) => {
|
||||
return value.seasonNumber !== 0;
|
||||
});
|
||||
|
||||
tmdbTvShow.seasons = [];
|
||||
tmdbTvShow.seasons = [];
|
||||
|
||||
for (const season of correctSeasons) {
|
||||
if (season.seasonNumber) {
|
||||
logger.info(`Fetching TV season ${season.seasonNumber}`);
|
||||
for (const season of correctSeasons) {
|
||||
if (season.seasonNumber) {
|
||||
logger.info(`Fetching TV season ${season.seasonNumber}`);
|
||||
|
||||
try {
|
||||
const seasonData = {
|
||||
id: tvdbId,
|
||||
episode_count: data.episodes.filter((value) => {
|
||||
return value.seasonNumber === season.seasonNumber;
|
||||
}).length,
|
||||
name: `${season.seasonNumber}`,
|
||||
overview: '',
|
||||
season_number: season.seasonNumber,
|
||||
poster_path: '',
|
||||
air_date: '',
|
||||
image: '',
|
||||
};
|
||||
try {
|
||||
const seasonData = {
|
||||
id: tvdbId,
|
||||
episode_count: data.episodes.filter((value) => {
|
||||
return value.seasonNumber === season.seasonNumber;
|
||||
}).length,
|
||||
name: `${season.seasonNumber}`,
|
||||
overview: '',
|
||||
season_number: season.seasonNumber,
|
||||
poster_path: '',
|
||||
air_date: '',
|
||||
image: '',
|
||||
};
|
||||
|
||||
tmdbTvShow.seasons.push(seasonData);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to get season ${season.seasonNumber} for TV show ${tvdbId}: ${error.message}`,
|
||||
{
|
||||
label: 'Tvdb',
|
||||
message: `Failed to get season ${season.seasonNumber} for TV show ${tvdbId}`,
|
||||
}
|
||||
);
|
||||
tmdbTvShow.seasons.push(seasonData);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to get season ${season.seasonNumber} for TV show ${tvdbId}: ${error.message}`,
|
||||
{
|
||||
label: 'Tvdb',
|
||||
message: `Failed to get season ${season.seasonNumber} for TV show ${tvdbId}`,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tmdbTvShow;
|
||||
return tmdbTvShow;
|
||||
} catch (e) {
|
||||
return tmdbTvShow;
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`[TVDB] Failed to fetch TV show details: ${error.message}`
|
||||
@@ -117,6 +106,14 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
}
|
||||
};
|
||||
|
||||
private getTvdbIdFromTmdb(tmdbTvShow: TmdbTvDetails) {
|
||||
try {
|
||||
return tmdbTvShow.external_ids.tvdb_id || -1;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public getTvSeason = async ({
|
||||
tvId,
|
||||
seasonNumber,
|
||||
@@ -139,9 +136,16 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
season_number: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const tmdbTvShow = await this.tmdb.getTvShow({ tvId: tvId });
|
||||
const tvdbId = this.getTvdbIdFromTmdb(tmdbTvShow);
|
||||
|
||||
if (tvdbId === -1) {
|
||||
return await this.tmdb.getTvSeason({ tvId, seasonNumber, language });
|
||||
}
|
||||
try {
|
||||
const tvdbSeason = await this.get<TvdbTvShowDetail>(
|
||||
`/en/${tvId}`,
|
||||
`/en/${tvdbId}`,
|
||||
{ lang: language },
|
||||
43200
|
||||
);
|
||||
@@ -150,11 +154,11 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
.filter((value) => {
|
||||
return value.seasonNumber === seasonNumber;
|
||||
})
|
||||
.map((episode) => ({
|
||||
.map((episode, index) => ({
|
||||
id: episode.tvdbId,
|
||||
air_date: episode.airDate,
|
||||
episode_number: episode.episodeNumber,
|
||||
name: episode.title || '',
|
||||
name: episode.title || `Episode ${index + 1}`,
|
||||
overview: episode.overview || '',
|
||||
season_number: episode.seasonNumber,
|
||||
production_code: '',
|
||||
@@ -176,11 +180,16 @@ class Tvdb extends ExternalAPI implements TvShowIndexer {
|
||||
season_number: episodes.length,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
logger.error(
|
||||
`[TVDB] Failed to fetch TV season details: ${error.message}`
|
||||
);
|
||||
return await this.tmdb.getTvSeason({ tvId, seasonNumber, language });
|
||||
}
|
||||
};
|
||||
|
||||
getSeasonIdentifier(req: any): number {
|
||||
return req.params.seasonId;
|
||||
}
|
||||
}
|
||||
|
||||
export default Tvdb;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { TvShowIndexer } from '@server/api/indexer';
|
||||
import TheMovieDb from '@server/api/indexer/themoviedb';
|
||||
import Tvdb from '@server/api/indexer/tvdb';
|
||||
import { MediaServerType } from '@server/constants/server';
|
||||
import { Permission } from '@server/lib/permissions';
|
||||
import { runMigrations } from '@server/lib/settings/migrator';
|
||||
@@ -787,4 +790,13 @@ export const getSettings = (initialSettings?: AllSettings): Settings => {
|
||||
return settings;
|
||||
};
|
||||
|
||||
export const getIndexer = (): TvShowIndexer => {
|
||||
const settings = getSettings();
|
||||
if (settings.tvdb.use) {
|
||||
return new Tvdb();
|
||||
} else {
|
||||
return new TheMovieDb();
|
||||
}
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -28,7 +28,7 @@ tvdbRoutes.put('/', (req, res) => {
|
||||
|
||||
tvdbRoutes.post('/test', async (req, res, next) => {
|
||||
try {
|
||||
const tvdb = await Tvdb.getInstance();
|
||||
const tvdb = new Tvdb();
|
||||
await tvdb.login();
|
||||
return res.status(200).json({ message: 'Successfully connected to Tvdb' });
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import TheMovieDb from '@server/api/indexer/themoviedb';
|
||||
import Tvdb from '@server/api/indexer/tvdb';
|
||||
import RottenTomatoes from '@server/api/rating/rottentomatoes';
|
||||
import { MediaType } from '@server/constants/media';
|
||||
import { getRepository } from '@server/datasource';
|
||||
import Media from '@server/entity/Media';
|
||||
import { Watchlist } from '@server/entity/Watchlist';
|
||||
import { getSettings } from '@server/lib/settings';
|
||||
import { getIndexer } from '@server/lib/settings';
|
||||
import logger from '@server/logger';
|
||||
import { mapTvResult } from '@server/models/Search';
|
||||
import { mapSeasonWithEpisodes, mapTvDetails } from '@server/models/Tv';
|
||||
@@ -14,15 +13,10 @@ import { Router } from 'express';
|
||||
const tvRoutes = Router();
|
||||
|
||||
tvRoutes.get('/:id', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
let tmdb;
|
||||
if (settings.tvdb.use) {
|
||||
tmdb = await Tvdb.getInstance();
|
||||
} else {
|
||||
tmdb = new TheMovieDb();
|
||||
}
|
||||
const indexer = getIndexer();
|
||||
|
||||
try {
|
||||
const tv = await tmdb.getTvShow({
|
||||
const tv = await indexer.getTvShow({
|
||||
tvId: Number(req.params.id),
|
||||
language: (req.query.language as string) ?? req.locale,
|
||||
});
|
||||
@@ -42,7 +36,9 @@ tvRoutes.get('/:id', async (req, res, next) => {
|
||||
|
||||
// TMDB issue where it doesnt fallback to English when no overview is available in requested locale.
|
||||
if (!data.overview) {
|
||||
const tvEnglish = await tmdb.getTvShow({ tvId: Number(req.params.id) });
|
||||
const tvEnglish = await indexer.getTvShow({
|
||||
tvId: Number(req.params.id),
|
||||
});
|
||||
data.overview = tvEnglish.overview;
|
||||
}
|
||||
|
||||
@@ -62,20 +58,13 @@ tvRoutes.get('/:id', async (req, res, next) => {
|
||||
|
||||
tvRoutes.get('/:id/season/:seasonNumber/:seasonId', async (req, res, next) => {
|
||||
try {
|
||||
const settings = getSettings();
|
||||
let tmdb;
|
||||
let seasonIdentifier;
|
||||
if (settings.tvdb.use) {
|
||||
tmdb = await Tvdb.getInstance();
|
||||
seasonIdentifier = req.params.seasonId;
|
||||
} else {
|
||||
tmdb = new TheMovieDb();
|
||||
seasonIdentifier = req.params.seasonNumber;
|
||||
}
|
||||
const indexer = getIndexer();
|
||||
const seasonIdentifier = indexer.getSeasonIdentifier(req);
|
||||
|
||||
const season = await tmdb.getTvSeason({
|
||||
const season = await indexer.getTvSeason({
|
||||
tvId: Number(req.params.id),
|
||||
seasonNumber: Number(seasonIdentifier),
|
||||
seasonId: seasonIdentifier,
|
||||
seasonNumber: Number(req.params.seasonNumber),
|
||||
});
|
||||
|
||||
return res.status(200).json(mapSeasonWithEpisodes(season));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Button from '@app/components/Common/Button';
|
||||
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
import SettingsBadge from '@app/components/Settings/SettingsBadge';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import { ArrowDownOnSquareIcon, BeakerIcon } from '@heroicons/react/24/outline';
|
||||
@@ -17,13 +18,11 @@ const messages = defineMessages('components.Settings', {
|
||||
apikey: 'API Key',
|
||||
pin: 'PIN',
|
||||
enable: 'Enable',
|
||||
enableTip: 'Enable Tvdb (only for season and episode)',
|
||||
enableTip:
|
||||
'Enable Tvdb (only for season and episode).' +
|
||||
' Due to a limitation of the api used, only English is available.',
|
||||
});
|
||||
|
||||
/*interface SettingsTvdbProps {
|
||||
onEdit: () => void;
|
||||
}*/
|
||||
|
||||
const SettingsTvdb = () => {
|
||||
const intl = useIntl();
|
||||
const [isTesting, setIsTesting] = useState(false);
|
||||
@@ -111,6 +110,8 @@ const SettingsTvdb = () => {
|
||||
<span className="mr-2">
|
||||
{intl.formatMessage(messages.enable)}
|
||||
</span>
|
||||
<SettingsBadge badgeType="experimental" />
|
||||
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.enableTip)}
|
||||
</span>
|
||||
|
||||
@@ -61,7 +61,7 @@ const Season = ({ seasonNumber, tvId, seasonId }: SeasonProps) => {
|
||||
<CachedImage
|
||||
type="tmdb"
|
||||
className="rounded-lg object-contain"
|
||||
src={`https://image.tmdb.org/t/p/original/${episode.stillPath}`}
|
||||
src={episode.stillPath}
|
||||
alt=""
|
||||
fill
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user