Skip to content

Instantly share code, notes, and snippets.

@lennyRBLX
Last active February 1, 2026 16:41
Show Gist options
  • Select an option

  • Save lennyRBLX/822b66bc2833f7998b78186888db5c3a to your computer and use it in GitHub Desktop.

Select an option

Save lennyRBLX/822b66bc2833f7998b78186888db5c3a to your computer and use it in GitHub Desktop.
Edit your own TVDB & TMDB API Keys into it.
const TMDB_ACCESS_TOKEN = "ENTER YOUR OWN TMDB API READ ACCESS TOKEN HERE";
// Cache expiration times (in milliseconds)
const CACHE_DURATION = {
EPISODE_GROUPS: 7 * 24 * 60 * 60 * 1000, // 7 days
EPISODE_GROUP_DETAILS: 7 * 24 * 60 * 60 * 1000, // 7 days
SEASON_TEMPLATE: 7 * 24 * 60 * 60 * 1000 // 7 days
};
// Parse the request URL to determine endpoint type
let requestUrl = $request.url;
// Check if this is a recommendations/videos/images endpoint - just pass through
if (requestUrl.includes("/recommendations") || requestUrl.includes("/videos") || requestUrl.includes("/images")) {
console.log("📋 Recommendations/Videos/Images endpoint detected - returning original response");
$done({});
}
let urlParts = requestUrl.match(/\/tv\/(\d+)(?:\/season\/(\d+)(\/credits)?)?/);
if (!urlParts) {
console.log("❌ URL doesn't match expected pattern");
$done({});
}
let tvShowId = urlParts[1];
let seasonNumber = urlParts[2] ? parseInt(urlParts[2]) : null;
let isCreditsEndpoint = urlParts[3] === "/credits";
console.log(`📡 Request: TV Show ${tvShowId}, Season ${seasonNumber}, Credits: ${isCreditsEndpoint}`);
// Handle credits endpoint
if (isCreditsEndpoint && seasonNumber !== null) {
handleCreditsEndpoint(tvShowId, seasonNumber);
}
// Handle season endpoint
else if (seasonNumber !== null) {
handleSeasonEndpoint(tvShowId, seasonNumber);
}
// Handle main TV show endpoint
else {
handleMainTVEndpoint(tvShowId);
}
// ==================== UTILITIES ====================
function episodeGroupIdToNumber(groupId) {
if (groupId == null) {
return null;
}
// Take first 6 hex characters and convert to decimal
let shortHex = groupId.substring(0, 6);
let decimal = parseInt(shortHex, 16);
// Constrain to 6 digits (000000-999999)
return parseInt((decimal % 1000000).toString().padStart(6, '0'));
}
function getCachedData(key) {
let cached = $persistentStore.read(key);
if (!cached) return null;
try {
let data = JSON.parse(cached);
if (data.expires && Date.now() > data.expires) {
console.log(` 🗑️ Cache expired for ${key}`);
$persistentStore.write(null, key);
return null;
}
return data.value;
} catch (e) {
return null;
}
}
function setCachedData(key, value, duration) {
let data = {
value: value,
expires: duration ? Date.now() + duration : null
};
$persistentStore.write(JSON.stringify(data), key);
}
function removeSpecialsFromResponse(tmdbResponse) {
console.log(`\n🧹 Cleaning up response - removing Season 0 and below...`);
if (!tmdbResponse.seasons || !Array.isArray(tmdbResponse.seasons)) {
console.log(` ⚠️ No seasons array found`);
return tmdbResponse;
}
let originalCount = tmdbResponse.seasons.length;
// Filter out Season 0 and below
tmdbResponse.seasons = tmdbResponse.seasons.filter(season => {
if (season.season_number <= 0) {
console.log(` 🗑️ Removed Season ${season.season_number}: ${season.name || 'Unnamed'} (${season.episode_count} episodes)`);
return false;
}
return true;
});
let newCount = tmdbResponse.seasons.length;
let removedCount = originalCount - newCount;
if (removedCount > 0) {
// Recalculate episode count
let totalEpisodes = 0;
tmdbResponse.seasons.forEach(season => {
totalEpisodes += season.episode_count || 0;
});
tmdbResponse.number_of_episodes = totalEpisodes;
tmdbResponse.number_of_seasons = newCount;
console.log(` ✅ Removed ${removedCount} season(s)`);
console.log(` Updated counts: ${newCount} seasons, ${totalEpisodes} episodes`);
} else {
console.log(` ℹ️ No seasons to remove`);
}
return tmdbResponse;
}
// ==================== CREDITS ENDPOINT ====================
function handleCreditsEndpoint(tvShowId, seasonNumber) {
// Check if response is valid (200 status and has body)
let hasValidResponse = false;
let responseData = null;
try {
if ($response.status === 200 && $response.body && $response.body.length > 0) {
responseData = JSON.parse($response.body);
if (responseData && (responseData.cast || responseData.crew)) {
hasValidResponse = true;
console.log(`✅ Credits response successful`);
console.log(` Cast: ${responseData.cast ? responseData.cast.length : 0} members`);
console.log(` Crew: ${responseData.crew ? responseData.crew.length : 0} members`);
$done({});
return;
}
}
} catch (e) {
console.log(`⚠️ Failed to parse response: ${e}`);
}
if (!hasValidResponse) {
console.log(`⚠️ Credits response failed (status: ${$response.status || 'unknown'})`);
if (seasonNumber !== 1) {
console.log(`🔄 Redirecting to season 1 credits...`);
let season1Url = `https://forwardinfo.vvebo.vip/tv/${tvShowId}/season/1/credits`;
$done({
status: 302,
headers: {
"Location": season1Url
}
});
return;
}
console.log(`❌ No credits data available for season 1`);
$done({});
}
}
// ==================== SEASON ENDPOINT ====================
function handleSeasonEndpoint(tvShowId, requestedSeasonNumber) {
console.log(`\n🔍 Processing season ${requestedSeasonNumber} request...`);
// Don't process season 0 (Specials) - return 404 or redirect to season 1
if (requestedSeasonNumber === 0) {
console.log(`⚠️ Season 0 (Specials) not supported, redirecting to season 1...`);
let season1Url = `https://forwardinfo.vvebo.vip/tv/${tvShowId}/season/1`;
$done({
status: 302,
headers: {
"Location": season1Url
}
});
return;
}
fetchEpisodeGroups(tvShowId, requestedSeasonNumber);
}
function fetchEpisodeGroups(tvShowId, targetSeasonNumber) {
let cacheKey = `episode_groups_${tvShowId}`;
let cachedGroups = getCachedData(cacheKey);
if (cachedGroups) {
console.log(`📦 Using cached episode groups`);
processEpisodeGroups(tvShowId, cachedGroups, targetSeasonNumber);
return;
}
let url = `https://api.themoviedb.org/3/tv/${tvShowId}/episode_groups`;
console.log(`🔄 Fetching episode groups from TMDB...`);
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`⚠️ Episode groups fetch failed: ${error}`);
console.log(` Falling back to standard season data...`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
return;
}
try {
let groupsData = JSON.parse(data);
if (groupsData && groupsData.results && groupsData.results.length > 0) {
console.log(`✅ Found ${groupsData.results.length} episode groups`);
setCachedData(cacheKey, groupsData, CACHE_DURATION.EPISODE_GROUPS);
processEpisodeGroups(tvShowId, groupsData, targetSeasonNumber);
} else {
console.log(`⚠️ No episode groups found, using standard data...`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
}
} catch (e) {
console.log(`❌ Failed to parse episode groups: ${e}`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
}
});
}
function processEpisodeGroups(tvShowId, groupsData, targetSeasonNumber) {
let seasonsGroup = null;
let groupSource = null;
// Priority 1: Find group with name "Seasons" (case-insensitive)
seasonsGroup = groupsData.results.find(group =>
group.name && group.name.toLowerCase() === "seasons"
);
if (seasonsGroup) {
groupSource = `name "Seasons"`;
}
// Priority 2: Find group with type 7
if (!seasonsGroup) {
seasonsGroup = groupsData.results.find(group => group.type === 7);
if (seasonsGroup) {
groupSource = `type 7`;
}
}
// Priority 3: Find group with type 6
if (!seasonsGroup) {
seasonsGroup = groupsData.results.find(group => group.type === 6);
if (seasonsGroup) {
groupSource = `type 6`;
}
}
// No suitable group found
if (!seasonsGroup) {
console.log(`⚠️ No suitable episode group found`);
console.log(` Available groups: ${groupsData.results.map(g => `${g.name} (type ${g.type})`).join(", ")}`);
console.log(` Falling back to standard season data...`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
return;
}
console.log(`✅ Found episode group by ${groupSource}: ${seasonsGroup.name}`);
console.log(` ID: ${seasonsGroup.id}`);
console.log(` Type: ${seasonsGroup.type}`);
console.log(` Episodes: ${seasonsGroup.episode_count}`);
console.log(` Groups: ${seasonsGroup.group_count}`);
fetchEpisodeGroupDetails(tvShowId, seasonsGroup.id, targetSeasonNumber);
}
function fetchEpisodeGroupDetails(tvShowId, groupId, targetSeasonNumber) {
let cacheKey = `episode_group_${groupId}`;
let cachedDetails = getCachedData(cacheKey);
if (cachedDetails) {
console.log(`📦 Using cached episode group details`);
processEpisodeGroupDetails(tvShowId, cachedDetails, targetSeasonNumber);
return;
}
let url = `https://api.themoviedb.org/3/tv/episode_group/${groupId}`;
console.log(`🔄 Fetching episode group details from TMDB...`);
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`❌ Episode group details fetch failed: ${error}`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
return;
}
try {
let detailsData = JSON.parse(data);
if (detailsData && detailsData.groups) {
console.log(`✅ Fetched episode group details`);
console.log(` Name: ${detailsData.name}`);
console.log(` Total groups: ${detailsData.groups.length}`);
setCachedData(cacheKey, detailsData, CACHE_DURATION.EPISODE_GROUP_DETAILS);
processEpisodeGroupDetails(tvShowId, detailsData, targetSeasonNumber);
} else {
console.log(`❌ Invalid episode group details`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
}
} catch (e) {
console.log(`❌ Failed to parse episode group details: ${e}`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
}
});
}
function processEpisodeGroupDetails(tvShowId, groupDetails, targetSeasonNumber) {
console.log(`\n🔧 Processing episode group for season ${targetSeasonNumber}...`);
// Find the target season - match by order field
let targetGroup = groupDetails.groups.find(group => group.order === targetSeasonNumber);
if (!targetGroup) {
console.log(`⚠️ Season ${targetSeasonNumber} not found in episode group`);
console.log(` Available seasons: ${groupDetails.groups.filter(g => g.order > 0).map(g => `${g.order}: ${g.name}`).join(", ")}`);
fetchStandardSeasonData(tvShowId, targetSeasonNumber);
return;
}
console.log(`✅ Found season ${targetSeasonNumber} in episode group`);
console.log(` Name: ${targetGroup.name}`);
console.log(` Episodes: ${targetGroup.episodes.length}`);
buildSeasonResponse(tvShowId, targetGroup, targetSeasonNumber);
}
function buildSeasonResponse(tmdbShowId, episodeGroup, targetSeasonNumber) {
console.log(`\n🔨 Building season ${targetSeasonNumber} response...`);
let episodes = episodeGroup.episodes;
console.log(` Processing ${episodes.length} episodes from episode group`);
// Build episodes array - renumber episodes sequentially
let newEpisodes = [];
episodes.forEach((ep, index) => {
let episodeNumber = index + 1;
let newEpisode = {
air_date: ep.air_date || null,
episode_number: episodeNumber,
episode_type: ep.episode_type || "standard",
id: ep.id,
name: ep.name || `Episode ${episodeNumber}`,
overview: ep.overview || "",
production_code: ep.production_code || "",
runtime: ep.runtime || 24,
season_number: targetSeasonNumber,
show_id: parseInt(tmdbShowId),
still_path: ep.still_path || null,
vote_average: ep.vote_average || 0,
vote_count: ep.vote_count || 0,
crew: [],
guest_stars: []
};
newEpisodes.push(newEpisode);
let stillInfo = newEpisode.still_path ? "[has still]" : "[no still]";
console.log(` ✓ S${targetSeasonNumber}E${episodeNumber}: ${newEpisode.name} ${stillInfo} [ID: ${newEpisode.id}]`);
});
// Create season response
let seasonResponse = {
air_date: newEpisodes.length > 0 ? newEpisodes[0].air_date : null,
season_number: targetSeasonNumber,
name: episodeGroup.name || `Season ${targetSeasonNumber}`,
vote_average: 0,
id: episodeGroupIdToNumber(episodeGroup.id),
episodes: newEpisodes,
poster_path: null,
overview: ""
};
console.log(`\n✅ Season ${targetSeasonNumber} response created with ${newEpisodes.length} episodes`);
console.log(` Air date: ${seasonResponse.air_date}`);
$done({
status: 200,
body: JSON.stringify(seasonResponse)
});
}
function fetchStandardSeasonData(tvShowId, targetSeasonNumber) {
console.log(`\n🔄 Fetching standard season data as fallback...`);
let url = `https://api.themoviedb.org/3/tv/${tvShowId}/season/${targetSeasonNumber}?language=en-US`;
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`❌ Standard season fetch failed: ${error}`);
$done({});
return;
}
try {
let seasonData = JSON.parse(data);
if (seasonData && seasonData.episodes) {
console.log(`✅ Using standard season data (${seasonData.episodes.length} episodes)`);
$done({
status: 200,
body: JSON.stringify(seasonData)
});
} else {
console.log(`❌ Invalid season data`);
$done({});
}
} catch (e) {
console.log(`❌ Failed to parse season data: ${e}`);
$done({});
}
});
}
// ==================== MAIN TV ENDPOINT ====================
function handleMainTVEndpoint(tvShowId) {
if (!$response.body || $response.body.length === 0) {
console.log(`⚠️ Response body is empty, fetching TMDB data for show ${tvShowId}...`);
fetchTMDBShowData(tvShowId);
return;
}
let obj;
try {
obj = JSON.parse($response.body);
} catch (e) {
console.log(`❌ Failed to parse response body: ${e}`);
console.log(` Fetching TMDB data for show ${tvShowId}...`);
fetchTMDBShowData(tvShowId);
return;
}
processTVShowData(obj);
}
function fetchTMDBShowData(tvShowId) {
let url = `https://api.themoviedb.org/3/tv/${tvShowId}?language=en-US`;
console.log(`🔄 Fetching TMDB data for show ${tvShowId}...`);
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`❌ TMDB fetch error: ${error}`);
$done({});
return;
}
try {
let showData = JSON.parse(data);
console.log(`✅ Fetched TMDB data for: ${showData.name || showData.original_name}`);
processTVShowData(showData);
} catch (e) {
console.log(`❌ Failed to parse TMDB response: ${e}`);
$done({});
}
});
}
function processTVShowData(obj) {
let isJapanese = obj.origin_country && obj.origin_country.includes("JP");
let isAnimation = obj.genres && obj.genres.some(genre => genre.id === 16);
if (isJapanese && isAnimation) {
console.log(`✅ Match found: ${obj.name} (TMDB ID: ${obj.id})`);
console.log(` Origin: ${obj.origin_country.join(", ")}`);
console.log(` Genres: ${obj.genres.map(g => g.name).join(", ")}`);
fetchEpisodeGroupsForMain(obj.id, obj);
} else {
console.log(`❌ No match: ${obj.name || "Unknown"}`);
console.log(` Japanese: ${isJapanese}, Animation: ${isAnimation}`);
// Still remove Season 0 even for non-anime shows
obj = removeSpecialsFromResponse(obj);
$done({
status: 200,
body: JSON.stringify(obj)
});
}
}
function fetchEpisodeGroupsForMain(tvShowId, originalResponse) {
let cacheKey = `episode_groups_${tvShowId}`;
let cachedGroups = getCachedData(cacheKey);
if (cachedGroups) {
console.log(`📦 Using cached episode groups`);
processEpisodeGroupsForMain(tvShowId, cachedGroups, originalResponse);
return;
}
let url = `https://api.themoviedb.org/3/tv/${tvShowId}/episode_groups`;
console.log(`🔄 Fetching episode groups...`);
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`⚠️ Episode groups fetch failed: ${error}`);
console.log(` Using original response with cleanup...`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
return;
}
try {
let groupsData = JSON.parse(data);
if (groupsData && groupsData.results && groupsData.results.length > 0) {
console.log(`✅ Found ${groupsData.results.length} episode groups`);
setCachedData(cacheKey, groupsData, CACHE_DURATION.EPISODE_GROUPS);
processEpisodeGroupsForMain(tvShowId, groupsData, originalResponse);
} else {
console.log(`⚠️ No episode groups found`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
}
} catch (e) {
console.log(`❌ Failed to parse episode groups: ${e}`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
}
});
}
function processEpisodeGroupsForMain(tvShowId, groupsData, originalResponse) {
let seasonsGroup = null;
let groupSource = null;
// Priority 1: Find group with name "Seasons" (case-insensitive)
seasonsGroup = groupsData.results.find(group =>
group.name && group.name.toLowerCase() === "seasons"
);
if (seasonsGroup) {
groupSource = `name "Seasons"`;
}
// Priority 2: Find group with type 7
if (!seasonsGroup) {
seasonsGroup = groupsData.results.find(group => group.type === 7);
if (seasonsGroup) {
groupSource = `type 7`;
}
}
// Priority 3: Find group with type 6
if (!seasonsGroup) {
seasonsGroup = groupsData.results.find(group => group.type === 6);
if (seasonsGroup) {
groupSource = `type 6`;
}
}
// No suitable group found
if (!seasonsGroup) {
console.log(`⚠️ No suitable episode group found`);
console.log(` Available groups: ${groupsData.results.map(g => `${g.name} (type ${g.type})`).join(", ")}`);
console.log(` Using original response with cleanup...`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
return;
}
console.log(`✅ Found episode group by ${groupSource}: ${seasonsGroup.name}`);
console.log(` ID: ${seasonsGroup.id}`);
console.log(` Type: ${seasonsGroup.type}`);
console.log(` Episodes: ${seasonsGroup.episode_count}`);
console.log(` Seasons: ${seasonsGroup.group_count}`);
fetchEpisodeGroupDetailsForMain(tvShowId, seasonsGroup.id, seasonsGroup, originalResponse);
}
function fetchEpisodeGroupDetailsForMain(tvShowId, groupId, seasonsGroup, originalResponse) {
let cacheKey = `episode_group_${groupId}`;
let cachedDetails = getCachedData(cacheKey);
if (cachedDetails) {
console.log(`📦 Using cached episode group details`);
updateMainTVResponse(originalResponse, cachedDetails, seasonsGroup);
return;
}
let url = `https://api.themoviedb.org/3/tv/episode_group/${groupId}`;
console.log(`🔄 Fetching episode group details...`);
$httpClient.get({
url: url,
headers: {
"Authorization": `Bearer ${TMDB_ACCESS_TOKEN}`,
"Content-Type": "application/json"
},
timeout: 5000
}, function(error, response, data) {
if (error) {
console.log(`❌ Episode group details fetch failed: ${error}`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
return;
}
try {
let detailsData = JSON.parse(data);
if (detailsData && detailsData.groups) {
console.log(`✅ Fetched episode group details`);
setCachedData(cacheKey, detailsData, CACHE_DURATION.EPISODE_GROUP_DETAILS);
updateMainTVResponse(originalResponse, detailsData, seasonsGroup);
} else {
console.log(`❌ Invalid episode group details`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
}
} catch (e) {
console.log(`❌ Failed to parse episode group details: ${e}`);
// Remove Season 0 from original response before returning
originalResponse = removeSpecialsFromResponse(originalResponse);
$done({
status: 200,
body: JSON.stringify(originalResponse)
});
}
});
}
function updateMainTVResponse(tmdbResponse, episodeGroupDetails, seasonsGroupSummary) {
console.log(`\n🔄 Updating TMDB response with episode group data...`);
// Filter out Season 0 and seasons with "Special" or "OVA" in the name
let regularSeasons = episodeGroupDetails.groups.filter(g => {
// Exclude Season 0
if (g.order <= 0) return false;
// Exclude if name contains "Special", "Specials", or "OVA" (case-insensitive)
if (g.name) {
let nameLower = g.name.toLowerCase();
if (nameLower.includes("special") || nameLower.includes("ova")) {
console.log(` 🗑️ Excluding season: "${g.name}" (order: ${g.order})`);
return false;
}
}
return true;
});
console.log(` Filtered: ${episodeGroupDetails.groups.length} total groups → ${regularSeasons.length} regular seasons`);
// Calculate total episodes excluding Specials/OVAs
let totalEpisodes = 0;
regularSeasons.forEach(group => {
totalEpisodes += group.episodes.length;
});
// Update counts
tmdbResponse.number_of_episodes = totalEpisodes;
tmdbResponse.number_of_seasons = regularSeasons.length;
console.log(` Total Episodes: ${tmdbResponse.number_of_episodes} (excluding Specials/OVAs)`);
console.log(` Total Seasons: ${tmdbResponse.number_of_seasons}`);
// Store old seasons data for metadata
let oldSeasons = tmdbResponse.seasons || [];
let oldSeasonData = {};
oldSeasons.forEach(season => {
oldSeasonData[season.season_number] = {
poster_path: season.poster_path,
overview: season.overview,
vote_average: season.vote_average,
id: season.id
};
});
// Build new seasons array (excluding Season 0 and Specials/OVAs)
tmdbResponse.seasons = [];
let lastKnownPosterPath = null;
regularSeasons.forEach(group => {
let seasonNumber = group.order;
let episodes = group.episodes;
let oldData = oldSeasonData[seasonNumber] || {};
// Determine poster_path
let posterPath = null;
if (oldData.poster_path) {
posterPath = oldData.poster_path;
lastKnownPosterPath = posterPath;
} else if (lastKnownPosterPath) {
posterPath = lastKnownPosterPath;
console.log(` 🖼️ Season ${seasonNumber} using last known poster`);
}
let seasonObj = {
air_date: episodes.length > 0 ? episodes[0].air_date : null,
episode_count: episodes.length,
id: oldData.id || episodeGroupIdToNumber(group.id) || null,
name: group.name || `Season ${seasonNumber}`,
overview: oldData.overview || "",
poster_path: posterPath,
season_number: seasonNumber,
vote_average: oldData.vote_average || 0
};
tmdbResponse.seasons.push(seasonObj);
console.log(` ✓ Season ${seasonNumber}: ${episodes.length} episodes - ${group.name}`);
});
// Sort seasons by season_number
tmdbResponse.seasons.sort((a, b) => a.season_number - b.season_number);
// Update last_episode_to_air and next_episode_to_air (excluding Specials/OVAs)
let today = new Date();
let allEpisodes = [];
regularSeasons.forEach(group => {
group.episodes.forEach((ep, index) => {
allEpisodes.push({
...ep,
seasonNumber: group.order,
episodeNumber: index + 1 // Sequential numbering within group
});
});
});
// Find last aired episode
let airedEpisodes = allEpisodes.filter(ep => {
if (!ep.air_date) return false;
let airDate = new Date(ep.air_date);
return airDate <= today;
});
airedEpisodes.sort((a, b) => new Date(b.air_date) - new Date(a.air_date));
if (airedEpisodes.length > 0) {
let lastEp = airedEpisodes[0];
tmdbResponse.last_episode_to_air = {
id: lastEp.id,
name: lastEp.name || "Untitled",
overview: lastEp.overview || "",
vote_average: lastEp.vote_average || 0,
vote_count: lastEp.vote_count || 0,
air_date: lastEp.air_date,
episode_number: lastEp.episodeNumber,
episode_type: lastEp.episode_type || "standard",
production_code: lastEp.production_code || "",
runtime: lastEp.runtime || 24,
season_number: lastEp.seasonNumber,
show_id: tmdbResponse.id,
still_path: lastEp.still_path || null
};
console.log(` Last Episode: S${lastEp.seasonNumber}E${lastEp.episodeNumber} - ${lastEp.name}`);
console.log(` Aired: ${lastEp.air_date}`);
}
// Find next episode to air
let unairedEpisodes = allEpisodes.filter(ep => {
if (!ep.air_date) return false;
let airDate = new Date(ep.air_date);
return airDate > today;
});
unairedEpisodes.sort((a, b) => new Date(a.air_date) - new Date(b.air_date));
if (unairedEpisodes.length > 0) {
let nextEp = unairedEpisodes[0];
tmdbResponse.next_episode_to_air = {
id: nextEp.id,
name: nextEp.name || "Untitled",
overview: nextEp.overview || "",
vote_average: nextEp.vote_average || 0,
vote_count: nextEp.vote_count || 0,
air_date: nextEp.air_date,
episode_number: nextEp.episodeNumber,
episode_type: nextEp.episode_type || "standard",
production_code: nextEp.production_code || "",
runtime: nextEp.runtime || 24,
season_number: nextEp.seasonNumber,
show_id: tmdbResponse.id,
still_path: nextEp.still_path || null
};
console.log(` Next Episode: S${nextEp.seasonNumber}E${nextEp.episodeNumber} - ${nextEp.name}`);
console.log(` Airs: ${nextEp.air_date}`);
} else {
tmdbResponse.next_episode_to_air = null;
console.log(` Next Episode: None scheduled`);
}
// Update last_air_date
if (airedEpisodes.length > 0) {
tmdbResponse.last_air_date = airedEpisodes[0].air_date;
}
console.log(`\n✅ TMDB response updated with episode group data (Specials/OVAs excluded)`);
$done({
status: 200,
body: JSON.stringify(tmdbResponse)
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment