mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-03 05:09:43 -05:00
* feat(tvdb): get tv seasons/episodes with tvdb * fix: fix rate limiter index tvdb indexer * fix(usersettings): remove unused column tvdbtoken * refactor(tvdb): replace tvdb api by skyhook * fix: error during get episodes * fix: error if tmdb poster is null * refactor: clean tvdb indexer code * fix: wrong language with tmdb indexer * style: replace avalaible to available * style: tvdb.login to tvdb.test * fix(test): fix discover test * fix(test): wrong url tv-details * test(tvdb): add tvdb tests * style(tvdb): rename pokemon to correct tv show * refactor(indexer): remove unused getSeasonIdentifier method * refactor(settings): replace tvdb object to boolean type * refactor(tmdb): reduce still path condition * test(tvdb): change 'use' to 'tvdb' condition check * fix(tmdb): fix build fix build after rebase * fix(build): revert package.json * fix(tvdb): ensure that seasons contain data * refactor(swagger): fix /tvdb/test response * fix(scanner): add tvdb indexer for scanner * refactor(tvdb): remove skyhook api * refactor(tvdb): use tvdb api * fix(tvdb): rename tvdb to medatada * refactor(medata): add tvdb settings * refactor(metadata): rewrite metadata settings * refactor(metadata): refactor metadata routes * refactor(metadata): remove french comments * refactor(metadata): refactor tvdb api calls * style(prettier): run prettier * fix(scanner): fix jellyfin scanner with tvdb provider * fix(scanner): fix plex scanner tvdb provider * style(provider): change provider name in info section * style(provider): full provider name in select * style(provider): remove french comment * fix(tests): fix all cypress tests * refactor(tvdb): fix apikey * refactor(tmdb): apply prettier * refactor(tvdb): remove logger info * feat(metadata): replace fetch with axios for API calls * feat(provider): replace indexer by provider * fix(tests): fix cypress test * chore: add project-wide apikey for tvdb * chore: add correct application-wide key * fix(test): fix test with default provider tmdb anime * style(cypress): fix anime name variable * chore(i18n): remove french translation + apply i18n:extract * style(wording): standardize naming to "Metadata Provider" in UI text * docs(comments): translate from French to English * refactor(tvdb): remove unnecessary try/catch block * feat(i18n): add missing translations * fix(scanner): correct metadata provider ID from Tmdb to Tvdb * style(settings): clarify navigation label from "Metadata" to "Metadata Providers" * style(logs): update error log label from "Metadata" to "MetadataProvider" * refactor(tvdb): replace indexer by metadata providers * refactor(settings): remove metadata providers logo * fix(config): restore missing config/db/.gitkeep file --------- Co-authored-by: TOomaAh <ubuntu@PC> Co-authored-by: fallenbagel <98979876+Fallenbagel@users.noreply.github.com>
218 lines
6.0 KiB
TypeScript
218 lines
6.0 KiB
TypeScript
import { getMetadataProvider } from '@server/api/metadata';
|
|
import RottenTomatoes from '@server/api/rating/rottentomatoes';
|
|
import TheMovieDb from '@server/api/themoviedb';
|
|
import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants';
|
|
import type { TmdbKeyword } from '@server/api/themoviedb/interfaces';
|
|
import { MediaType } from '@server/constants/media';
|
|
import { getRepository } from '@server/datasource';
|
|
import Media from '@server/entity/Media';
|
|
import { Watchlist } from '@server/entity/Watchlist';
|
|
import logger from '@server/logger';
|
|
import { mapTvResult } from '@server/models/Search';
|
|
import { mapSeasonWithEpisodes, mapTvDetails } from '@server/models/Tv';
|
|
import { Router } from 'express';
|
|
|
|
const tvRoutes = Router();
|
|
|
|
tvRoutes.get('/:id', async (req, res, next) => {
|
|
const tmdb = new TheMovieDb();
|
|
|
|
try {
|
|
const tmdbTv = await tmdb.getTvShow({
|
|
tvId: Number(req.params.id),
|
|
});
|
|
const metadataProvider = tmdbTv.keywords.results.some(
|
|
(keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID
|
|
)
|
|
? await getMetadataProvider('anime')
|
|
: await getMetadataProvider('tv');
|
|
const tv = await metadataProvider.getTvShow({
|
|
tvId: Number(req.params.id),
|
|
language: (req.query.language as string) ?? req.locale,
|
|
});
|
|
const media = await Media.getMedia(tv.id, MediaType.TV);
|
|
|
|
const onUserWatchlist = await getRepository(Watchlist).exist({
|
|
where: {
|
|
tmdbId: Number(req.params.id),
|
|
requestedBy: {
|
|
id: req.user?.id,
|
|
},
|
|
},
|
|
});
|
|
|
|
const data = mapTvDetails(tv, media, onUserWatchlist);
|
|
|
|
// TMDB issue where it doesnt fallback to English when no overview is available in requested locale.
|
|
if (!data.overview) {
|
|
const tvEnglish = await metadataProvider.getTvShow({
|
|
tvId: Number(req.params.id),
|
|
});
|
|
data.overview = tvEnglish.overview;
|
|
}
|
|
|
|
return res.status(200).json(data);
|
|
} catch (e) {
|
|
logger.debug('Something went wrong retrieving series', {
|
|
label: 'API',
|
|
errorMessage: e.message,
|
|
tvId: req.params.id,
|
|
});
|
|
return next({
|
|
status: 500,
|
|
message: 'Unable to retrieve series.',
|
|
});
|
|
}
|
|
});
|
|
|
|
tvRoutes.get('/:id/season/:seasonNumber', async (req, res, next) => {
|
|
try {
|
|
const tmdb = new TheMovieDb();
|
|
const tmdbTv = await tmdb.getTvShow({
|
|
tvId: Number(req.params.id),
|
|
});
|
|
const metadataProvider = tmdbTv.keywords.results.some(
|
|
(keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID
|
|
)
|
|
? await getMetadataProvider('anime')
|
|
: await getMetadataProvider('tv');
|
|
|
|
const season = await metadataProvider.getTvSeason({
|
|
tvId: Number(req.params.id),
|
|
seasonNumber: Number(req.params.seasonNumber),
|
|
});
|
|
|
|
return res.status(200).json(mapSeasonWithEpisodes(season));
|
|
} catch (e) {
|
|
logger.debug('Something went wrong retrieving season', {
|
|
label: 'API',
|
|
errorMessage: e.message,
|
|
tvId: req.params.id,
|
|
seasonNumber: req.params.seasonNumber,
|
|
});
|
|
return next({
|
|
status: 500,
|
|
message: 'Unable to retrieve season.',
|
|
});
|
|
}
|
|
});
|
|
|
|
tvRoutes.get('/:id/recommendations', async (req, res, next) => {
|
|
const tmdb = new TheMovieDb();
|
|
|
|
try {
|
|
const results = await tmdb.getTvRecommendations({
|
|
tvId: Number(req.params.id),
|
|
page: Number(req.query.page),
|
|
language: (req.query.language as string) ?? req.locale,
|
|
});
|
|
|
|
const media = await Media.getRelatedMedia(
|
|
req.user,
|
|
results.results.map((result) => result.id)
|
|
);
|
|
|
|
return res.status(200).json({
|
|
page: results.page,
|
|
totalPages: results.total_pages,
|
|
totalResults: results.total_results,
|
|
results: results.results.map((result) =>
|
|
mapTvResult(
|
|
result,
|
|
media.find(
|
|
(req) => req.tmdbId === result.id && req.mediaType === MediaType.TV
|
|
)
|
|
)
|
|
),
|
|
});
|
|
} catch (e) {
|
|
logger.debug('Something went wrong retrieving series recommendations', {
|
|
label: 'API',
|
|
errorMessage: e.message,
|
|
tvId: req.params.id,
|
|
});
|
|
return next({
|
|
status: 500,
|
|
message: 'Unable to retrieve series recommendations.',
|
|
});
|
|
}
|
|
});
|
|
|
|
tvRoutes.get('/:id/similar', async (req, res, next) => {
|
|
const tmdb = new TheMovieDb();
|
|
|
|
try {
|
|
const results = await tmdb.getTvSimilar({
|
|
tvId: Number(req.params.id),
|
|
page: Number(req.query.page),
|
|
language: (req.query.language as string) ?? req.locale,
|
|
});
|
|
|
|
const media = await Media.getRelatedMedia(
|
|
req.user,
|
|
results.results.map((result) => result.id)
|
|
);
|
|
|
|
return res.status(200).json({
|
|
page: results.page,
|
|
totalPages: results.total_pages,
|
|
totalResults: results.total_results,
|
|
results: results.results.map((result) =>
|
|
mapTvResult(
|
|
result,
|
|
media.find(
|
|
(req) => req.tmdbId === result.id && req.mediaType === MediaType.TV
|
|
)
|
|
)
|
|
),
|
|
});
|
|
} catch (e) {
|
|
logger.debug('Something went wrong retrieving similar series', {
|
|
label: 'API',
|
|
errorMessage: e.message,
|
|
tvId: req.params.id,
|
|
});
|
|
return next({
|
|
status: 500,
|
|
message: 'Unable to retrieve similar series.',
|
|
});
|
|
}
|
|
});
|
|
|
|
tvRoutes.get('/:id/ratings', async (req, res, next) => {
|
|
const tmdb = new TheMovieDb();
|
|
const rtapi = new RottenTomatoes();
|
|
|
|
try {
|
|
const tv = await tmdb.getTvShow({
|
|
tvId: Number(req.params.id),
|
|
});
|
|
|
|
const rtratings = await rtapi.getTVRatings(
|
|
tv.name,
|
|
tv.first_air_date ? Number(tv.first_air_date.slice(0, 4)) : undefined
|
|
);
|
|
|
|
if (!rtratings) {
|
|
return next({
|
|
status: 404,
|
|
message: 'Rotten Tomatoes ratings not found.',
|
|
});
|
|
}
|
|
|
|
return res.status(200).json(rtratings);
|
|
} catch (e) {
|
|
logger.debug('Something went wrong retrieving series ratings', {
|
|
label: 'API',
|
|
errorMessage: e.message,
|
|
tvId: req.params.id,
|
|
});
|
|
return next({
|
|
status: 500,
|
|
message: 'Unable to retrieve series ratings.',
|
|
});
|
|
}
|
|
});
|
|
|
|
export default tvRoutes;
|