diff --git a/src/providers/all.ts b/src/providers/all.ts index d9e544b..b1c8807 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -77,6 +77,7 @@ import { uiraliveScraper } from './sources/uiralive'; import { vidapiClickScraper } from './sources/vidapiclick'; import { warezcdnScraper } from './sources/warezcdn'; import { webtorScraper } from './sources/webtor'; +import { xprimeScraper } from './sources/xprime'; export function gatherAllSources(): Array { // all sources are gathered here @@ -106,6 +107,7 @@ export function gatherAllSources(): Array { nunflixScraper, riveScraper, EightStreamScraper, + xprimeScraper, ]; } diff --git a/src/providers/sources/xprime.ts b/src/providers/sources/xprime.ts new file mode 100644 index 0000000..a540bb8 --- /dev/null +++ b/src/providers/sources/xprime.ts @@ -0,0 +1,81 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +const baseUrl = 'https://xprime.tv/foxtemp'; + +const languageMap: Record = { + 'chinese - hong kong': 'zh', + 'chinese - traditional': 'zh', + czech: 'cs', + danish: 'da', + dutch: 'nl', + english: 'en', + 'english - sdh': 'en', + finnish: 'fi', + french: 'fr', + german: 'de', + greek: 'el', + hungarian: 'hu', + italian: 'it', + korean: 'ko', + norwegian: 'no', + polish: 'pl', + portuguese: 'pt', + 'portuguese - brazilian': 'pt', + romanian: 'ro', + 'spanish - european': 'es', + 'spanish - latin american': 'es', + swedish: 'sv', + turkish: 'tr', +}; + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const params = new URLSearchParams({ + name: ctx.media.title, + pstream: 'true', + }); + + if (ctx.media.type === 'show') { + params.append('season', ctx.media.season.number.toString()); + params.append('episode', ctx.media.episode.number.toString()); + } + + const apiRes = await ctx.fetcher(`${baseUrl}?${params.toString()}`); + if (!apiRes) throw new NotFoundError('No response received'); + const data = await JSON.parse(apiRes); + if (!data.url) throw new NotFoundError('No stream URL found in response'); + + const captions = + data.subtitles?.map((sub: { file: string; label: string }) => ({ + type: 'vtt', + url: sub.file, + language: languageMap[sub.label.toLowerCase()] || 'unknown', + })) || []; + + ctx.progress(90); + + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist: `https://oca.kendrickl-3amar.site/?v=${encodeURIComponent(data.url)}&headers=${encodeURIComponent(JSON.stringify({ referer: 'https://megacloud.store/', origin: 'https://megacloud.store' }))}`, + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + embeds: [], + }; +} + +export const xprimeScraper = makeSourcerer({ + id: 'xprimetv', + name: 'xprime.tv', + rank: 240, + disabled: false, + flags: [flags.CORS_ALLOWED], + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +}); diff --git a/src/utils/valid.ts b/src/utils/valid.ts index f90df7c..e46823a 100644 --- a/src/utils/valid.ts +++ b/src/utils/valid.ts @@ -4,6 +4,7 @@ import { FedAPIPrivateScraper, FedDBScraper } from '@/providers/embeds/fedapi'; import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4'; import { embedsuScraper } from '@/providers/sources/embedsu'; import { uiraliveScraper } from '@/providers/sources/uiralive'; +import { xprimeScraper } from '@/providers/sources/xprime'; import { Stream } from '@/providers/streams'; import { IndividualEmbedRunnerOptions } from '@/runners/individualRunner'; import { ProviderRunnerOptions } from '@/runners/runner'; @@ -19,6 +20,7 @@ const SKIP_VALIDATION_CHECK_IDS = [ embedsuScraper.id, FedAPIPrivateScraper.id, FedDBScraper.id, + xprimeScraper.id, ]; export function isValidStream(stream: Stream | undefined): boolean {