From 8ef40f0b4fe2596581be3eb239dae57d84d5ef5f Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Fri, 28 Feb 2025 19:32:26 -0700 Subject: [PATCH] add fed db --- src/providers/all.ts | 2 + src/providers/sources/fedapidb.ts | 144 ++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/providers/sources/fedapidb.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 7b17ac0..de7eb58 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -83,6 +83,7 @@ import { webtor1080Scraper, webtor480Scraper, webtor4kScraper, webtor720Scraper import { wootlyScraper } from './embeds/wootly'; import { embedsuScraper } from './sources/embedsu'; import { FedAPIScraper } from './sources/fedapi'; +import { FedAPIDBScraper } from './sources/fedapidb'; import { goojaraScraper } from './sources/goojara'; import { hdRezkaScraper } from './sources/hdrezka'; import { iosmirrorScraper } from './sources/iosmirror'; @@ -140,6 +141,7 @@ export function gatherAllSources(): Array { embedsuScraper, vidlinkScraper, FedAPIScraper, + FedAPIDBScraper, slidemoviesScraper, iosmirrorScraper, iosmirrorPVScraper, diff --git a/src/providers/sources/fedapidb.ts b/src/providers/sources/fedapidb.ts new file mode 100644 index 0000000..ebca561 --- /dev/null +++ b/src/providers/sources/fedapidb.ts @@ -0,0 +1,144 @@ +/* eslint-disable no-console */ +import { flags } from '@/entrypoint/utils/targets'; +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { Caption } from '../captions'; + +// Thanks Nemo for this API, and Custom for hosting! +const BASE_URL = 'https://fed-api.pstream.org/cache'; + +// this is so fucking useless +const languageMap: Record = { + English: 'en', + Spanish: 'es', + French: 'fr', + German: 'de', + Italian: 'it', + Portuguese: 'pt', + Arabic: 'ar', + Russian: 'ru', + Japanese: 'ja', + Korean: 'ko', + Chinese: 'zh', + Hindi: 'hi', + Turkish: 'tr', + Dutch: 'nl', + Polish: 'pl', + Swedish: 'sv', + Indonesian: 'id', + Thai: 'th', + Vietnamese: 'vi', +}; + +interface StreamData { + streams: Record; + subtitles: Record>; + error?: string; +} + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const apiUrl = + ctx.media.type === 'movie' + ? `${BASE_URL}/${ctx.media.imdbId}` + : `${BASE_URL}/${ctx.media.imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + + const data = await ctx.fetcher(apiUrl); + + if (data?.error) { + throw new NotFoundError('No stream found'); + } + if (!data) throw new NotFoundError('No response from API'); + ctx.progress(50); + + const streams = Object.entries(data.streams).reduce((acc: Record, [quality, url]) => { + let qualityKey: number; + if (quality === '4K') { + qualityKey = 2160; + } else if (quality === 'ORG') { + return acc; + } else { + qualityKey = parseInt(quality.replace('P', ''), 10); + } + if (Number.isNaN(qualityKey) || acc[qualityKey]) return acc; + acc[qualityKey] = url; + return acc; + }, {}); + + const captions: Caption[] = []; + for (const [langKey, subs] of Object.entries(data.subtitles)) { + // Extract language name from key ("english_subtitles" -> "English") + const languageKeyPart = langKey.split('_')[0]; + const languageName = languageKeyPart.charAt(0).toUpperCase() + languageKeyPart.slice(1); + const languageCode = languageMap[languageName]?.toLowerCase() ?? 'unknown'; + + for (const sub of subs) { + const url = sub.url; + const isVtt = url.toLowerCase().endsWith('.vtt'); + captions.push({ + type: isVtt ? 'vtt' : 'srt', + id: url, + url, + language: languageCode, + hasCorsRestrictions: false, + }); + } + } + + ctx.progress(90); + + return { + embeds: [], + stream: [ + { + id: 'primary', + captions, + qualities: { + ...(streams[2160] && { + '4k': { + type: 'mp4', + url: streams[2160], + }, + }), + ...(streams[1080] && { + 1080: { + type: 'mp4', + url: streams[1080], + }, + }), + ...(streams[720] && { + 720: { + type: 'mp4', + url: streams[720], + }, + }), + ...(streams[480] && { + 480: { + type: 'mp4', + url: streams[480], + }, + }), + ...(streams[360] && { + 360: { + type: 'mp4', + url: streams[360], + }, + }), + }, + type: 'file', + flags: [flags.CORS_ALLOWED], + }, + ], + }; +} + +export const FedAPIDBScraper = makeSourcerer({ + id: 'fedapidb', + name: 'FED DB (Beta)', + rank: 259, + disabled: false, + flags: [flags.CORS_ALLOWED], + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +});