mirror of
https://github.com/p-stream/providers.git
synced 2026-01-11 20:10:33 +00:00
bye bye
This commit is contained in:
parent
fdf0dd9941
commit
2281bdfa37
20 changed files with 0 additions and 2348 deletions
|
|
@ -1,11 +1,5 @@
|
|||
import { Embed, Sourcerer } from '@/providers/base';
|
||||
import { doodScraper } from '@/providers/embeds/dood';
|
||||
import {
|
||||
hianimeHd1DubEmbed,
|
||||
hianimeHd1SubEmbed,
|
||||
hianimeHd2DubEmbed,
|
||||
hianimeHd2SubEmbed,
|
||||
} from '@/providers/embeds/hianime';
|
||||
import { mixdropScraper } from '@/providers/embeds/mixdrop';
|
||||
import { turbovidScraper } from '@/providers/embeds/turbovid';
|
||||
import { upcloudScraper } from '@/providers/embeds/upcloud';
|
||||
|
|
@ -13,23 +7,11 @@ import { autoembedScraper } from '@/providers/sources/autoembed';
|
|||
import { catflixScraper } from '@/providers/sources/catflix';
|
||||
import { ee3Scraper } from '@/providers/sources/ee3';
|
||||
import { fsharetvScraper } from '@/providers/sources/fsharetv';
|
||||
import { hianimeScraper } from '@/providers/sources/hianime';
|
||||
import { insertunitScraper } from '@/providers/sources/insertunit';
|
||||
import { mp4hydraScraper } from '@/providers/sources/mp4hydra';
|
||||
import { tugaflixScraper } from '@/providers/sources/tugaflix';
|
||||
import { vidsrcsuScraper } from '@/providers/sources/vidsrcsu';
|
||||
|
||||
import {
|
||||
oneServerAnimepaheEmbed,
|
||||
oneServerAnizoneEmbed,
|
||||
oneServerAutoembedEmbed,
|
||||
oneServerFlixhqEmbed,
|
||||
oneServerFoxstreamEmbed,
|
||||
oneServerGokuEmbed,
|
||||
oneServerHianimeEmbed,
|
||||
oneServerPrimeboxEmbed,
|
||||
oneServerVidsrcsuEmbed,
|
||||
} from './embeds/1server';
|
||||
import {
|
||||
autoembedBengaliScraper,
|
||||
autoembedEnglishScraper,
|
||||
|
|
@ -39,13 +21,6 @@ import {
|
|||
} from './embeds/autoembed';
|
||||
import { cinemaosEmbeds } from './embeds/cinemaos';
|
||||
import { closeLoadScraper } from './embeds/closeload';
|
||||
import {
|
||||
ConsumetStreamSBScraper,
|
||||
ConsumetStreamTapeScraper,
|
||||
ConsumetVidCloudScraper,
|
||||
ConsumetVidStreamingScraper,
|
||||
} from './embeds/consumet';
|
||||
import { FedAPIPrivateScraper, FedDBScraper } from './embeds/fedapi';
|
||||
import { mp4hydraServer1Scraper, mp4hydraServer2Scraper } from './embeds/mp4hydra';
|
||||
import { ridooScraper } from './embeds/ridoo';
|
||||
import { streamtapeLatinoScraper, streamtapeScraper } from './embeds/streamtape';
|
||||
|
|
@ -76,43 +51,23 @@ import { viperScraper } from './embeds/viper';
|
|||
import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls';
|
||||
import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4';
|
||||
import { warezPlayerScraper } from './embeds/warezcdn/warezplayer';
|
||||
import { webtor1080Scraper, webtor480Scraper, webtor4kScraper, webtor720Scraper } from './embeds/webtor';
|
||||
import {
|
||||
xprimeApolloEmbed,
|
||||
xprimeFendiEmbed,
|
||||
xprimeFoxEmbed,
|
||||
xprimeHarbourEmbed,
|
||||
xprimeMarantEmbed,
|
||||
xprimePhoenixEmbed,
|
||||
xprimePrimenetEmbed,
|
||||
xprimeStreamboxEmbed,
|
||||
xprimeVolkswagenEmbed,
|
||||
} from './embeds/xprime';
|
||||
import { oneServerScraper } from './sources/1server';
|
||||
import { EightStreamScraper } from './sources/8stream';
|
||||
import { animeflvScraper } from './sources/animeflv';
|
||||
import { cinemaosScraper } from './sources/cinemaos';
|
||||
import { coitusScraper } from './sources/coitus';
|
||||
import { ConsumetScraper } from './sources/consumet';
|
||||
import { cuevana3Scraper } from './sources/cuevana3';
|
||||
import { embedsuScraper } from './sources/embedsu';
|
||||
import { FedAPIScraper } from './sources/fedapi';
|
||||
import { hdRezkaScraper } from './sources/hdrezka';
|
||||
import { hollymoviehdScraper } from './sources/hollymoviehd';
|
||||
import { iosmirrorScraper } from './sources/iosmirror';
|
||||
import { iosmirrorPVScraper } from './sources/iosmirrorpv';
|
||||
import { nunflixScraper } from './sources/nunflix';
|
||||
import { oneroomScraper } from './sources/oneroom';
|
||||
import { ridooMoviesScraper } from './sources/ridomovies';
|
||||
import { slidemoviesScraper } from './sources/slidemovies';
|
||||
import { soaperTvScraper } from './sources/soapertv';
|
||||
import { streamboxScraper } from './sources/streambox';
|
||||
import { uiraliveScraper } from './sources/uiralive';
|
||||
import { vidapiClickScraper } from './sources/vidapiclick';
|
||||
import { warezcdnScraper } from './sources/warezcdn';
|
||||
import { webtorScraper } from './sources/webtor';
|
||||
import { wecimaScraper } from './sources/wecima';
|
||||
import { xprimeScraper } from './sources/xprime';
|
||||
|
||||
export function gatherAllSources(): Array<Sourcerer> {
|
||||
// all sources are gathered here
|
||||
|
|
@ -130,27 +85,18 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
fsharetvScraper,
|
||||
vidsrcsuScraper,
|
||||
mp4hydraScraper,
|
||||
webtorScraper,
|
||||
embedsuScraper,
|
||||
FedAPIScraper,
|
||||
slidemoviesScraper,
|
||||
iosmirrorScraper,
|
||||
iosmirrorPVScraper,
|
||||
uiraliveScraper,
|
||||
vidapiClickScraper,
|
||||
coitusScraper,
|
||||
streamboxScraper,
|
||||
nunflixScraper,
|
||||
EightStreamScraper,
|
||||
xprimeScraper,
|
||||
ConsumetScraper,
|
||||
hianimeScraper,
|
||||
oneServerScraper,
|
||||
wecimaScraper,
|
||||
animeflvScraper,
|
||||
cinemaosScraper,
|
||||
hollymoviehdScraper,
|
||||
oneroomScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -189,39 +135,7 @@ export function gatherAllEmbeds(): Array<Embed> {
|
|||
VidsrcsuServer11Scraper,
|
||||
VidsrcsuServer12Scraper,
|
||||
VidsrcsuServer20Scraper,
|
||||
webtor4kScraper,
|
||||
webtor1080Scraper,
|
||||
webtor720Scraper,
|
||||
webtor480Scraper,
|
||||
viperScraper,
|
||||
FedAPIPrivateScraper,
|
||||
FedDBScraper,
|
||||
xprimeFoxEmbed,
|
||||
xprimeApolloEmbed,
|
||||
xprimeStreamboxEmbed,
|
||||
xprimeMarantEmbed,
|
||||
xprimeFendiEmbed,
|
||||
xprimePrimenetEmbed,
|
||||
xprimeVolkswagenEmbed,
|
||||
xprimeHarbourEmbed,
|
||||
xprimePhoenixEmbed,
|
||||
ConsumetVidCloudScraper,
|
||||
ConsumetStreamSBScraper,
|
||||
ConsumetVidStreamingScraper,
|
||||
ConsumetStreamTapeScraper,
|
||||
hianimeHd1DubEmbed,
|
||||
hianimeHd2DubEmbed,
|
||||
hianimeHd1SubEmbed,
|
||||
hianimeHd2SubEmbed,
|
||||
oneServerAutoembedEmbed,
|
||||
oneServerVidsrcsuEmbed,
|
||||
oneServerPrimeboxEmbed,
|
||||
oneServerFoxstreamEmbed,
|
||||
oneServerFlixhqEmbed,
|
||||
oneServerGokuEmbed,
|
||||
oneServerHianimeEmbed,
|
||||
oneServerAnimepaheEmbed,
|
||||
oneServerAnizoneEmbed,
|
||||
streamwishJapaneseScraper,
|
||||
streamwishLatinoScraper,
|
||||
streamwishSpanishScraper,
|
||||
|
|
|
|||
|
|
@ -1,294 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { EmbedOutput, makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { createM3U8ProxyUrl, updateM3U8ProxyUrl } from '@/utils/proxy';
|
||||
|
||||
const baseUrl = 'https://flix.1anime.app';
|
||||
|
||||
const languageMap: Record<string, string> = {
|
||||
'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',
|
||||
اَلْعَرَبِيَّةُ: 'ar',
|
||||
বাংলা: 'bn',
|
||||
filipino: 'tl',
|
||||
indonesia: 'id',
|
||||
اردو: 'ur',
|
||||
};
|
||||
|
||||
function createProxyUrl(originalUrl: string, referer?: string): string {
|
||||
const headers: Record<string, string> = {};
|
||||
if (referer) {
|
||||
headers.referer = referer;
|
||||
}
|
||||
|
||||
return createM3U8ProxyUrl(originalUrl, headers);
|
||||
}
|
||||
|
||||
function processProxiedURL(url: string): string {
|
||||
// Handle orbitproxy URLs
|
||||
if (url.includes('orbitproxy')) {
|
||||
try {
|
||||
const urlParts = url.split(/orbitproxy\.[^/]+\//);
|
||||
if (urlParts.length >= 2) {
|
||||
const encryptedPart = urlParts[1].split('.m3u8')[0];
|
||||
try {
|
||||
const decodedData =
|
||||
typeof window !== 'undefined'
|
||||
? atob(encryptedPart)
|
||||
: Buffer.from(encryptedPart, 'base64').toString('utf-8');
|
||||
|
||||
const jsonData = JSON.parse(decodedData);
|
||||
const originalUrl = jsonData.u;
|
||||
const referer = jsonData.r || '';
|
||||
|
||||
return createProxyUrl(originalUrl, referer);
|
||||
} catch (jsonError) {
|
||||
console.error('Error decoding/parsing orbitproxy data:', jsonError);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing orbitproxy URL:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle other proxied URLs
|
||||
if (url.includes('/m3u8-proxy?url=')) {
|
||||
return updateM3U8ProxyUrl(url);
|
||||
}
|
||||
|
||||
return createProxyUrl(url);
|
||||
}
|
||||
|
||||
function isOnionflixerUrl(url: string): boolean {
|
||||
return url.includes('onionflixer');
|
||||
}
|
||||
|
||||
function processSubtitles(subtitles: any[] | undefined): any[] {
|
||||
const captions = [];
|
||||
|
||||
if (subtitles && Array.isArray(subtitles)) {
|
||||
for (const sub of subtitles) {
|
||||
const url = sub.url || sub.file;
|
||||
const lang = sub.lang || sub.label || 'unknown';
|
||||
|
||||
if (url) {
|
||||
captions.push({
|
||||
type: sub.type || 'vtt',
|
||||
url,
|
||||
language: languageMap[lang.toLowerCase()] || lang.toLowerCase() || 'unknown',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return captions;
|
||||
}
|
||||
|
||||
function processApiResponse(response: any, ctx: any): EmbedOutput {
|
||||
if (!response) {
|
||||
throw new NotFoundError('No response received');
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new NotFoundError(`${response.error}${response.hint ? ` - ${response.hint}` : ''}`);
|
||||
}
|
||||
|
||||
// Handle array responses (multiple provider options)
|
||||
if (Array.isArray(response)) {
|
||||
for (const item of response) {
|
||||
// Handle format with headers and sources array
|
||||
if (item.headers && item.sources && Array.isArray(item.sources)) {
|
||||
const bestSource = item.sources.find((s: any) => s.isM3U8);
|
||||
if (bestSource && bestSource.url) {
|
||||
const playlistUrl = processProxiedURL(bestSource.url);
|
||||
const captions = processSubtitles(item.subtitles);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: playlistUrl,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Handle format with source.provider and source.files
|
||||
if (item.source && item.source.files && Array.isArray(item.source.files)) {
|
||||
const bestFile = item.source.files.find((f: any) => f.type === 'hls' || f.file.includes('.m3u8'));
|
||||
if (bestFile && bestFile.file) {
|
||||
const playlistUrl = processProxiedURL(bestFile.file);
|
||||
const captions = processSubtitles(item.source.subtitles);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: playlistUrl,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle original format with sources object
|
||||
const sourcesObj = response.sources;
|
||||
if (sourcesObj) {
|
||||
let bestSource = null;
|
||||
|
||||
// Look through all providers in the sources object
|
||||
for (const provider in sourcesObj) {
|
||||
if (Object.prototype.hasOwnProperty.call(sourcesObj, provider)) {
|
||||
const providerSources = sourcesObj[provider];
|
||||
if (providerSources && providerSources.length > 0) {
|
||||
// Find the highest quality non-onionflixer source
|
||||
for (const source of providerSources) {
|
||||
if (source.url && source.isM3U8 && !isOnionflixerUrl(source.url)) {
|
||||
bestSource = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we found a good source, no need to check other providers
|
||||
if (bestSource) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no non-onionflixer source was found, use the first available one
|
||||
if (!bestSource) {
|
||||
for (const provider in sourcesObj) {
|
||||
if (Object.prototype.hasOwnProperty.call(sourcesObj, provider)) {
|
||||
const providerSources = sourcesObj[provider];
|
||||
if (providerSources && providerSources.length > 0) {
|
||||
bestSource = providerSources[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestSource && bestSource.url) {
|
||||
const playlistUrl = processProxiedURL(bestSource.url);
|
||||
const captions = processSubtitles(response.subtitles);
|
||||
|
||||
ctx.progress(100);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: createProxyUrl(playlistUrl, ctx.referer),
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundError('No valid stream URL found in response');
|
||||
}
|
||||
|
||||
/* MOVIE & TV PROVIDERS */
|
||||
const movieTvProviders = [
|
||||
{ id: 'autoembed', name: 'Autoembed', rank: 165 },
|
||||
{ id: 'vidsrcsu', name: 'vidsrc.su', rank: 164, disabled: true },
|
||||
{ id: 'primebox', name: 'Primebox', rank: 162, disabled: true },
|
||||
{ id: 'foxstream', name: 'Foxstream', rank: 161, disabled: true },
|
||||
{ id: 'flixhq', name: 'FlixHQ', rank: 166 },
|
||||
{ id: 'goku', name: 'Goku', rank: 163, disabled: true },
|
||||
];
|
||||
|
||||
const createMovieTvEmbed = (provider: { id: string; name: string; rank: number; disabled?: boolean }) =>
|
||||
makeEmbed({
|
||||
id: `oneserver-${provider.id}`,
|
||||
name: provider.name,
|
||||
rank: provider.rank,
|
||||
disabled: provider.disabled,
|
||||
async scrape(ctx) {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const apiUrl =
|
||||
query.type === 'movie'
|
||||
? `${baseUrl}/movie/${provider.id}/${query.tmdbId}`
|
||||
: `${baseUrl}/tv/${provider.id}/${query.tmdbId}/${query.season}/${query.episode}`;
|
||||
|
||||
try {
|
||||
const response = await ctx.fetcher(apiUrl);
|
||||
ctx.progress(50);
|
||||
return processApiResponse(response, ctx);
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) throw error;
|
||||
throw new NotFoundError(`Failed to fetch from ${provider.id}: ${error}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const [
|
||||
oneServerAutoembedEmbed,
|
||||
oneServerVidsrcsuEmbed,
|
||||
oneServerPrimeboxEmbed,
|
||||
oneServerFoxstreamEmbed,
|
||||
oneServerFlixhqEmbed,
|
||||
oneServerGokuEmbed,
|
||||
] = movieTvProviders.map(createMovieTvEmbed);
|
||||
|
||||
/* ANIME PROVIDERS */
|
||||
const animeProviders = [
|
||||
{ id: 'hianime', name: 'Hianime', rank: 269 },
|
||||
{ id: 'animepahe', name: 'Animepahe', rank: 268 },
|
||||
{ id: 'anizone', name: 'Anizone', rank: 267 },
|
||||
];
|
||||
|
||||
const createAnimeEmbed = (provider: { id: string; name: string; rank: number }) =>
|
||||
makeEmbed({
|
||||
id: `oneserver-${provider.id}`,
|
||||
name: provider.name,
|
||||
rank: provider.rank,
|
||||
async scrape(ctx) {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const apiUrl = `${baseUrl}/anime/${provider.id}/${query.anilistId}${query.episode ? `/${query.episode}` : ''}`;
|
||||
|
||||
try {
|
||||
const response = await ctx.fetcher(apiUrl);
|
||||
return processApiResponse(response, ctx);
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) throw error;
|
||||
throw new NotFoundError(`Failed to fetch from ${provider.id}: ${error}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const [oneServerHianimeEmbed, oneServerAnimepaheEmbed, oneServerAnizoneEmbed] =
|
||||
animeProviders.map(createAnimeEmbed);
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { EmbedOutput, makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { createM3U8ProxyUrl } from '@/utils/proxy';
|
||||
|
||||
import { Caption } from '../captions';
|
||||
|
||||
interface StreamData {
|
||||
headers: {
|
||||
Referer: string;
|
||||
Origin?: string;
|
||||
};
|
||||
intro: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
outro: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
sources: Array<{
|
||||
url: string;
|
||||
isM3U8: boolean;
|
||||
type: string;
|
||||
}>;
|
||||
subtitles: Array<{
|
||||
url: string;
|
||||
lang: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const providers = [
|
||||
{
|
||||
id: 'consumet-vidcloud',
|
||||
rank: 405,
|
||||
name: 'VidCloud',
|
||||
server: 'vidcloud',
|
||||
},
|
||||
{
|
||||
id: 'consumet-streamsb',
|
||||
rank: 404,
|
||||
name: 'StreamSB',
|
||||
server: 'streamsb',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
id: 'consumet-vidstreaming',
|
||||
rank: 403,
|
||||
name: 'VidStreaming',
|
||||
server: 'vidstreaming',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
id: 'consumet-streamtape',
|
||||
rank: 402,
|
||||
name: 'StreamTape',
|
||||
server: 'streamtape',
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
const languageMap: Record<string, string> = {
|
||||
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',
|
||||
};
|
||||
|
||||
function embed(provider: { id: string; rank: number; name: string; server: string; disabled?: boolean }) {
|
||||
return makeEmbed({
|
||||
id: provider.id,
|
||||
name: provider.name,
|
||||
rank: provider.rank,
|
||||
disabled: provider.disabled,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const apiUrl = `https://api.1anime.app/anime/zoro/watch?episodeId=${query.episodeId}&server=${provider.server}`;
|
||||
|
||||
const data = await ctx.fetcher<StreamData>(apiUrl);
|
||||
|
||||
if (!data?.sources?.length) {
|
||||
throw new NotFoundError('No stream found');
|
||||
}
|
||||
|
||||
ctx.progress(50);
|
||||
|
||||
const captions: Caption[] = data.subtitles
|
||||
.filter((sub) => sub.lang !== 'thumbnails')
|
||||
.map((sub) => ({
|
||||
type: 'vtt',
|
||||
id: sub.url,
|
||||
url: sub.url,
|
||||
language: languageMap[sub.lang] || 'unknown',
|
||||
hasCorsRestrictions: false,
|
||||
}));
|
||||
|
||||
const streams = data.sources.reduce(
|
||||
(acc, source) => {
|
||||
if (source.isM3U8) {
|
||||
acc.unknown = source.url;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
const thumbnailTrack = data.subtitles.find((sub) => sub.lang === 'thumbnails');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
if (data.headers.Referer) {
|
||||
headers.referer = data.headers.Referer;
|
||||
}
|
||||
if (data.headers.Origin) {
|
||||
headers.origin = data.headers.Origin;
|
||||
}
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
captions,
|
||||
playlist: createM3U8ProxyUrl(streams.unknown, headers),
|
||||
type: 'hls',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
...(thumbnailTrack && {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: thumbnailTrack.url,
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const [
|
||||
ConsumetVidCloudScraper,
|
||||
ConsumetStreamSBScraper,
|
||||
ConsumetVidStreamingScraper,
|
||||
ConsumetStreamTapeScraper,
|
||||
] = providers.map(embed);
|
||||
|
|
@ -1,298 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
// Thanks Nemo for this API!
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { EmbedOutput, makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
import { Caption } from '../captions';
|
||||
|
||||
const getRegion = (): string | null => {
|
||||
try {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const regionData = window.localStorage.getItem('__MW::region');
|
||||
if (!regionData) return null;
|
||||
const parsed = JSON.parse(regionData);
|
||||
return parsed?.state?.region ?? null;
|
||||
} catch (e) {
|
||||
console.warn('Unable to access localStorage or parse auth data:', e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getBaseUrl = (): string => {
|
||||
const region = getRegion();
|
||||
switch (region) {
|
||||
case 'us-east':
|
||||
return 'https://fed-api-east.pstream.org';
|
||||
case 'us-west':
|
||||
return 'https://fed-api-west.pstream.org';
|
||||
case 'south-america':
|
||||
return 'https://fed-api-south.pstream.org';
|
||||
case 'asia':
|
||||
return 'https://fed-api-asia.pstream.org';
|
||||
case 'europe':
|
||||
return 'https://fed-api-europe.pstream.org';
|
||||
default:
|
||||
return 'https://fed-api-east.pstream.org';
|
||||
}
|
||||
};
|
||||
|
||||
const BASE_URL = getBaseUrl();
|
||||
|
||||
// Language mapping for subtitles
|
||||
const languageMap: Record<string, string> = {
|
||||
'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',
|
||||
اَلْعَرَبِيَّةُ: 'ar',
|
||||
বাংলা: 'bn',
|
||||
filipino: 'tl',
|
||||
indonesia: 'id',
|
||||
اردو: 'ur',
|
||||
English: 'en',
|
||||
Arabic: 'ar',
|
||||
Bosnian: 'bs',
|
||||
Bulgarian: 'bg',
|
||||
Croatian: 'hr',
|
||||
Czech: 'cs',
|
||||
Danish: 'da',
|
||||
Dutch: 'nl',
|
||||
Estonian: 'et',
|
||||
Finnish: 'fi',
|
||||
French: 'fr',
|
||||
German: 'de',
|
||||
Greek: 'el',
|
||||
Hebrew: 'he',
|
||||
Hungarian: 'hu',
|
||||
Indonesian: 'id',
|
||||
Italian: 'it',
|
||||
Norwegian: 'no',
|
||||
Persian: 'fa',
|
||||
Polish: 'pl',
|
||||
Portuguese: 'pt',
|
||||
'Protuguese (BR)': 'pt-br',
|
||||
Romanian: 'ro',
|
||||
Russian: 'ru',
|
||||
Serbian: 'sr',
|
||||
Slovenian: 'sl',
|
||||
Spanish: 'es',
|
||||
Swedish: 'sv',
|
||||
Thai: 'th',
|
||||
Turkish: 'tr',
|
||||
};
|
||||
|
||||
interface StreamData {
|
||||
streams: Record<string, string>;
|
||||
subtitles: Record<string, any>;
|
||||
error?: string;
|
||||
name?: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
const providers = [
|
||||
{
|
||||
id: 'fedapi-private',
|
||||
rank: 303,
|
||||
name: 'FED API (Private)',
|
||||
useToken: true,
|
||||
useCacheUrl: false,
|
||||
},
|
||||
{
|
||||
id: 'feddb',
|
||||
rank: 302,
|
||||
name: 'FED DB',
|
||||
useToken: false,
|
||||
useCacheUrl: true,
|
||||
},
|
||||
];
|
||||
|
||||
function embed(provider: {
|
||||
id: string;
|
||||
rank: number;
|
||||
name: string;
|
||||
useToken: boolean;
|
||||
useCacheUrl: boolean;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return makeEmbed({
|
||||
id: provider.id,
|
||||
name: provider.name,
|
||||
rank: provider.rank,
|
||||
disabled: provider.disabled,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
// Parse the query parameters from the URL
|
||||
const query = JSON.parse(ctx.url);
|
||||
|
||||
// Build the API URL based on the provider configuration and media type
|
||||
let apiUrl: string;
|
||||
|
||||
if (provider.useCacheUrl) {
|
||||
// Cache URL format
|
||||
apiUrl =
|
||||
query.type === 'movie'
|
||||
? `${BASE_URL}/cache/${query.imdbId}`
|
||||
: `${BASE_URL}/cache/${query.imdbId}/${query.season}/${query.episode}`;
|
||||
} else {
|
||||
// Standard API URL format
|
||||
apiUrl =
|
||||
query.type === 'movie'
|
||||
? `${BASE_URL}/movie/${query.imdbId}`
|
||||
: `${BASE_URL}/tv/${query.imdbId}/${query.season}/${query.episode}`;
|
||||
}
|
||||
|
||||
// Prepare request headers
|
||||
const headers: Record<string, string> = {};
|
||||
if (provider.useToken && query.token) {
|
||||
headers['ui-token'] = query.token;
|
||||
}
|
||||
|
||||
// Fetch data from the API
|
||||
const data = await ctx.fetcher<StreamData>(apiUrl, {
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
});
|
||||
|
||||
if (data?.error && data.error.startsWith('No results found in MovieBox search')) {
|
||||
throw new NotFoundError('No stream found');
|
||||
}
|
||||
if (data?.error === 'No cached data found for this episode') {
|
||||
throw new NotFoundError('No stream found');
|
||||
}
|
||||
if (data?.error === 'No cached data found for this ID') {
|
||||
throw new NotFoundError('No stream found');
|
||||
}
|
||||
if (!data) throw new NotFoundError('No response from API');
|
||||
|
||||
ctx.progress(50);
|
||||
|
||||
// Process streams data
|
||||
const streams = Object.entries(data.streams).reduce((acc: Record<string, string>, [quality, url]) => {
|
||||
let qualityKey: number;
|
||||
if (quality === 'ORG') {
|
||||
// Only add unknown quality if it's an mp4 (handle URLs with query parameters)
|
||||
const urlPath = url.split('?')[0]; // Remove query parameters
|
||||
if (urlPath.toLowerCase().endsWith('.mp4')) {
|
||||
acc.unknown = url;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
if (quality === '4K') {
|
||||
qualityKey = 2160;
|
||||
} else {
|
||||
qualityKey = parseInt(quality.replace('P', ''), 10);
|
||||
}
|
||||
if (Number.isNaN(qualityKey) || acc[qualityKey]) return acc;
|
||||
acc[qualityKey] = url;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Filter qualities based on provider type
|
||||
const filteredStreams = Object.entries(streams).reduce((acc: Record<string, string>, [quality, url]) => {
|
||||
// Skip unknown for cached provider
|
||||
if (provider.useCacheUrl && quality === 'unknown') {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc[quality] = url;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Process captions data
|
||||
const captions: Caption[] = [];
|
||||
if (data.subtitles) {
|
||||
for (const [langKey, subtitleData] of Object.entries(data.subtitles)) {
|
||||
// Extract language name from key
|
||||
const languageKeyPart = langKey.split('_')[0];
|
||||
const languageName = languageKeyPart.charAt(0).toUpperCase() + languageKeyPart.slice(1);
|
||||
const languageCode = languageMap[languageName]?.toLowerCase() ?? 'unknown';
|
||||
|
||||
// Check if the subtitle data is in the new format (has subtitle_link)
|
||||
if (subtitleData.subtitle_link) {
|
||||
const url = subtitleData.subtitle_link;
|
||||
const isVtt = url.toLowerCase().endsWith('.vtt');
|
||||
captions.push({
|
||||
type: isVtt ? 'vtt' : 'srt',
|
||||
id: url,
|
||||
url,
|
||||
language: languageCode,
|
||||
hasCorsRestrictions: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
captions,
|
||||
qualities: {
|
||||
...(filteredStreams[2160] && {
|
||||
'4k': {
|
||||
type: 'mp4',
|
||||
url: filteredStreams[2160],
|
||||
},
|
||||
}),
|
||||
...(filteredStreams[1080] && {
|
||||
1080: {
|
||||
type: 'mp4',
|
||||
url: filteredStreams[1080],
|
||||
},
|
||||
}),
|
||||
...(filteredStreams[720] && {
|
||||
720: {
|
||||
type: 'mp4',
|
||||
url: filteredStreams[720],
|
||||
},
|
||||
}),
|
||||
...(filteredStreams[480] && {
|
||||
480: {
|
||||
type: 'mp4',
|
||||
url: filteredStreams[480],
|
||||
},
|
||||
}),
|
||||
...(filteredStreams[360] && {
|
||||
360: {
|
||||
type: 'mp4',
|
||||
url: filteredStreams[360],
|
||||
},
|
||||
}),
|
||||
...(filteredStreams.unknown && {
|
||||
unknown: {
|
||||
type: 'mp4',
|
||||
url: filteredStreams.unknown,
|
||||
},
|
||||
}),
|
||||
},
|
||||
type: 'file',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const [FedAPIPrivateScraper, FedDBScraper] = providers.map(embed);
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { EmbedOutput, makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { createM3U8ProxyUrl } from '@/utils/proxy';
|
||||
|
||||
export const hianimeHd1DubEmbed = makeEmbed({
|
||||
id: 'hianime-hd1-dub',
|
||||
name: 'HD-1 (Dub)',
|
||||
rank: 250,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const url = `https://hianime.pstream.org/api/v2/hianime/episode/sources?animeEpisodeId=${query.episodeId}&server=hd-1&category=dub`;
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (!data.data?.sources?.[0]?.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: createM3U8ProxyUrl(data.data.sources[0].url, data.data.headers),
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
...(thumbnailTrack
|
||||
? {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: thumbnailTrack,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const hianimeHd2DubEmbed = makeEmbed({
|
||||
id: 'hianime-hd2-dub',
|
||||
name: 'HD-2 (Dub)',
|
||||
rank: 251,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const url = `https://hianime.pstream.org/api/v2/hianime/episode/sources?animeEpisodeId=${query.episodeId}&server=hd-2&category=dub`;
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (!data.data?.sources?.[0]?.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: createM3U8ProxyUrl(data.data.sources[0].url, data.data.headers),
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
...(thumbnailTrack
|
||||
? {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: thumbnailTrack,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const hianimeHd1SubEmbed = makeEmbed({
|
||||
id: 'hianime-hd1-sub',
|
||||
name: 'HD-1 (Sub)',
|
||||
rank: 252,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const url = `https://hianime.pstream.org/api/v2/hianime/episode/sources?animeEpisodeId=${query.episodeId}&server=hd-1&category=sub`;
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (!data.data?.sources?.[0]?.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: createM3U8ProxyUrl(data.data.sources[0].url, data.data.headers),
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
...(thumbnailTrack
|
||||
? {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: thumbnailTrack,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const hianimeHd2SubEmbed = makeEmbed({
|
||||
id: 'hianime-hd2-sub',
|
||||
name: 'HD-2 (Sub)',
|
||||
rank: 253,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const url = `https://hianime.pstream.org/api/v2/hianime/episode/sources?animeEpisodeId=${query.episodeId}&server=hd-2&category=sub`;
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (!data.data?.sources?.[0]?.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: createM3U8ProxyUrl(data.data.sources[0].url, data.data.headers),
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
...(thumbnailTrack
|
||||
? {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: thumbnailTrack,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
const providers = [
|
||||
{ id: 'webtor-1080', rank: 80 }, // 1080p should be a higher rank becuase it loads faster
|
||||
{ id: 'webtor-4k', rank: 79 },
|
||||
{ id: 'webtor-720', rank: 78 },
|
||||
{ id: 'webtor-480', rank: 77 },
|
||||
];
|
||||
|
||||
function embed(provider: { id: string; rank: number }) {
|
||||
return makeEmbed({
|
||||
id: provider.id,
|
||||
name: `Webtor ${provider.id.split('-')[1].toUpperCase()}`,
|
||||
rank: provider.rank,
|
||||
async scrape(ctx) {
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: ctx.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const [webtor4kScraper, webtor1080Scraper, webtor720Scraper, webtor480Scraper] = providers.map(embed);
|
||||
|
|
@ -1,477 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { EmbedOutput, makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
const foxBaseUrl = 'https://backend.xprime.tv/fox';
|
||||
const apolloBaseUrl = 'https://kendrickl-3amar.site';
|
||||
const showboxBaseUrl = 'https://backend.xprime.tv/primebox';
|
||||
const marantBaseUrl = 'https://backend.xprime.tv/marant';
|
||||
const primenetBaseUrl = 'https://backend.xprime.tv/primenet';
|
||||
const volkswagenBaseUrl = 'https://backend.xprime.tv/volkswagen';
|
||||
const harbourBaseUrl = 'https://backend.xprime.tv/harbour';
|
||||
const fendiBaseUrl = 'https://backend.xprime.tv/fendi';
|
||||
|
||||
const languageMap: Record<string, string> = {
|
||||
'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',
|
||||
اَلْعَرَبِيَّةُ: 'ar',
|
||||
বাংলা: 'bn',
|
||||
filipino: 'tl',
|
||||
indonesia: 'id',
|
||||
اردو: 'ur',
|
||||
English: 'en',
|
||||
Arabic: 'ar',
|
||||
Bosnian: 'bs',
|
||||
Bulgarian: 'bg',
|
||||
Croatian: 'hr',
|
||||
Czech: 'cs',
|
||||
Danish: 'da',
|
||||
Dutch: 'nl',
|
||||
Estonian: 'et',
|
||||
Finnish: 'fi',
|
||||
French: 'fr',
|
||||
German: 'de',
|
||||
Greek: 'el',
|
||||
Hebrew: 'he',
|
||||
Hungarian: 'hu',
|
||||
Indonesian: 'id',
|
||||
Italian: 'it',
|
||||
Norwegian: 'no',
|
||||
Persian: 'fa',
|
||||
Polish: 'pl',
|
||||
Portuguese: 'pt',
|
||||
'Protuguese (BR)': 'pt-br',
|
||||
Romanian: 'ro',
|
||||
Russian: 'ru',
|
||||
Serbian: 'sr',
|
||||
Slovenian: 'sl',
|
||||
Spanish: 'es',
|
||||
Swedish: 'sv',
|
||||
Thai: 'th',
|
||||
Turkish: 'tr',
|
||||
};
|
||||
|
||||
export const xprimeApolloEmbed = makeEmbed({
|
||||
id: 'xprime-apollo',
|
||||
name: 'Appolo',
|
||||
rank: 237,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
let url = `${apolloBaseUrl}/${query.tmdbId}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `/${query.season}/${query.episode}`;
|
||||
}
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
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: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
...(data.thumbnails?.file
|
||||
? {
|
||||
thumbnailTrack: {
|
||||
type: 'vtt',
|
||||
url: data.thumbnails.file,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeStreamboxEmbed = makeEmbed({
|
||||
id: 'xprime-streambox',
|
||||
name: 'Streambox',
|
||||
rank: 236,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
|
||||
let url = `${showboxBaseUrl}?name=${query.title}&year=${query.releaseYear}&fallback_year=${query.releaseYear}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `&season=${query.season}&episode=${query.episode}`;
|
||||
}
|
||||
|
||||
// Old handling in case
|
||||
// if (query.type === 'show') {
|
||||
// url += `?id=${query.tmdbId}&season=${query.season}&episode=${query.episode}`;
|
||||
// } else {
|
||||
// url += `?id=${query.tmdbId}`;
|
||||
// }
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.streams) throw new NotFoundError('No streams found in response');
|
||||
|
||||
const captions =
|
||||
data.subtitles?.map((sub: { file: string; label: string }) => ({
|
||||
id: sub.label,
|
||||
url: sub.file,
|
||||
language: languageMap[sub.label.toLowerCase()] || 'unknown',
|
||||
type: 'srt',
|
||||
})) || [];
|
||||
|
||||
const qualityMap: Record<string, { type: string; url: string }> = {};
|
||||
|
||||
Object.entries(data.streams).forEach(([key, value]) => {
|
||||
const normalizedKey = key.toLowerCase().replace('p', '');
|
||||
qualityMap[normalizedKey] = {
|
||||
type: 'mp4',
|
||||
url: value as string,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
captions,
|
||||
qualities: qualityMap,
|
||||
type: 'file',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimePrimenetEmbed = makeEmbed({
|
||||
id: 'xprime-primenet',
|
||||
name: 'Primenet',
|
||||
rank: 235,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
let url = `${primenetBaseUrl}?id=${query.tmdbId}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `&season=${query.season}&episode=${query.episode}`;
|
||||
}
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimePhoenixEmbed = makeEmbed({
|
||||
id: 'xprime-phoenix',
|
||||
name: 'Phoenix',
|
||||
rank: 234,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('id', query.tmdbId);
|
||||
params.append('imdb', query.imdbId);
|
||||
|
||||
// For TV shows, add season and episode
|
||||
if (query.type === 'show') {
|
||||
params.append('season', query.season.toString());
|
||||
params.append('episode', query.episode.toString());
|
||||
}
|
||||
|
||||
const url = `https://backend.xprime.tv/phoenix?${params.toString()}`;
|
||||
ctx.progress(50);
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
// Parse and format captions
|
||||
const captions = data.subtitles
|
||||
? data.subtitles.map((sub: any) => {
|
||||
// Extract the base label without number suffixes
|
||||
const baseLabel = sub.label.split(' ')[0];
|
||||
// Use mapped ISO code or the original label if not found in the map
|
||||
const langCode = languageMap[baseLabel] || baseLabel.toLowerCase().substring(0, 2);
|
||||
|
||||
return {
|
||||
id: `${sub.label.replace(/\s+/g, '_').toLowerCase()}`,
|
||||
language: langCode,
|
||||
url: sub.file,
|
||||
label: sub.label,
|
||||
type: 'vtt',
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeFoxEmbed = makeEmbed({
|
||||
id: 'xprime-fox',
|
||||
name: 'Fox',
|
||||
rank: 233,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const params = new URLSearchParams({
|
||||
name: query.title,
|
||||
pstream: 'true',
|
||||
});
|
||||
|
||||
if (query.type === 'show') {
|
||||
params.append('season', query.season.toString());
|
||||
params.append('episode', query.episode.toString());
|
||||
}
|
||||
|
||||
const apiRes = await ctx.fetcher(`${foxBaseUrl}?${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,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeHarbourEmbed = makeEmbed({
|
||||
id: 'xprime-harbour',
|
||||
name: 'Harbour',
|
||||
rank: 232,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
const params = new URLSearchParams({
|
||||
name: query.title,
|
||||
year: query.releaseYear.toString(),
|
||||
});
|
||||
|
||||
if (query.type === 'show') {
|
||||
params.append('season', query.season.toString());
|
||||
params.append('episode', query.episode.toString());
|
||||
}
|
||||
|
||||
const apiRes = await ctx.fetcher(`${harbourBaseUrl}?${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: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeFendiEmbed = makeEmbed({
|
||||
id: 'xprime-fendi',
|
||||
name: 'Fendi',
|
||||
rank: 231,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
let url = `${fendiBaseUrl}?id=${query.tmdbId}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `&season=${query.season}&episode=${query.episode}`;
|
||||
}
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeMarantEmbed = makeEmbed({
|
||||
id: 'xprime-marant',
|
||||
name: 'Marant (French + English)',
|
||||
rank: 230,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
let url = `${marantBaseUrl}?id=${query.tmdbId}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `&season=${query.season}&episode=${query.episode}`;
|
||||
}
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.url) throw new NotFoundError('No stream URL found in response');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: data.url,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const xprimeVolkswagenEmbed = makeEmbed({
|
||||
id: 'xprime-volkswagen',
|
||||
name: 'Volkswagen (German)',
|
||||
rank: 229,
|
||||
async scrape(ctx): Promise<EmbedOutput> {
|
||||
const query = JSON.parse(ctx.url);
|
||||
let url = `${volkswagenBaseUrl}?name=${query.title}`;
|
||||
|
||||
if (query.type === 'show') {
|
||||
url += `&season=${query.season}&episode=${query.episode}`;
|
||||
} else {
|
||||
url += `&year=${query.releaseYear}`;
|
||||
}
|
||||
|
||||
const data = await ctx.fetcher(url);
|
||||
|
||||
if (!data) throw new NotFoundError('No response received');
|
||||
if (data.error) throw new NotFoundError(data.error);
|
||||
if (!data.streams) throw new NotFoundError('No streams found in response');
|
||||
|
||||
const qualityMap: Record<string, { type: string; url: string }> = {};
|
||||
|
||||
Object.entries(data.streams).forEach(([key, value]) => {
|
||||
const normalizedKey = key.toLowerCase().replace('p', '');
|
||||
qualityMap[normalizedKey] = {
|
||||
type: 'mp4',
|
||||
url: value as string,
|
||||
};
|
||||
});
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
qualities: qualityMap,
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const query = {
|
||||
type: ctx.media.type,
|
||||
title: ctx.media.title,
|
||||
tmdbId: ctx.media.tmdbId.toString(),
|
||||
...(ctx.media.type === 'show' && {
|
||||
season: ctx.media.season.number,
|
||||
episode: ctx.media.episode.number,
|
||||
}),
|
||||
};
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: 'oneserver-autoembed',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'oneserver-vidsrcsu',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'oneserver-primebox',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'oneserver-foxstream',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'oneserver-flixhq',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'oneserver-goku',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
// {
|
||||
// embedId: 'oneserver-hianime',
|
||||
// url: JSON.stringify(query),
|
||||
// },
|
||||
// {
|
||||
// embedId: 'oneserver-animepahe',
|
||||
// url: JSON.stringify(query),
|
||||
// },
|
||||
// {
|
||||
// embedId: 'oneserver-anizone',
|
||||
// url: JSON.stringify(query),
|
||||
// },
|
||||
];
|
||||
|
||||
return { embeds };
|
||||
}
|
||||
|
||||
export const oneServerScraper = makeSourcerer({
|
||||
id: '1server',
|
||||
name: '1Server 🤝',
|
||||
rank: 119,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
import { InfoResponse, SearchResponse } from './types';
|
||||
|
||||
async function consumetScraper(ctx: ShowScrapeContext): Promise<SourcererOutput> {
|
||||
// Search
|
||||
const searchQuery = ctx.media.title;
|
||||
const page = 1;
|
||||
|
||||
const searchUrl = `https://api.1anime.app/anime/zoro/${encodeURIComponent(searchQuery)}?page=${page}`;
|
||||
const searchResponse = await ctx.fetcher<SearchResponse>(searchUrl);
|
||||
|
||||
if (!searchResponse?.results?.length) {
|
||||
throw new Error('No results found');
|
||||
}
|
||||
|
||||
const bestMatch =
|
||||
searchResponse.results.find((result) => result.title.toLowerCase() === ctx.media.title.toLowerCase()) ||
|
||||
searchResponse.results[0];
|
||||
|
||||
// Get episode list
|
||||
const infoUrl = `https://api.1anime.app/anime/zoro/info?id=${bestMatch.id}`;
|
||||
const infoResponse = await ctx.fetcher<InfoResponse>(infoUrl);
|
||||
|
||||
if (!infoResponse?.episodes?.length) {
|
||||
throw new Error('No episodes found');
|
||||
}
|
||||
|
||||
const targetEpisode = infoResponse.episodes.find((ep) => ep.number === ctx.media.episode.number);
|
||||
|
||||
if (!targetEpisode) {
|
||||
throw new Error('Episode not found');
|
||||
}
|
||||
|
||||
// Parse embeds
|
||||
const query = {
|
||||
episodeId: `${bestMatch.id}$${ctx.media.season.number}$${targetEpisode.id}$both`,
|
||||
};
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: 'consumet-vidcloud',
|
||||
url: JSON.stringify({ ...query, server: 'vidcloud' }),
|
||||
},
|
||||
{
|
||||
embedId: 'consumet-streamsb',
|
||||
url: JSON.stringify({ ...query, server: 'streamsb' }),
|
||||
},
|
||||
{
|
||||
embedId: 'consumet-vidstreaming',
|
||||
url: JSON.stringify({ ...query, server: 'vidstreaming' }),
|
||||
},
|
||||
{
|
||||
embedId: 'consumet-streamtape',
|
||||
url: JSON.stringify({ ...query, server: 'streamtape' }),
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
embeds,
|
||||
};
|
||||
}
|
||||
|
||||
export const ConsumetScraper = makeSourcerer({
|
||||
id: 'consumet',
|
||||
name: 'Consumet (Anime) 🔥',
|
||||
rank: 5,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeShow: consumetScraper,
|
||||
});
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
export interface SearchResult {
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
releaseDate: string | null;
|
||||
subOrDub: 'sub' | 'dub';
|
||||
}
|
||||
|
||||
export interface SearchResponse {
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
hasNextPage: boolean;
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
export interface Episode {
|
||||
id: string;
|
||||
number: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface InfoResponse {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
image: string;
|
||||
releaseDate: string | null;
|
||||
description: string | null;
|
||||
genres: string[];
|
||||
subOrDub: 'sub' | 'dub';
|
||||
type: string | null;
|
||||
status: string;
|
||||
otherName: string | null;
|
||||
totalEpisodes: number;
|
||||
episodes: Episode[];
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
const getUserToken = (): string | null => {
|
||||
try {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const prefData = window.localStorage.getItem('__MW::preferences');
|
||||
if (!prefData) return null;
|
||||
const parsedAuth = JSON.parse(prefData);
|
||||
return parsedAuth?.state?.febboxKey || null;
|
||||
} catch (e) {
|
||||
console.warn('Unable to access localStorage or parse auth data:', e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const query = {
|
||||
type: ctx.media.type,
|
||||
imdbId: ctx.media.imdbId,
|
||||
tmdbId: ctx.media.tmdbId,
|
||||
...(ctx.media.type === 'show' && {
|
||||
season: ctx.media.season.number,
|
||||
episode: ctx.media.episode.number,
|
||||
}),
|
||||
};
|
||||
|
||||
const userToken = getUserToken();
|
||||
const embeds = [];
|
||||
|
||||
if (userToken) {
|
||||
embeds.push({
|
||||
embedId: 'fedapi-private',
|
||||
url: `${JSON.stringify({ ...query, token: userToken })}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!userToken) {
|
||||
embeds.push({
|
||||
embedId: 'feddb',
|
||||
url: `${JSON.stringify(query)}`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
embeds,
|
||||
};
|
||||
}
|
||||
|
||||
export const FedAPIScraper = makeSourcerer({
|
||||
id: 'fedapi',
|
||||
name: 'FED API (4K) 🔥',
|
||||
rank: 260,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
const apiKey = '5b9790d9305dca8713b9a0afad42ea8d'; // plz dont abuse
|
||||
const baseUrl = 'https://hianime.pstream.org/';
|
||||
|
||||
interface HianimeSearchResult {
|
||||
success: boolean;
|
||||
data: {
|
||||
animes: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
interface HianimeEpisodeResult {
|
||||
success: boolean;
|
||||
data: {
|
||||
episodes: Array<{
|
||||
number: number;
|
||||
title: string;
|
||||
episodeId: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
async function searchAnime(title: string): Promise<string> {
|
||||
const response = await fetch(`${baseUrl}api/v2/hianime/search?q=${encodeURIComponent(title)}`);
|
||||
if (!response.ok) throw new Error('Failed to search anime');
|
||||
const data: HianimeSearchResult = await response.json();
|
||||
|
||||
if (!data.success || !data.data.animes.length) {
|
||||
throw new Error('Anime not found');
|
||||
}
|
||||
|
||||
// Try to find exact match (case-insensitive)
|
||||
const match = data.data.animes.find((anime) => anime.name.toLowerCase() === title.toLowerCase());
|
||||
|
||||
// Return the matched ID or fallback to the first result
|
||||
return match?.id ?? data.data.animes[0].id;
|
||||
}
|
||||
|
||||
async function fetchTmdbShowDetails(tmdbShowId: string): Promise<string> {
|
||||
const response = await fetch(`https://api.themoviedb.org/3/tv/${tmdbShowId}?api_key=${apiKey}`);
|
||||
if (!response.ok) throw new NotFoundError('Failed to fetch show data from TMDB');
|
||||
const data = await response.json();
|
||||
|
||||
// Return the English title, falling back to the original title if not available
|
||||
return data.name || data.original_name;
|
||||
}
|
||||
|
||||
async function fetchTmdbSeasonEpisodes(tmdbShowId: string, seasonNumber: number): Promise<any[]> {
|
||||
const response = await fetch(
|
||||
`https://api.themoviedb.org/3/tv/${tmdbShowId}/season/${seasonNumber}?api_key=${apiKey}`,
|
||||
);
|
||||
if (!response.ok) throw new NotFoundError('Failed to fetch season data from TMDB');
|
||||
const data = await response.json();
|
||||
return data.episodes; // each item has 'episode_number' and 'season_number'
|
||||
}
|
||||
|
||||
async function calculateAbsoluteEpisodeNumber(
|
||||
tmdbShowId: string,
|
||||
seasonNumber: number,
|
||||
episodeNumber: number,
|
||||
): Promise<number> {
|
||||
const previousSeasons = await Promise.all(
|
||||
Array.from({ length: seasonNumber - 1 }, (_, i) => fetchTmdbSeasonEpisodes(tmdbShowId, i + 1)),
|
||||
);
|
||||
|
||||
const episodesBefore = previousSeasons.reduce((sum, seasonEpisodes) => sum + seasonEpisodes.length, 0);
|
||||
|
||||
return episodesBefore + episodeNumber;
|
||||
}
|
||||
|
||||
async function fetchEpisodeData(animeId: string): Promise<HianimeEpisodeResult> {
|
||||
const response = await fetch(`${baseUrl}api/v2/hianime/anime/${animeId}/episodes`);
|
||||
if (!response.ok) throw new NotFoundError('Failed to fetch episode data');
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext): Promise<SourcererOutput> {
|
||||
// Get the English title from TMDB first
|
||||
const englishTitle = await fetchTmdbShowDetails(ctx.media.tmdbId);
|
||||
|
||||
// Use the English title to search for the anime
|
||||
const animeId = await searchAnime(englishTitle);
|
||||
|
||||
const absoluteEp = await calculateAbsoluteEpisodeNumber(
|
||||
ctx.media.tmdbId,
|
||||
ctx.media.season.number,
|
||||
ctx.media.episode.number,
|
||||
);
|
||||
|
||||
// console.log(absoluteEp);
|
||||
|
||||
const episodeData = await fetchEpisodeData(animeId);
|
||||
// console.log(episodeData);
|
||||
const episode = episodeData.data.episodes.find((ep) => ep.number === absoluteEp);
|
||||
if (!episode) throw new NotFoundError('Episode not found');
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: 'hianime-hd1-dub',
|
||||
url: JSON.stringify({
|
||||
episodeId: episode.episodeId,
|
||||
}),
|
||||
},
|
||||
{
|
||||
embedId: 'hianime-hd2-dub',
|
||||
url: JSON.stringify({
|
||||
episodeId: episode.episodeId,
|
||||
}),
|
||||
},
|
||||
{
|
||||
embedId: 'hianime-hd1-sub',
|
||||
url: JSON.stringify({
|
||||
episodeId: episode.episodeId,
|
||||
}),
|
||||
},
|
||||
{
|
||||
embedId: 'hianime-hd2-sub',
|
||||
url: JSON.stringify({
|
||||
episodeId: episode.episodeId,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
return { embeds };
|
||||
}
|
||||
|
||||
export const hianimeScraper = makeSourcerer({
|
||||
id: 'hianime',
|
||||
name: 'Hianime 🔥',
|
||||
rank: 175,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import CryptoJS from 'crypto-js';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { convertPlaylistsToDataUrls } from '@/utils/playlist';
|
||||
|
||||
const VRF_SECRET_KEY = atob('c3VwZXJzZWNyZXRrZXk=');
|
||||
const apiBase = 'https://reyna.bludclart.com/api/source/hollymoviehd';
|
||||
|
||||
function generateVrf(tmdbId: string | number, season: string | number = '', episode: string | number = ''): string {
|
||||
const msg = `${tmdbId}:${season}:${episode}`;
|
||||
const hash = CryptoJS.HmacSHA256(msg, VRF_SECRET_KEY);
|
||||
return hash.toString(CryptoJS.enc.Hex);
|
||||
}
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
let url = `${apiBase}/${ctx.media.tmdbId}`;
|
||||
let season = '';
|
||||
let episode = '';
|
||||
if (ctx.media.type === 'show') {
|
||||
season = ctx.media.season.number.toString();
|
||||
episode = ctx.media.episode.number.toString();
|
||||
url += `/${season}/${episode}`;
|
||||
}
|
||||
const vrf = generateVrf(ctx.media.tmdbId, season, episode);
|
||||
url += `?vrf=${vrf}`;
|
||||
|
||||
const data = await ctx.proxiedFetcher(url);
|
||||
const firstUrl = data?.sources?.[0]?.file;
|
||||
if (!firstUrl) throw new NotFoundError('Sources not found.');
|
||||
ctx.progress(50);
|
||||
|
||||
ctx.progress(90);
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: await convertPlaylistsToDataUrls(ctx.proxiedFetcher, firstUrl),
|
||||
proxyDepth: 2,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const hollymoviehdScraper = makeSourcerer({
|
||||
id: 'hollymoviehd',
|
||||
name: 'BludClart: HollyMovieHD 🤝',
|
||||
rank: 180,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import CryptoJS from 'crypto-js';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { Qualities } from '@/providers/streams';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
const VRF_SECRET_KEY = atob('c3VwZXJzZWNyZXRrZXk=');
|
||||
const apiBase = 'https://reyna.bludclart.com/api/source/oneroom';
|
||||
|
||||
function generateVrf(tmdbId: string | number, season: string | number = '', episode: string | number = ''): string {
|
||||
const msg = `${tmdbId}:${season}:${episode}`;
|
||||
const hash = CryptoJS.HmacSHA256(msg, VRF_SECRET_KEY);
|
||||
return hash.toString(CryptoJS.enc.Hex);
|
||||
}
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
let url = `${apiBase}/${ctx.media.tmdbId}`;
|
||||
let season = '';
|
||||
let episode = '';
|
||||
if (ctx.media.type === 'show') {
|
||||
season = ctx.media.season.number.toString();
|
||||
episode = ctx.media.episode.number.toString();
|
||||
url += `/${season}/${episode}`;
|
||||
}
|
||||
const vrf = generateVrf(ctx.media.tmdbId, season, episode);
|
||||
url += `?vrf=${vrf}`;
|
||||
|
||||
const data = await ctx.proxiedFetcher(url);
|
||||
const sources = data?.sources;
|
||||
if (!sources || sources.length === 0) throw new NotFoundError('Sources not found.');
|
||||
ctx.progress(50);
|
||||
|
||||
// Build qualities object for mp4 sources
|
||||
const qualities: Partial<Record<Qualities, { type: 'mp4'; url: string }>> = {};
|
||||
for (const source of sources) {
|
||||
// Try to extract quality from label (e.g., '720p')
|
||||
const match = /([0-9]{3,4})p/.exec(source.label);
|
||||
const quality = match ? match[1] : 'unknown';
|
||||
qualities[quality as Qualities] = {
|
||||
type: 'mp4',
|
||||
url: source.file,
|
||||
};
|
||||
}
|
||||
|
||||
ctx.progress(90);
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
qualities,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const oneroomScraper = makeSourcerer({
|
||||
id: 'oneroom',
|
||||
name: 'BludClart: OneRoom 🤝',
|
||||
rank: 179,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// thanks uira for this api!
|
||||
const baseUrl = 'https://xj4h5qk3tf7v2mlr9s.uira.live/';
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const fetchUrl = `${baseUrl}all/${ctx.media.tmdbId}${
|
||||
ctx.media.type === 'movie' ? '' : `?s=${ctx.media.season.number}&e=${ctx.media.episode.number}`
|
||||
}`;
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await ctx.fetcher(fetchUrl);
|
||||
} catch (e: any) {
|
||||
if (e instanceof NotFoundError) throw new NotFoundError(`${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
try {
|
||||
result = await ctx.fetcher(fetchUrl);
|
||||
} catch (e: any) {
|
||||
if (e instanceof NotFoundError) throw new NotFoundError(`${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result || !result.sources || result.sources.length === 0) {
|
||||
throw new NotFoundError('No sources found');
|
||||
}
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
if (!result.sources[0].url) {
|
||||
throw new Error('Source URL is missing');
|
||||
}
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
playlist: result.sources[0].url,
|
||||
type: 'hls',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: result.captions || [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const uiraliveScraper = makeSourcerer({
|
||||
id: 'uiralive',
|
||||
name: 'uira.live 🤝',
|
||||
rank: 250,
|
||||
disabled: true,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import { Stream } from './types';
|
||||
|
||||
const trackers = [
|
||||
'udp://tracker.opentrackr.org:1337/announce',
|
||||
'udp://open.demonii.com:1337/announce',
|
||||
'udp://open.tracker.cl:1337/announce',
|
||||
'udp://open.stealth.si:80/announce',
|
||||
'udp://tracker.torrent.eu.org:451/announce',
|
||||
'udp://explodie.org:6969/announce',
|
||||
'udp://tracker.qu.ax:6969/announce',
|
||||
'udp://tracker.ololosh.space:6969/announce',
|
||||
'udp://tracker.dump.cl:6969/announce',
|
||||
'udp://tracker.dler.org:6969/announce',
|
||||
'udp://tracker.bittor.pw:1337/announce',
|
||||
'udp://tracker-udp.gbitt.info:80/announce',
|
||||
'udp://opentracker.io:6969/announce',
|
||||
'udp://open.free-tracker.ga:6969/announce',
|
||||
'udp://ns-1.x-fins.com:6969/announce',
|
||||
'udp://leet-tracker.moe:1337/announce',
|
||||
'udp://isk.richardsw.club:6969/announce',
|
||||
'udp://discord.heihachi.pw:6969/announce',
|
||||
'http://www.torrentsnipe.info:2701/announce',
|
||||
'http://www.genesis-sp.org:2710/announce',
|
||||
];
|
||||
|
||||
export function getMagnetUrl(infoHash: string, name: string): string {
|
||||
const encodedName = encodeURIComponent(name);
|
||||
const trackerParams = trackers.map((tracker) => `&tr=${encodeURIComponent(tracker)}`).join('');
|
||||
return `magnet:?xt=urn:btih:${infoHash}&dn=${encodedName}${trackerParams}`;
|
||||
}
|
||||
|
||||
export function constructProxyUrl(magnetUrl: string): string {
|
||||
const encodedMagnet = encodeURIComponent(magnetUrl);
|
||||
return `https://savingshub.online/api/fetchHls?magnet=${encodedMagnet}`; // Thanks to Custom and DebateMyRoomba for this API
|
||||
}
|
||||
|
||||
export function categorizeStreams(streams: Stream[]): Record<string, Stream[]> {
|
||||
const categories: Record<string, Stream[]> = {
|
||||
'4k': [],
|
||||
'1080p': [],
|
||||
'720p': [],
|
||||
'480p': [],
|
||||
};
|
||||
|
||||
streams.forEach((stream) => {
|
||||
const name = stream.name.toLowerCase();
|
||||
if (name.includes('4k')) {
|
||||
categories['4k'].push(stream);
|
||||
} else if (name.includes('1080p')) {
|
||||
categories['1080p'].push(stream);
|
||||
} else if (name.includes('720p')) {
|
||||
categories['720p'].push(stream);
|
||||
} else if (name.includes('480p')) {
|
||||
categories['480p'].push(stream);
|
||||
}
|
||||
});
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
export function getTopStreamsBySeeders(categoryStreams: Stream[], limit: number): Stream[] {
|
||||
return categoryStreams
|
||||
.sort((a, b) => {
|
||||
const seedersA = parseInt(a.title.match(/👤 (\d+) /)?.[1] || '0', 10);
|
||||
const seedersB = parseInt(b.title.match(/👤 (\d+) /)?.[1] || '0', 10);
|
||||
return seedersB - seedersA;
|
||||
})
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
import { categorizeStreams, constructProxyUrl, getMagnetUrl, getTopStreamsBySeeders } from './common';
|
||||
import { Response } from './types';
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const search =
|
||||
ctx.media.type === 'movie'
|
||||
? `movie/${ctx.media.imdbId}.json`
|
||||
: `series/${ctx.media.imdbId}:${ctx.media.season.number}:${ctx.media.episode.number}.json`;
|
||||
|
||||
const response: Response = await ctx
|
||||
.fetcher(
|
||||
`https://torrentio.strem.fun/providers=yts,eztv,rarbg,1337x,thepiratebay,kickasstorrents,torrentgalaxy,magnetdl,horriblesubs,nyaasi,tokyotosho,anidex/stream/${search}`,
|
||||
)
|
||||
.then((res) => (typeof res === 'string' ? JSON.parse(res) : res));
|
||||
|
||||
ctx.progress(50);
|
||||
|
||||
const categories = categorizeStreams(response.streams);
|
||||
const embeds: { embedId: string; url: string }[] = [];
|
||||
|
||||
const qualityResults = await Promise.all(
|
||||
Object.entries(categories).map(async ([category, streams]) => {
|
||||
const [topStream] = getTopStreamsBySeeders(streams, 1);
|
||||
if (!topStream) return null;
|
||||
|
||||
try {
|
||||
const magnet = getMagnetUrl(topStream.infoHash, topStream.name);
|
||||
const apiUrl = constructProxyUrl(magnet);
|
||||
|
||||
const apiResponse = await ctx.fetcher(apiUrl);
|
||||
const responseData = typeof apiResponse === 'string' ? JSON.parse(apiResponse) : apiResponse;
|
||||
if (!responseData?.m3u8Link) throw new Error('No m3u8 link in response');
|
||||
|
||||
return {
|
||||
quality: category,
|
||||
url: responseData.m3u8Link,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch ${category}:`, error);
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
qualityResults.forEach((result) => {
|
||||
if (result?.url) {
|
||||
embeds.push({
|
||||
embedId: `webtor-${result.quality.replace('p', '')}`,
|
||||
url: result.url,
|
||||
});
|
||||
}
|
||||
});
|
||||
ctx.progress(90);
|
||||
|
||||
return { embeds };
|
||||
}
|
||||
|
||||
export const webtorScraper = makeSourcerer({
|
||||
id: 'webtor',
|
||||
name: 'Webtor 🤝',
|
||||
rank: 2,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
export interface Stream {
|
||||
name: string;
|
||||
title: string;
|
||||
infoHash: string;
|
||||
fileIdx: number;
|
||||
behaviorHints: {
|
||||
bingeGroup: string;
|
||||
filename: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Response {
|
||||
streams: Stream[];
|
||||
cacheMaxAge: number;
|
||||
staleRevalidate: number;
|
||||
staleError: number;
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const query = {
|
||||
type: ctx.media.type,
|
||||
title: ctx.media.title,
|
||||
tmdbId: ctx.media.tmdbId,
|
||||
imdbId: ctx.media.imdbId,
|
||||
...(ctx.media.type === 'show' && {
|
||||
season: ctx.media.season.number,
|
||||
episode: ctx.media.episode.number,
|
||||
}),
|
||||
releaseYear: ctx.media.releaseYear,
|
||||
};
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: 'xprime-apollo',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-streambox',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-primenet',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-phoenix',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-fox',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-fendi',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-marant',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
{
|
||||
embedId: 'xprime-volkswagen',
|
||||
url: JSON.stringify(query),
|
||||
},
|
||||
];
|
||||
|
||||
return { embeds };
|
||||
}
|
||||
|
||||
export const xprimeScraper = makeSourcerer({
|
||||
id: 'xprimetv',
|
||||
name: 'xprime.tv 🤝',
|
||||
rank: 240,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
|
|
@ -1,30 +1,6 @@
|
|||
// import { alphaScraper, deltaScraper } from '@/providers/embeds/nsbx';
|
||||
// import { astraScraper, novaScraper, orionScraper } from '@/providers/embeds/whvx';
|
||||
import {
|
||||
oneServerAnimepaheEmbed,
|
||||
oneServerAnizoneEmbed,
|
||||
oneServerAutoembedEmbed,
|
||||
oneServerFlixhqEmbed,
|
||||
oneServerFoxstreamEmbed,
|
||||
oneServerGokuEmbed,
|
||||
oneServerHianimeEmbed,
|
||||
oneServerPrimeboxEmbed,
|
||||
oneServerVidsrcsuEmbed,
|
||||
} from '@/providers/embeds/1server';
|
||||
import { cinemaosHexaEmbeds } from '@/providers/embeds/cinemaos';
|
||||
import {
|
||||
ConsumetStreamSBScraper,
|
||||
ConsumetStreamTapeScraper,
|
||||
ConsumetVidCloudScraper,
|
||||
ConsumetVidStreamingScraper,
|
||||
} from '@/providers/embeds/consumet';
|
||||
import { FedAPIPrivateScraper, FedDBScraper } from '@/providers/embeds/fedapi';
|
||||
import {
|
||||
hianimeHd1DubEmbed,
|
||||
hianimeHd1SubEmbed,
|
||||
hianimeHd2DubEmbed,
|
||||
hianimeHd2SubEmbed,
|
||||
} from '@/providers/embeds/hianime';
|
||||
import {
|
||||
streamwishEnglishScraper,
|
||||
streamwishLatinoScraper,
|
||||
|
|
@ -32,20 +8,8 @@ import {
|
|||
} from '@/providers/embeds/streamwish';
|
||||
import { viperScraper } from '@/providers/embeds/viper';
|
||||
import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4';
|
||||
import {
|
||||
xprimeApolloEmbed,
|
||||
xprimeFendiEmbed,
|
||||
xprimeFoxEmbed,
|
||||
xprimeHarbourEmbed,
|
||||
xprimeMarantEmbed,
|
||||
xprimePhoenixEmbed,
|
||||
xprimePrimenetEmbed,
|
||||
xprimeStreamboxEmbed,
|
||||
xprimeVolkswagenEmbed,
|
||||
} from '@/providers/embeds/xprime';
|
||||
import { embedsuScraper } from '@/providers/sources/embedsu';
|
||||
import { soaperTvScraper } from '@/providers/sources/soapertv';
|
||||
import { uiraliveScraper } from '@/providers/sources/uiralive';
|
||||
import { wecimaScraper } from '@/providers/sources/wecima';
|
||||
import { Stream } from '@/providers/streams';
|
||||
import { IndividualEmbedRunnerOptions } from '@/runners/individualRunner';
|
||||
|
|
@ -62,36 +26,7 @@ const SKIP_VALIDATION_CHECK_IDS = [
|
|||
streamwishLatinoScraper.id,
|
||||
streamwishSpanishScraper.id,
|
||||
streamwishEnglishScraper.id,
|
||||
uiraliveScraper.id,
|
||||
embedsuScraper.id,
|
||||
FedAPIPrivateScraper.id,
|
||||
FedDBScraper.id,
|
||||
xprimeFoxEmbed.id,
|
||||
xprimeApolloEmbed.id,
|
||||
xprimeStreamboxEmbed.id,
|
||||
xprimeMarantEmbed.id,
|
||||
xprimeFendiEmbed.id,
|
||||
xprimePrimenetEmbed.id,
|
||||
xprimeVolkswagenEmbed.id,
|
||||
xprimeHarbourEmbed.id,
|
||||
xprimePhoenixEmbed.id,
|
||||
ConsumetVidCloudScraper.id,
|
||||
ConsumetStreamSBScraper.id,
|
||||
ConsumetVidStreamingScraper.id,
|
||||
ConsumetStreamTapeScraper.id,
|
||||
hianimeHd1DubEmbed.id,
|
||||
hianimeHd1SubEmbed.id,
|
||||
hianimeHd2DubEmbed.id,
|
||||
hianimeHd2SubEmbed.id,
|
||||
oneServerAutoembedEmbed.id,
|
||||
oneServerVidsrcsuEmbed.id,
|
||||
oneServerPrimeboxEmbed.id,
|
||||
oneServerFoxstreamEmbed.id,
|
||||
oneServerFlixhqEmbed.id,
|
||||
oneServerGokuEmbed.id,
|
||||
oneServerHianimeEmbed.id,
|
||||
oneServerAnimepaheEmbed.id,
|
||||
oneServerAnizoneEmbed.id,
|
||||
wecimaScraper.id,
|
||||
...cinemaosHexaEmbeds.map((e) => e.id),
|
||||
soaperTvScraper.id,
|
||||
|
|
|
|||
Loading…
Reference in a new issue