feat(ageRating): add age rating in imdb link and genres based on config

This commit is contained in:
Bimal Timilsina
2025-10-12 13:25:28 +05:45
parent 526ab579f7
commit b7438492d5
8 changed files with 195 additions and 56 deletions
+29 -17
View File
@@ -78,16 +78,16 @@ addon.get('/:catalogChoices?/configure', function (req, res) {
});
addon.get("/:catalogChoices?/manifest.json", async function (req, res) {
const { catalogChoices } = req.params;
const config = parseConfig(catalogChoices) || {};
const manifest = await getManifest(config);
const cacheOpts = {
cacheMaxAge: 12 * 60 * 60,
staleRevalidate: 14 * 24 * 60 * 60,
staleError: 30 * 24 * 60 * 60,
};
respond(res, manifest, cacheOpts);
const { catalogChoices } = req.params;
const config = parseConfig(catalogChoices) || {};
const manifest = await getManifest(config);
const cacheOpts = {
cacheMaxAge: 12 * 60 * 60,
staleRevalidate: 14 * 24 * 60 * 60,
staleError: 30 * 24 * 60 * 60,
};
respond(res, manifest, cacheOpts);
});
addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (req, res) {
@@ -129,7 +129,7 @@ addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (re
return;
}
const cacheOpts = {
cacheMaxAge: 1 * 24 * 60 * 60,
cacheMaxAge: 1 * 24 * 60 * 60,
staleRevalidate: 7 * 24 * 60 * 60,
staleError: 14 * 24 * 60 * 60,
};
@@ -137,7 +137,7 @@ addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (re
try {
metas = JSON.parse(JSON.stringify(metas));
metas.metas = await Promise.all(metas.metas.map(async (el) => {
const rpdbImage = getRpdbPoster(type, el.id.replace('tmdb:', ''), language, rpdbkey)
const rpdbImage = getRpdbPoster(type, el.id.replace('tmdb:', ''), language, rpdbkey)
el.poster = await checkIfExists(rpdbImage) ? rpdbImage : el.poster;
return el;
}))
@@ -156,7 +156,13 @@ addon.get("/:catalogChoices?/meta/:type/:id.json", async function (req, res) {
if (req.params.id.includes("tmdb:")) {
const resp = await cacheWrapMeta(`${language}:${type}:${tmdbId}`, async () => {
return await getMeta(type, language, tmdbId, rpdbkey, config);
return await getMeta(type, language, tmdbId, rpdbkey, {
...config,
hideEpisodeThumbnails: config.hideEpisodeThumbnails === "true",
enableAgeRating: config.enableAgeRating === "true",
showAgeRatingInGenres: config.showAgeRatingInGenres !== "false",
showAgeRatingWithImdbRating: config.showAgeRatingWithImdbRating === "true"
});
});
const cacheOpts = {
staleRevalidate: 20 * 24 * 60 * 60,
@@ -174,7 +180,13 @@ addon.get("/:catalogChoices?/meta/:type/:id.json", async function (req, res) {
const tmdbId = await getTmdb(type, imdbId);
if (tmdbId) {
const resp = await cacheWrapMeta(`${language}:${type}:${tmdbId}`, async () => {
return await getMeta(type, language, tmdbId, rpdbkey, config);
return await getMeta(type, language, tmdbId, rpdbkey, {
...config,
hideEpisodeThumbnails: config.hideEpisodeThumbnails === "true",
enableAgeRating: config.enableAgeRating === "true",
showAgeRatingInGenres: config.showAgeRatingInGenres !== "false",
showAgeRatingWithImdbRating: config.showAgeRatingWithImdbRating === "true"
});
});
const cacheOpts = {
staleRevalidate: 20 * 24 * 60 * 60,
@@ -216,21 +228,21 @@ addon.get("/api/proxy/status", async function (req, res) {
addon.get("/api/image/blur", async function (req, res) {
const imageUrl = req.query.url;
if (!imageUrl) {
return res.status(400).json({ error: 'Image URL not provided' });
}
try {
const blurredImageBuffer = await blurImage(imageUrl);
if (!blurredImageBuffer) {
return res.status(500).json({ error: 'Error processing image' });
}
res.setHeader('Content-Type', 'image/jpeg');
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.send(blurredImageBuffer);
} catch (error) {
console.error('Error in blur route:', error);
+98 -26
View File
@@ -29,20 +29,40 @@ async function getCachedImdbRating(imdbId, type) {
}
// Helper functions
const getCacheKey = (type, language, tmdbId, rpdbkey, showAgeRatingInGenres = true) =>
`${type}-${language}-${tmdbId}-${rpdbkey}-ageRating:${showAgeRatingInGenres}`;
const getCacheKey = (
type,
language,
tmdbId,
rpdbkey,
enableAgeRating = false,
showAgeRatingInGenres = true,
showAgeRatingWithImdbRating = false
) =>
`${type}-${language}-${tmdbId}-${rpdbkey}-ageRating:${enableAgeRating}-${showAgeRatingInGenres}-${showAgeRatingWithImdbRating}`;
const processLogo = (logo) => {
if (!logo || blacklistLogoUrls.includes(logo)) return null;
return logo.replace("http://", "https://");
};
const buildLinks = (imdbRating, imdbId, title, type, genres, credits, language, castCount, ageRating = null, showAgeRatingInGenres = true) => [
Utils.parseImdbLink(imdbRating, imdbId),
Utils.parseShareLink(title, imdbId, type),
...Utils.parseGenreLink(genres, type, language, imdbId, ageRating, showAgeRatingInGenres),
...Utils.parseCreditsLink(credits, castCount)
];
const buildLinks = (
imdbRating,
imdbId,
title,
type,
genres,
credits,
language,
castCount,
ageRating = null,
showAgeRatingInGenres = true,
showAgeRatingWithImdbRating = false
) => [
Utils.parseImdbLink(imdbRating, imdbId, ageRating, showAgeRatingWithImdbRating),
Utils.parseShareLink(title, imdbId, type),
...Utils.parseGenreLink(genres, type, language, imdbId, ageRating, showAgeRatingInGenres),
...Utils.parseCreditsLink(credits, castCount)
];
// Helper function to add age rating to genres
const addAgeRatingToGenres = (ageRating, genres, showAgeRatingInGenres = true) => {
@@ -60,6 +80,10 @@ const fetchMovieData = async (tmdbId, language) => {
};
const buildMovieResponse = async (res, type, language, tmdbId, rpdbkey, config = {}) => {
const enableAgeRating = config.enableAgeRating === true || config.enableAgeRating === "true";
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false && config.showAgeRatingInGenres !== "false";
const showAgeRatingWithImdbRating = config.showAgeRatingWithImdbRating === true || config.showAgeRatingWithImdbRating === "true";
const [poster, logo, imdbRatingRaw, ageRating] = await Promise.all([
Utils.parsePoster(type, tmdbId, res.poster_path, language, rpdbkey),
getLogo(tmdbId, language, res.original_language).catch(e => {
@@ -67,10 +91,10 @@ const buildMovieResponse = async (res, type, language, tmdbId, rpdbkey, config =
return null;
}),
getCachedImdbRating(res.external_ids?.imdb_id, type),
getCachedAgeRating(tmdbId, type, language).catch(e => {
enableAgeRating ? getCachedAgeRating(tmdbId, type, language).catch(e => {
console.warn(`Error fetching age rating for movie ${tmdbId}:`, e.message);
return null;
}),
}) : Promise.resolve(null),
]);
const imdbRating = imdbRatingRaw || res.vote_average?.toFixed(1) || "N/A";
@@ -79,14 +103,14 @@ const buildMovieResponse = async (res, type, language, tmdbId, rpdbkey, config =
const hideInCinemaTag = config.hideInCinemaTag === true || config.hideInCinemaTag === "true";
const parsedGenres = Utils.parseGenres(res.genres);
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false; // Default to true
const resolvedAgeRating = enableAgeRating ? ageRating : null;
const response = {
imdb_id: res.imdb_id,
country: Utils.parseCoutry(res.production_countries),
description: res.overview,
director: Utils.parseDirector(res.credits),
genre: addAgeRatingToGenres(ageRating, parsedGenres, showAgeRatingInGenres),
genre: addAgeRatingToGenres(resolvedAgeRating, parsedGenres, showAgeRatingInGenres),
imdbRating,
name: res.title,
released: new Date(res.release_date),
@@ -99,11 +123,23 @@ const buildMovieResponse = async (res, type, language, tmdbId, rpdbkey, config =
poster,
runtime: Utils.parseRunTime(res.runtime),
id: returnImdbId ? res.imdb_id : `tmdb:${tmdbId}`,
genres: addAgeRatingToGenres(ageRating, parsedGenres, showAgeRatingInGenres),
ageRating,
genres: addAgeRatingToGenres(resolvedAgeRating, parsedGenres, showAgeRatingInGenres),
ageRating: resolvedAgeRating,
releaseInfo: res.release_date ? res.release_date.substr(0, 4) : "",
trailerStreams: Utils.parseTrailerStream(res.videos),
links: buildLinks(imdbRating, res.imdb_id, res.title, type, res.genres, res.credits, language, castCount, ageRating, showAgeRatingInGenres),
links: buildLinks(
imdbRating,
res.imdb_id,
res.title,
type,
res.genres,
res.credits,
language,
castCount,
resolvedAgeRating,
showAgeRatingInGenres,
showAgeRatingWithImdbRating
),
behaviorHints: {
defaultVideoId: res.imdb_id ? res.imdb_id : `tmdb:${res.id}`,
hasScheduledVideos: false
@@ -128,6 +164,9 @@ const fetchTvData = async (tmdbId, language) => {
const buildTvResponse = async (res, type, language, tmdbId, rpdbkey, config = {}) => {
const runtime = res.episode_run_time?.[0] ?? res.last_episode_to_air?.runtime ?? res.next_episode_to_air?.runtime ?? null;
const enableAgeRating = config.enableAgeRating === true || config.enableAgeRating === "true";
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false && config.showAgeRatingInGenres !== "false";
const showAgeRatingWithImdbRating = config.showAgeRatingWithImdbRating === true || config.showAgeRatingWithImdbRating === "true";
const [poster, logo, imdbRatingRaw, episodes, ageRating] = await Promise.all([
Utils.parsePoster(type, tmdbId, res.poster_path, language, rpdbkey),
@@ -142,10 +181,10 @@ const buildTvResponse = async (res, type, language, tmdbId, rpdbkey, config = {}
console.warn(`Error fetching episodes for series ${tmdbId}:`, e.message);
return [];
}),
getCachedAgeRating(tmdbId, type, language).catch(e => {
enableAgeRating ? getCachedAgeRating(tmdbId, type, language).catch(e => {
console.warn(`Error fetching age rating for series ${tmdbId}:`, e.message);
return null;
})
}) : Promise.resolve(null)
]);
const imdbRating = imdbRatingRaw || res.vote_average?.toFixed(1) || "N/A";
@@ -153,12 +192,12 @@ const buildTvResponse = async (res, type, language, tmdbId, rpdbkey, config = {}
const returnImdbId = config.returnImdbId === true || config.returnImdbId === "true";
const hideInCinemaTag = config.hideInCinemaTag === true || config.hideInCinemaTag === "true";
const parsedGenres = Utils.parseGenres(res.genres);
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false; // Default to true
const resolvedAgeRating = enableAgeRating ? ageRating : null;
const response = {
country: Utils.parseCoutry(res.production_countries),
description: res.overview,
genre: addAgeRatingToGenres(ageRating, parsedGenres, showAgeRatingInGenres),
genre: addAgeRatingToGenres(resolvedAgeRating, parsedGenres, showAgeRatingInGenres),
imdbRating,
imdb_id: res.external_ids.imdb_id,
name: res.name,
@@ -172,11 +211,23 @@ const buildTvResponse = async (res, type, language, tmdbId, rpdbkey, config = {}
background: `https://image.tmdb.org/t/p/original${res.backdrop_path}`,
slug: Utils.parseSlug(type, res.name, res.external_ids.imdb_id),
id: returnImdbId ? res.imdb_id : `tmdb:${tmdbId}`,
genres: addAgeRatingToGenres(ageRating, parsedGenres, showAgeRatingInGenres),
ageRating,
genres: addAgeRatingToGenres(resolvedAgeRating, parsedGenres, showAgeRatingInGenres),
ageRating: resolvedAgeRating,
releaseInfo: Utils.parseYear(res.status, res.first_air_date, res.last_air_date),
videos: episodes || [],
links: buildLinks(imdbRating, res.external_ids.imdb_id, res.name, type, res.genres, res.credits, language, castCount, ageRating, showAgeRatingInGenres),
links: buildLinks(
imdbRating,
res.external_ids.imdb_id,
res.name,
type,
res.genres,
res.credits,
language,
castCount,
resolvedAgeRating,
showAgeRatingInGenres,
showAgeRatingWithImdbRating
),
trailers: Utils.parseTrailers(res.videos),
trailerStreams: Utils.parseTrailerStream(res.videos),
behaviorHints: {
@@ -206,8 +257,19 @@ const buildTvResponse = async (res, type, language, tmdbId, rpdbkey, config = {}
// Main function
async function getMeta(type, language, tmdbId, rpdbkey, config = {}) {
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false; // Default to true
const cacheKey = getCacheKey(type, language, tmdbId, rpdbkey, showAgeRatingInGenres);
const enableAgeRating = config.enableAgeRating === true || config.enableAgeRating === "true";
const showAgeRatingInGenres = config.showAgeRatingInGenres !== false && config.showAgeRatingInGenres !== "false";
const showAgeRatingWithImdbRating = config.showAgeRatingWithImdbRating === true || config.showAgeRatingWithImdbRating === "true";
const cacheKey = getCacheKey(
type,
language,
tmdbId,
rpdbkey,
enableAgeRating,
showAgeRatingInGenres,
showAgeRatingWithImdbRating
);
const cachedData = cache.get(cacheKey);
if (cachedData && (Date.now() - cachedData.timestamp) < CACHE_TTL) {
@@ -216,8 +278,18 @@ async function getMeta(type, language, tmdbId, rpdbkey, config = {}) {
try {
const meta = await (type === "movie" ?
fetchMovieData(tmdbId, language).then(res => buildMovieResponse(res, type, language, tmdbId, rpdbkey, config)) :
fetchTvData(tmdbId, language).then(res => buildTvResponse(res, type, language, tmdbId, rpdbkey, config))
fetchMovieData(tmdbId, language).then(res => buildMovieResponse(res, type, language, tmdbId, rpdbkey, {
...config,
enableAgeRating,
showAgeRatingInGenres,
showAgeRatingWithImdbRating
})) :
fetchTvData(tmdbId, language).then(res => buildTvResponse(res, type, language, tmdbId, rpdbkey, {
...config,
enableAgeRating,
showAgeRatingInGenres,
showAgeRatingWithImdbRating
}))
);
cache.set(cacheKey, { data: meta, timestamp: Date.now() });
+2 -2
View File
@@ -71,9 +71,9 @@ function parseTrailerStream(videos) {
});
}
function parseImdbLink(vote_average, imdb_id) {
function parseImdbLink(vote_average, imdb_id, ageRating = null, showAgeRatingWithImdbRating = false) {
return {
name: vote_average,
name: showAgeRatingWithImdbRating && ageRating ? `${ageRating}\u2003\u2003${vote_average}` : vote_average,
category: "imdb",
url: `https://imdb.com/title/${imdb_id}`,
};