add febbox subtitles API

This commit is contained in:
Pas 2025-08-26 22:31:35 -06:00
parent 575521cc88
commit 6f9e08b4ab

View file

@ -212,6 +212,88 @@ export async function scrapeOpenSubtitlesCaptions(
}
}
export async function scrapeFebboxCaptions(
imdbId: string,
season?: number,
episode?: number,
): Promise<CaptionListItem[]> {
try {
let url: string;
if (season && episode) {
url = `https://fed-subs.pstream.mov/tv/${imdbId}/s${season}/e${episode}`;
} else {
url = `https://fed-subs.pstream.mov/movie/${imdbId}`;
}
// console.log("Searching Febbox subtitles with URL:", url);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Febbox API returned ${response.status}`);
}
const data = await response.json();
// Check for error response
if (data.error) {
console.log("Febbox API error:", data.error);
return [];
}
// Check if subtitles exist
if (!data.subtitles || typeof data.subtitles !== "object") {
console.log("No subtitles found in Febbox response");
return [];
}
const febboxCaptions: CaptionListItem[] = [];
// Iterate through all available languages
for (const [languageName, subtitleData] of Object.entries(data.subtitles)) {
if (typeof subtitleData === "object" && subtitleData !== null) {
const subtitle = subtitleData as {
subtitle_link: string;
subtitle_name: string;
};
if (subtitle.subtitle_link) {
const language = labelToLanguageCode(languageName);
const fileExtension = subtitle.subtitle_link
.split(".")
.pop()
?.toLowerCase();
// Determine subtitle type based on file extension
let type: string = "srt";
if (fileExtension === "vtt") {
type = "vtt";
} else if (fileExtension === "sub") {
type = "sub";
}
febboxCaptions.push({
id: subtitle.subtitle_link,
language,
url: subtitle.subtitle_link,
type,
needsProxy: false,
opensubtitles: true,
display: subtitle.subtitle_name,
source: "febbox",
});
}
}
}
console.log(`Found ${febboxCaptions.length} Febbox subtitles`);
return febboxCaptions;
} catch (error) {
console.error("Error fetching Febbox subtitles:", error);
return [];
}
}
export async function scrapeExternalSubtitles(
meta: PlayerMeta,
): Promise<CaptionListItem[]> {
@ -227,25 +309,32 @@ export async function scrapeExternalSubtitles(
const episode = meta.episode?.number;
const tmdbId = meta.tmdbId;
// Fetch both Wyzie and OpenSubtitles captions with timeouts
const [wyzieCaptions, openSubsCaptions] = await Promise.all([
Promise.race([
scrapeWyzieCaptions(tmdbId, imdbId, season, episode),
timeout(2000, "Wyzie"),
]),
Promise.race([
scrapeOpenSubtitlesCaptions(imdbId, season, episode),
timeout(5000, "OpenSubtitles"),
]),
]);
// Fetch Wyzie, OpenSubtitles, and Febbox captions with timeouts
const [wyzieCaptions, openSubsCaptions, febboxCaptions] = await Promise.all(
[
Promise.race([
scrapeWyzieCaptions(tmdbId, imdbId, season, episode),
timeout(2000, "Wyzie"),
]),
Promise.race([
scrapeOpenSubtitlesCaptions(imdbId, season, episode),
timeout(5000, "OpenSubtitles"),
]),
Promise.race([
scrapeFebboxCaptions(imdbId, season, episode),
timeout(3000, "Febbox"),
]),
],
);
const allCaptions: CaptionListItem[] = [];
if (wyzieCaptions) allCaptions.push(...wyzieCaptions);
if (openSubsCaptions) allCaptions.push(...openSubsCaptions);
if (febboxCaptions) allCaptions.push(...febboxCaptions);
console.log(
`Found ${allCaptions.length} external captions (Wyzie: ${wyzieCaptions?.length || 0}, OpenSubtitles: ${openSubsCaptions?.length || 0})`,
`Found ${allCaptions.length} external captions (Wyzie: ${wyzieCaptions?.length || 0}, OpenSubtitles: ${openSubsCaptions?.length || 0}, Febbox: ${febboxCaptions?.length || 0})`,
);
return allCaptions;