Archive old providers

This commit is contained in:
Pas 2025-03-14 20:36:49 -06:00
parent 21cb58dd33
commit 252283b09d
128 changed files with 2093 additions and 2186 deletions

View file

@ -1,41 +1,16 @@
import { Embed, Sourcerer } from '@/providers/base';
import { doodScraper } from '@/providers/embeds/dood';
import { droploadScraper } from '@/providers/embeds/dropload';
import { febboxHlsScraper } from '@/providers/embeds/febbox/hls';
import { febboxMp4Scraper } from '@/providers/embeds/febbox/mp4';
import { filelionsScraper } from '@/providers/embeds/filelions';
import { mixdropScraper } from '@/providers/embeds/mixdrop';
import { mp4uploadScraper } from '@/providers/embeds/mp4upload';
import { streambucketScraper } from '@/providers/embeds/streambucket';
import { streamsbScraper } from '@/providers/embeds/streamsb';
import { streamwishScraper } from '@/providers/embeds/streamwish';
import { turbovidScraper } from '@/providers/embeds/turbovid';
import { upcloudScraper } from '@/providers/embeds/upcloud';
import { upstreamScraper } from '@/providers/embeds/upstream';
import { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
import { vTubeScraper } from '@/providers/embeds/vtube';
import { astraScraper, novaScraper, orionScraper } from '@/providers/embeds/whvx';
import { autoembedScraper } from '@/providers/sources/autoembed';
import { bombtheirishScraper } from '@/providers/sources/bombtheirish';
import { catflixScraper } from '@/providers/sources/catflix';
import { ee3Scraper } from '@/providers/sources/ee3';
import { flixhqScraper } from '@/providers/sources/flixhq/index';
import { fsharetvScraper } from '@/providers/sources/fsharetv';
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
import { insertunitScraper } from '@/providers/sources/insertunit';
import { kissAsianScraper } from '@/providers/sources/kissasian/index';
import { lookmovieScraper } from '@/providers/sources/lookmovie';
import { mp4hydraScraper } from '@/providers/sources/mp4hydra';
import { nsbxScraper } from '@/providers/sources/nsbx';
import { redStarScraper } from '@/providers/sources/redstar';
import { remotestreamScraper } from '@/providers/sources/remotestream';
import { showboxScraper } from '@/providers/sources/showbox/index';
import { TASFScraper } from '@/providers/sources/theyallsayflix';
import { tugaflixScraper } from '@/providers/sources/tugaflix';
import { vidsrcScraper } from '@/providers/sources/vidsrc/index';
import { vidsrcsuScraper } from '@/providers/sources/vidsrcsu';
import { whvxScraper } from '@/providers/sources/whvx';
import { zoechipScraper } from '@/providers/sources/zoechip';
import {
autoembedBengaliScraper,
@ -44,21 +19,12 @@ import {
autoembedTamilScraper,
autoembedTeluguScraper,
} from './embeds/autoembed';
import { bflixScraper } from './embeds/bflix';
import { closeLoadScraper } from './embeds/closeload';
import { fileMoonScraper } from './embeds/filemoon';
import { fileMoonMp4Scraper } from './embeds/filemoon/mp4';
import { hydraxScraper } from './embeds/hydrax';
import { mp4hydraServer1Scraper, mp4hydraServer2Scraper } from './embeds/mp4hydra';
import { alphaScraper, deltaScraper } from './embeds/nsbx';
import { playm4uNMScraper } from './embeds/playm4u/nm';
import { ridooScraper } from './embeds/ridoo';
import { smashyStreamOScraper } from './embeds/smashystream/opstream';
import { smashyStreamFScraper } from './embeds/smashystream/video1';
import { streamtapeScraper } from './embeds/streamtape';
import { streamvidScraper } from './embeds/streamvid';
import { vidCloudScraper } from './embeds/vidcloud';
import { vidplayScraper } from './embeds/vidplay';
import {
VidsrcsuServer10Scraper,
VidsrcsuServer11Scraper,
@ -75,33 +41,22 @@ import {
VidsrcsuServer9Scraper,
} from './embeds/vidsrcsu';
import { viperScraper } from './embeds/viper';
import { voeScraper } from './embeds/voe';
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 { wootlyScraper } from './embeds/wootly';
import { coitusScraper } from './sources/coitus';
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';
import { iosmirrorPVScraper } from './sources/iosmirrorpv';
import { m4uScraper } from './sources/m4ufree';
import { nepuScraper } from './sources/nepu';
import { nitesScraper } from './sources/nites';
import { primewireScraper } from './sources/primewire';
import { ridooMoviesScraper } from './sources/ridomovies';
import { roverScraper } from './sources/rover';
import { slidemoviesScraper } from './sources/slidemovies';
import { smashyStreamScraper } from './sources/smashystream';
import { soaperTvScraper } from './sources/soapertv';
import { uiraliveScraper } from './sources/uiralive';
import { vidapiClickScraper } from './sources/vidapiclick';
import { vidlinkScraper } from './sources/vidlink';
import { vidSrcToScraper } from './sources/vidsrcto';
import { warezcdnScraper } from './sources/warezcdn';
import { webtorScraper } from './sources/webtor';
@ -109,40 +64,19 @@ export function gatherAllSources(): Array<Sourcerer> {
// all sources are gathered here
return [
catflixScraper,
flixhqScraper,
remotestreamScraper,
kissAsianScraper,
showboxScraper,
goMoviesScraper,
zoechipScraper,
vidsrcScraper,
lookmovieScraper,
nsbxScraper,
smashyStreamScraper,
ridooMoviesScraper,
vidSrcToScraper,
nepuScraper,
goojaraScraper,
hdRezkaScraper,
m4uScraper,
primewireScraper,
warezcdnScraper,
insertunitScraper,
nitesScraper,
soaperTvScraper,
autoembedScraper,
tugaflixScraper,
ee3Scraper,
whvxScraper,
fsharetvScraper,
redStarScraper,
bombtheirishScraper,
vidsrcsuScraper,
TASFScraper,
mp4hydraScraper,
webtorScraper,
embedsuScraper,
vidlinkScraper,
FedAPIScraper,
FedAPIDBScraper,
slidemoviesScraper,
@ -151,7 +85,6 @@ export function gatherAllSources(): Array<Sourcerer> {
uiraliveScraper,
vidapiClickScraper,
coitusScraper,
roverScraper,
];
}
@ -160,47 +93,21 @@ export function gatherAllEmbeds(): Array<Embed> {
return [
upcloudScraper,
vidCloudScraper,
mp4uploadScraper,
streamsbScraper,
upstreamScraper,
febboxMp4Scraper,
febboxHlsScraper,
mixdropScraper,
vidsrcembedScraper,
streambucketScraper,
smashyStreamFScraper,
smashyStreamOScraper,
ridooScraper,
closeLoadScraper,
fileMoonScraper,
fileMoonMp4Scraper,
deltaScraper,
alphaScraper,
vidplayScraper,
wootlyScraper,
doodScraper,
streamvidScraper,
voeScraper,
streamtapeScraper,
droploadScraper,
filelionsScraper,
vTubeScraper,
warezcdnembedHlsScraper,
warezcdnembedMp4Scraper,
warezPlayerScraper,
bflixScraper,
playm4uNMScraper,
hydraxScraper,
autoembedEnglishScraper,
autoembedHindiScraper,
autoembedBengaliScraper,
autoembedTamilScraper,
autoembedTeluguScraper,
turbovidScraper,
novaScraper,
astraScraper,
orionScraper,
streamwishScraper,
mp4hydraServer1Scraper,
mp4hydraServer2Scraper,
VidsrcsuServer1Scraper,

View file

@ -0,0 +1,52 @@
// import { unpack } from 'unpacker';
// import { flags } from '@/entrypoint/utils/targets';
// import { makeEmbed } from '../base';
// const evalCodeRegex = /eval\((.*)\)/g;
// const fileRegex = /file:"(.*?)"/g;
// const tracksRegex = /\{file:"([^"]+)",kind:"thumbnails"\}/g;
// export const droploadScraper = makeEmbed({
// id: 'dropload',
// name: 'Dropload',
// rank: 120,
// scrape: async (ctx) => {
// const mainPageRes = await ctx.proxiedFetcher.full<string>(ctx.url, {
// headers: {
// referer: ctx.url,
// },
// });
// const mainPageUrl = new URL(mainPageRes.finalUrl);
// const mainPage = mainPageRes.body;
// const evalCode = mainPage.match(evalCodeRegex);
// if (!evalCode) throw new Error('Failed to find eval code');
// const unpacked = unpack(evalCode[1]);
// const file = fileRegex.exec(unpacked);
// const thumbnailTrack = tracksRegex.exec(unpacked);
// if (!file?.[1]) throw new Error('Failed to find file');
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// playlist: file[1],
// flags: [flags.IP_LOCKED, flags.CORS_ALLOWED],
// captions: [],
// ...(thumbnailTrack
// ? {
// thumbnailTrack: {
// type: 'vtt',
// url: mainPageUrl.origin + thumbnailTrack[1],
// },
// }
// : {}),
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,24 @@
// import { MediaTypes } from '@/entrypoint/utils/media';
// export const febBoxBase = `https://www.febbox.com`;
// export interface FebboxFileList {
// file_name: string;
// ext: string;
// fid: number;
// oss_fid: number;
// is_dir: 0 | 1;
// }
// export function parseInputUrl(url: string) {
// const [type, id, seasonId, episodeId] = url.slice(1).split('/');
// const season = seasonId ? parseInt(seasonId, 10) : undefined;
// const episode = episodeId ? parseInt(episodeId, 10) : undefined;
// return {
// type: type as MediaTypes,
// id,
// season,
// episode,
// };
// }

View file

@ -0,0 +1,69 @@
// import { MediaTypes } from '@/entrypoint/utils/media';
// import { FebboxFileList, febBoxBase } from '@/providers/embeds/febbox/common';
// import { EmbedScrapeContext } from '@/utils/context';
// export async function getFileList(
// ctx: EmbedScrapeContext,
// shareKey: string,
// parentId?: number,
// ): Promise<FebboxFileList[]> {
// const query: Record<string, string> = {
// share_key: shareKey,
// pwd: '',
// };
// if (parentId) {
// query.parent_id = parentId.toString();
// query.page = '1';
// }
// const streams = await ctx.proxiedFetcher<{
// data?: {
// file_list?: FebboxFileList[];
// };
// }>('/file/file_share_list', {
// headers: {
// 'accept-language': 'en', // without this header, the request is marked as a webscraper
// },
// baseUrl: febBoxBase,
// query,
// });
// return streams.data?.file_list ?? [];
// }
// function isValidStream(file: FebboxFileList): boolean {
// return file.ext === 'mp4' || file.ext === 'mkv';
// }
// export async function getStreams(
// ctx: EmbedScrapeContext,
// shareKey: string,
// type: MediaTypes,
// season?: number,
// episode?: number,
// ): Promise<FebboxFileList[]> {
// const streams = await getFileList(ctx, shareKey);
// if (type === 'show') {
// const seasonFolder = streams.find((v) => {
// if (!v.is_dir) return false;
// return v.file_name.toLowerCase() === `season ${season}`;
// });
// if (!seasonFolder) return [];
// const episodes = await getFileList(ctx, shareKey, seasonFolder.fid);
// const s = season?.toString() ?? '0';
// const e = episode?.toString() ?? '0';
// const episodeRegex = new RegExp(`[Ss]0*${s}[Ee]0*${e}`);
// return episodes
// .filter((file) => {
// if (file.is_dir) return false;
// const match = file.file_name.match(episodeRegex);
// if (!match) return false;
// return true;
// })
// .filter(isValidStream);
// }
// return streams.filter((v) => !v.is_dir).filter(isValidStream);
// }

View file

@ -0,0 +1,50 @@
// import { MediaTypes } from '@/entrypoint/utils/media';
// import { makeEmbed } from '@/providers/base';
// import { parseInputUrl } from '@/providers/embeds/febbox/common';
// import { getStreams } from '@/providers/embeds/febbox/fileList';
// import { getSubtitles } from '@/providers/embeds/febbox/subtitles';
// import { showboxBase } from '@/providers/sources/showbox/common';
// // structure: https://www.febbox.com/share/<random_key>
// export function extractShareKey(url: string): string {
// const parsedUrl = new URL(url);
// const shareKey = parsedUrl.pathname.split('/')[2];
// return shareKey;
// }
// export const febboxHlsScraper = makeEmbed({
// id: 'febbox-hls',
// name: 'Febbox (HLS)',
// rank: 160,
// disabled: true,
// async scrape(ctx) {
// const { type, id, season, episode } = parseInputUrl(ctx.url);
// const sharelinkResult = await ctx.proxiedFetcher<{
// data?: { link?: string };
// }>('/index/share_link', {
// baseUrl: showboxBase,
// query: {
// id,
// type: type === 'movie' ? '1' : '2',
// },
// });
// if (!sharelinkResult?.data?.link) throw new Error('No embed url found');
// ctx.progress(30);
// const shareKey = extractShareKey(sharelinkResult.data.link);
// const fileList = await getStreams(ctx, shareKey, type, season, episode);
// const firstStream = fileList[0];
// if (!firstStream) throw new Error('No playable mp4 stream found');
// ctx.progress(70);
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// flags: [],
// captions: await getSubtitles(ctx, id, firstStream.fid, type as MediaTypes, season, episode),
// playlist: `https://www.febbox.com/hls/main/${firstStream.oss_fid}.m3u8`,
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,53 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { makeEmbed } from '@/providers/base';
// import { parseInputUrl } from '@/providers/embeds/febbox/common';
// import { getStreamQualities } from '@/providers/embeds/febbox/qualities';
// import { getSubtitles } from '@/providers/embeds/febbox/subtitles';
// export const febboxMp4Scraper = makeEmbed({
// id: 'febbox-mp4',
// name: 'Febbox (MP4)',
// rank: 190,
// async scrape(ctx) {
// const { type, id, season, episode } = parseInputUrl(ctx.url);
// let apiQuery: object | null = null;
// if (type === 'movie') {
// apiQuery = {
// uid: '',
// module: 'Movie_downloadurl_v3',
// mid: id,
// oss: '1',
// group: '',
// };
// } else if (type === 'show') {
// apiQuery = {
// uid: '',
// module: 'TV_downloadurl_v3',
// tid: id,
// season,
// episode,
// oss: '1',
// group: '',
// };
// }
// if (!apiQuery) throw Error('Incorrect type');
// const { qualities, fid } = await getStreamQualities(ctx, apiQuery);
// if (fid === undefined) throw new Error('No streamable file found');
// ctx.progress(70);
// return {
// stream: [
// {
// id: 'primary',
// captions: await getSubtitles(ctx, id, fid, type, episode, season),
// qualities,
// type: 'file',
// flags: [flags.CORS_ALLOWED],
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,44 @@
// import { sendRequest } from '@/providers/sources/showbox/sendRequest';
// import { StreamFile } from '@/providers/streams';
// import { ScrapeContext } from '@/utils/context';
// const allowedQualities = ['360', '480', '720', '1080', '4k'];
// interface FebboxQuality {
// path: string;
// real_quality: string;
// fid?: number;
// }
// function mapToQuality(quality: FebboxQuality): FebboxQuality | null {
// const q = quality.real_quality.replace('p', '').toLowerCase();
// if (!allowedQualities.includes(q)) return null;
// return {
// real_quality: q,
// path: quality.path,
// fid: quality.fid,
// };
// }
// export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) {
// const mediaRes: { list: FebboxQuality[] } = (await sendRequest(ctx, apiQuery)).data;
// const qualityMap = mediaRes.list.map((v) => mapToQuality(v)).filter((v): v is FebboxQuality => !!v);
// const qualities: Record<string, StreamFile> = {};
// allowedQualities.forEach((quality) => {
// const foundQuality = qualityMap.find((q) => q.real_quality === quality && q.path);
// if (foundQuality) {
// qualities[quality] = {
// type: 'mp4',
// url: foundQuality.path,
// };
// }
// });
// return {
// qualities,
// fid: mediaRes.list[0]?.fid,
// };
// }

View file

@ -0,0 +1,75 @@
// import {
// Caption,
// getCaptionTypeFromUrl,
// isValidLanguageCode,
// removeDuplicatedLanguages as removeDuplicateLanguages,
// } from '@/providers/captions';
// import { captionsDomains } from '@/providers/sources/showbox/common';
// import { sendRequest } from '@/providers/sources/showbox/sendRequest';
// import { ScrapeContext } from '@/utils/context';
// interface CaptionApiResponse {
// data: {
// list: {
// subtitles: {
// order: number;
// lang: string;
// file_path: string;
// }[];
// }[];
// };
// }
// export async function getSubtitles(
// ctx: ScrapeContext,
// id: string,
// fid: number | undefined,
// type: 'show' | 'movie',
// episodeId?: number,
// seasonId?: number,
// ): Promise<Caption[]> {
// const module = type === 'movie' ? 'Movie_srt_list_v2' : 'TV_srt_list_v2';
// const subtitleApiQuery = {
// fid,
// uid: '',
// module,
// mid: type === 'movie' ? id : undefined,
// tid: type !== 'movie' ? id : undefined,
// episode: episodeId?.toString(),
// season: seasonId?.toString(),
// };
// const subResult = (await sendRequest(ctx, subtitleApiQuery)) as CaptionApiResponse;
// const subtitleList = subResult.data.list;
// let output: Caption[] = [];
// subtitleList.forEach((sub) => {
// const subtitle = sub.subtitles.sort((a, b) => b.order - a.order)[0];
// if (!subtitle) return;
// const subtitleFilePath = subtitle.file_path
// .replace(captionsDomains[0], captionsDomains[1])
// .replace(/\s/g, '+')
// .replace(/[()]/g, (c) => {
// return `%${c.charCodeAt(0).toString(16)}`;
// });
// const subtitleType = getCaptionTypeFromUrl(subtitleFilePath);
// if (!subtitleType) return;
// const validCode = isValidLanguageCode(subtitle.lang);
// if (!validCode) return;
// output.push({
// id: subtitleFilePath,
// language: subtitle.lang,
// hasCorsRestrictions: true,
// type: subtitleType,
// url: subtitleFilePath,
// });
// });
// output = removeDuplicateLanguages(output);
// return output;
// }

View file

@ -0,0 +1,62 @@
// import { load } from 'cheerio';
// import { unpack } from 'unpacker';
// import { flags } from '@/entrypoint/utils/targets';
// import { SubtitleResult } from './types';
// import { makeEmbed } from '../../base';
// import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '../../captions';
// const evalCodeRegex = /eval\((.*)\)/g;
// const fileRegex = /file:"(.*?)"/g;
// export const fileMoonScraper = makeEmbed({
// id: 'filemoon',
// name: 'Filemoon',
// rank: 300,
// scrape: async (ctx) => {
// const embedRes = await ctx.proxiedFetcher<string>(ctx.url, {
// headers: {
// referer: ctx.url,
// },
// });
// const embedHtml = load(embedRes);
// const evalCode = embedHtml('script').text().match(evalCodeRegex);
// if (!evalCode) throw new Error('Failed to find eval code');
// const unpacked = unpack(evalCode[0]);
// const file = fileRegex.exec(unpacked);
// if (!file?.[1]) throw new Error('Failed to find file');
// const url = new URL(ctx.url);
// const subtitlesLink = url.searchParams.get('sub.info');
// const captions: Caption[] = [];
// if (subtitlesLink) {
// const captionsResult = await ctx.proxiedFetcher<SubtitleResult>(subtitlesLink);
// for (const caption of captionsResult) {
// const language = labelToLanguageCode(caption.label);
// const captionType = getCaptionTypeFromUrl(caption.file);
// if (!language || !captionType) continue;
// captions.push({
// id: caption.file,
// url: caption.file,
// type: captionType,
// language,
// hasCorsRestrictions: false,
// });
// }
// }
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// playlist: file[1],
// flags: [flags.IP_LOCKED],
// captions,
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,38 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { NotFoundError } from '@/utils/errors';
// import { makeEmbed } from '../../base';
// import { fileMoonScraper } from './index';
// export const fileMoonMp4Scraper = makeEmbed({
// id: 'filemoon-mp4',
// name: 'Filemoon MP4',
// rank: 400,
// scrape: async (ctx) => {
// const result = await fileMoonScraper.scrape(ctx);
// if (!result.stream) throw new NotFoundError('Failed to find result');
// if (result.stream[0].type !== 'hls') throw new NotFoundError('Failed to find hls stream');
// const url = result.stream[0].playlist.replace(/\/hls2\//, '/download/').replace(/\.m3u8/, '.mp4');
// return {
// stream: [
// {
// id: 'primary',
// type: 'file',
// qualities: {
// unknown: {
// type: 'mp4',
// url,
// },
// },
// flags: [flags.IP_LOCKED],
// captions: result.stream[0].captions,
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,57 @@
// import { makeFullUrl } from '@/fetchers/common';
// import { decodeData } from '@/providers/sources/vidsrcto/common';
// import { EmbedScrapeContext } from '@/utils/context';
// export const vidplayBase = 'https://vidplay.online';
// export const referer = `${vidplayBase}/`;
// // This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16
// // Full credits to @Ciarands!
// export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise<string[]> => {
// const res = await ctx.proxiedFetcher<string>('https://github.com/Ciarands/vidsrc-keys/blob/main/keys.json');
// const regex = /"rawLines":\s*\[([\s\S]*?)\]/;
// const rawLines = res.match(regex)?.[1];
// if (!rawLines) throw new Error('No keys found');
// const keys = JSON.parse(`${rawLines.substring(1).replace(/\\"/g, '"')}]`);
// return keys;
// };
// export const getEncodedId = async (ctx: EmbedScrapeContext) => {
// const url = new URL(ctx.url);
// const id = url.pathname.replace('/e/', '');
// const keyList = await getDecryptionKeys(ctx);
// const decodedId = decodeData(keyList[0], id);
// const encodedResult = decodeData(keyList[1], decodedId);
// const b64encoded = btoa(encodedResult);
// return b64encoded.replace('/', '_');
// };
// export const getFuTokenKey = async (ctx: EmbedScrapeContext) => {
// const id = await getEncodedId(ctx);
// const fuTokenRes = await ctx.proxiedFetcher<string>('/futoken', {
// baseUrl: vidplayBase,
// headers: {
// referer: ctx.url,
// },
// });
// const fuKey = fuTokenRes.match(/var\s+k\s*=\s*'([^']+)'/)?.[1];
// if (!fuKey) throw new Error('No fuKey found');
// const tokens = [];
// for (let i = 0; i < id.length; i += 1) {
// tokens.push(fuKey.charCodeAt(i % fuKey.length) + id.charCodeAt(i));
// }
// return `${fuKey},${tokens.join(',')}`;
// };
// export const getFileUrl = async (ctx: EmbedScrapeContext) => {
// const fuToken = await getFuTokenKey(ctx);
// return makeFullUrl(`/mediainfo/${fuToken}`, {
// baseUrl: vidplayBase,
// query: {
// ...Object.fromEntries(new URL(ctx.url).searchParams.entries()),
// autostart: 'true',
// },
// });
// };

View file

@ -0,0 +1,68 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { makeEmbed } from '@/providers/base';
// import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions';
// import { getFileUrl } from './common';
// import { SubtitleResult, ThumbnailTrack, VidplaySourceResponse } from './types';
// export const vidplayScraper = makeEmbed({
// id: 'vidplay',
// name: 'VidPlay',
// rank: 401,
// scrape: async (ctx) => {
// const fileUrl = await getFileUrl(ctx);
// const fileUrlRes = await ctx.proxiedFetcher<VidplaySourceResponse>(fileUrl, {
// headers: {
// referer: ctx.url,
// },
// });
// if (typeof fileUrlRes.result === 'number') throw new Error('File not found');
// const source = fileUrlRes.result.sources[0].file;
// const thumbnailSource = fileUrlRes.result.tracks.find((track) => track.kind === 'thumbnails');
// let thumbnailTrack: ThumbnailTrack | undefined;
// if (thumbnailSource) {
// thumbnailTrack = {
// type: 'vtt',
// url: thumbnailSource.file,
// };
// }
// const url = new URL(ctx.url);
// const subtitlesLink = url.searchParams.get('sub.info');
// const captions: Caption[] = [];
// if (subtitlesLink) {
// const captionsResult = await ctx.proxiedFetcher<SubtitleResult>(subtitlesLink);
// for (const caption of captionsResult) {
// const language = labelToLanguageCode(caption.label);
// const captionType = getCaptionTypeFromUrl(caption.file);
// if (!language || !captionType) continue;
// captions.push({
// id: caption.file,
// url: caption.file,
// type: captionType,
// language,
// hasCorsRestrictions: false,
// });
// }
// }
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// playlist: source,
// flags: [flags.PROXY_BLOCKED],
// headers: {
// Referer: url.origin,
// Origin: url.origin,
// },
// captions,
// thumbnailTrack,
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,69 @@
// import { makeEmbed } from '@/providers/base';
// import { vidsrcRCPBase } from '@/providers/sources/vidsrc/common';
// const hlsURLRegex = /file:"(.*?)"/;
// const setPassRegex = /var pass_path = "(.*set_pass\.php.*)";/;
// function formatHlsB64(data: string): string {
// const encodedB64 = data.replace(/\/@#@\/[^=/]+==/g, '');
// if (encodedB64.match(/\/@#@\/[^=/]+==/)) {
// return formatHlsB64(encodedB64);
// }
// return encodedB64;
// }
// export const vidsrcembedScraper = makeEmbed({
// id: 'vidsrcembed', // VidSrc is both a source and an embed host
// name: 'VidSrc',
// rank: 197,
// async scrape(ctx) {
// const html = await ctx.proxiedFetcher<string>(ctx.url, {
// headers: {
// referer: ctx.url,
// },
// });
// // When this eventually breaks see the player js @ pjs_main.js
// // If you know what youre doing and are slightly confused about how to reverse this feel free to reach out to ciaran_ds on discord with any queries
// let hlsMatch = html.match(hlsURLRegex)?.[1]?.slice(2);
// if (!hlsMatch) throw new Error('Unable to find HLS playlist');
// hlsMatch = formatHlsB64(hlsMatch);
// const finalUrl = atob(hlsMatch);
// if (!finalUrl.includes('.m3u8')) throw new Error('Unable to find HLS playlist');
// let setPassLink = html.match(setPassRegex)?.[1];
// // isn't neeeded, the stream works without it anyway
// // shouldn't fail if the setpass link is not found
// if (setPassLink) {
// if (setPassLink.startsWith('//')) {
// setPassLink = `https:${setPassLink}`;
// }
// // VidSrc uses a password endpoint to temporarily whitelist the user's IP. This is called in an interval by the player.
// // It currently has no effect on the player itself, the content plays fine without it.
// // In the future we might have to introduce hooks for the frontend to call this endpoint.
// await ctx.proxiedFetcher(setPassLink, {
// headers: {
// referer: ctx.url,
// },
// });
// }
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// playlist: finalUrl,
// headers: {
// Referer: vidsrcRCPBase,
// Origin: vidsrcRCPBase,
// },
// flags: [],
// captions: [],
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,51 @@
// import { load } from 'cheerio';
// import { unpack } from 'unpacker';
// import { flags } from '@/entrypoint/utils/targets';
// import { makeEmbed } from '../base';
// const evalCodeRegex = /eval\((.*)\)/g;
// const fileRegex = /file:"(.*?)"/g;
// const tracksRegex = /\{file:"([^"]+)",kind:"thumbnails"\}/g;
// export const vTubeScraper = makeEmbed({
// id: 'vtube',
// name: 'vTube',
// rank: 145,
// scrape: async (ctx) => {
// const mainPageRes = await ctx.proxiedFetcher.full<string>(ctx.url, {
// headers: {
// referer: ctx.url,
// },
// });
// const mainPage = mainPageRes.body;
// const html = load(mainPage);
// const evalCode = html('script').text().match(evalCodeRegex);
// if (!evalCode) throw new Error('Failed to find eval code');
// const unpacked = unpack(evalCode?.toString());
// const file = fileRegex.exec(unpacked);
// const thumbnailTrack = tracksRegex.exec(unpacked);
// if (!file?.[1]) throw new Error('Failed to find file');
// return {
// stream: [
// {
// id: 'primary',
// type: 'hls',
// playlist: file[1],
// flags: [flags.CORS_ALLOWED],
// captions: [],
// ...(thumbnailTrack
// ? {
// thumbnailTrack: {
// type: 'vtt',
// url: new URL(mainPageRes.finalUrl).origin + thumbnailTrack[1],
// },
// }
// : {}),
// },
// ],
// };
// },
// });

View file

@ -0,0 +1,70 @@
// import { EmbedOutput, makeEmbed } from '@/providers/base';
// import { baseUrl } from '@/providers/sources/whvx';
// import { NotFoundError } from '@/utils/errors';
// const providers = [
// {
// id: 'nova',
// rank: 720,
// },
// {
// id: 'astra',
// rank: 710,
// },
// {
// id: 'orion',
// rank: 700,
// },
// ];
// export const headers = {
// Origin: 'https://www.vidbinge.com',
// Referer: 'https://www.vidbinge.com',
// };
// function embed(provider: { id: string; rank: number; disabled?: boolean }) {
// return makeEmbed({
// id: provider.id,
// name: provider.id.charAt(0).toUpperCase() + provider.id.slice(1),
// rank: provider.rank,
// disabled: provider.disabled,
// async scrape(ctx) {
// let progress = 50;
// const interval = setInterval(() => {
// if (progress < 100) {
// progress += 1;
// ctx.progress(progress);
// }
// }, 100);
// try {
// const search = await ctx.fetcher.full(
// `${baseUrl}/search?query=${encodeURIComponent(ctx.url)}&provider=${provider.id}`,
// { headers },
// );
// if (search.statusCode === 429) {
// throw new Error('Rate limited');
// } else if (search.statusCode !== 200) {
// throw new NotFoundError('Failed to search');
// }
// const result = await ctx.fetcher(
// `${baseUrl}/source?resourceId=${encodeURIComponent(search.body.url)}&provider=${provider.id}`,
// { headers },
// );
// clearInterval(interval);
// ctx.progress(100);
// return result as EmbedOutput;
// } catch (error) {
// clearInterval(interval);
// ctx.progress(100);
// throw new NotFoundError('Failed to search');
// }
// },
// });
// }
// export const [novaScraper, astraScraper, orionScraper] = providers.map(embed);

BIN
src/providers/archive/sources/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,66 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { SourcererEmbed, makeSourcerer } from '@/providers/base';
// import { upcloudScraper } from '@/providers/embeds/upcloud';
// import { vidCloudScraper } from '@/providers/embeds/vidcloud';
// import { getFlixhqMovieSources, getFlixhqShowSources, getFlixhqSourceDetails } from '@/providers/sources/flixhq/scrape';
// import { getFlixhqId } from '@/providers/sources/flixhq/search';
// import { NotFoundError } from '@/utils/errors';
// export const flixhqScraper = makeSourcerer({
// id: 'flixhq',
// name: 'FlixHQ',
// rank: 229,
// flags: [flags.CORS_ALLOWED],
// disabled: true,
// async scrapeMovie(ctx) {
// const id = await getFlixhqId(ctx, ctx.media);
// if (!id) throw new NotFoundError('no search results match');
// const sources = await getFlixhqMovieSources(ctx, ctx.media, id);
// const embeds: SourcererEmbed[] = [];
// for (const source of sources) {
// if (source.embed.toLowerCase() === 'upcloud') {
// embeds.push({
// embedId: upcloudScraper.id,
// url: await getFlixhqSourceDetails(ctx, source.episodeId),
// });
// } else if (source.embed.toLowerCase() === 'vidcloud') {
// embeds.push({
// embedId: vidCloudScraper.id,
// url: await getFlixhqSourceDetails(ctx, source.episodeId),
// });
// }
// }
// return {
// embeds,
// };
// },
// async scrapeShow(ctx) {
// const id = await getFlixhqId(ctx, ctx.media);
// if (!id) throw new NotFoundError('no search results match');
// const sources = await getFlixhqShowSources(ctx, ctx.media, id);
// const embeds: SourcererEmbed[] = [];
// for (const source of sources) {
// if (source.embed.toLowerCase() === 'server upcloud') {
// embeds.push({
// embedId: upcloudScraper.id,
// url: await getFlixhqSourceDetails(ctx, source.episodeId),
// });
// } else if (source.embed.toLowerCase() === 'server vidcloud') {
// embeds.push({
// embedId: vidCloudScraper.id,
// url: await getFlixhqSourceDetails(ctx, source.episodeId),
// });
// }
// }
// return {
// embeds,
// };
// },
// });

View file

@ -0,0 +1,92 @@
// import { load } from 'cheerio';
// import { MovieMedia, ShowMedia } from '@/entrypoint/utils/media';
// import { flixHqBase } from '@/providers/sources/flixhq/common';
// import { ScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// export async function getFlixhqSourceDetails(ctx: ScrapeContext, sourceId: string): Promise<string> {
// const jsonData = await ctx.proxiedFetcher<Record<string, any>>(`/ajax/sources/${sourceId}`, {
// baseUrl: flixHqBase,
// });
// return jsonData.link;
// }
// export async function getFlixhqMovieSources(ctx: ScrapeContext, media: MovieMedia, id: string) {
// const episodeParts = id.split('-');
// const episodeId = episodeParts[episodeParts.length - 1];
// const data = await ctx.proxiedFetcher<string>(`/ajax/movie/episodes/${episodeId}`, {
// baseUrl: flixHqBase,
// });
// const doc = load(data);
// const sourceLinks = doc('.nav-item > a')
// .toArray()
// .map((el) => {
// const query = doc(el);
// const embedTitle = query.attr('title');
// const linkId = query.attr('data-linkid');
// if (!embedTitle || !linkId) throw new Error('invalid sources');
// return {
// embed: embedTitle,
// episodeId: linkId,
// };
// });
// return sourceLinks;
// }
// export async function getFlixhqShowSources(ctx: ScrapeContext, media: ShowMedia, id: string) {
// const episodeParts = id.split('-');
// const episodeId = episodeParts[episodeParts.length - 1];
// const seasonsListData = await ctx.proxiedFetcher<string>(`/ajax/season/list/${episodeId}`, {
// baseUrl: flixHqBase,
// });
// const seasonsDoc = load(seasonsListData);
// const season = seasonsDoc('.dropdown-item')
// .toArray()
// .find((el) => seasonsDoc(el).text() === `Season ${media.season.number}`)?.attribs['data-id'];
// if (!season) throw new NotFoundError('season not found');
// const seasonData = await ctx.proxiedFetcher<string>(`/ajax/season/episodes/${season}`, {
// baseUrl: flixHqBase,
// });
// const seasonDoc = load(seasonData);
// const episode = seasonDoc('.nav-item > a')
// .toArray()
// .map((el) => {
// return {
// id: seasonDoc(el).attr('data-id'),
// title: seasonDoc(el).attr('title'),
// };
// })
// .find((e) => e.title?.startsWith(`Eps ${media.episode.number}`))?.id;
// if (!episode) throw new NotFoundError('episode not found');
// const data = await ctx.proxiedFetcher<string>(`/ajax/episode/servers/${episode}`, {
// baseUrl: flixHqBase,
// });
// const doc = load(data);
// const sourceLinks = doc('.nav-item > a')
// .toArray()
// .map((el) => {
// const query = doc(el);
// const embedTitle = query.attr('title');
// const linkId = query.attr('data-id');
// if (!embedTitle || !linkId) throw new Error('invalid sources');
// return {
// embed: embedTitle,
// episodeId: linkId,
// };
// });
// return sourceLinks;
// }

View file

@ -0,0 +1,44 @@
// import { load } from 'cheerio';
// import { MovieMedia, ShowMedia } from '@/entrypoint/utils/media';
// import { flixHqBase } from '@/providers/sources/flixhq/common';
// import { compareMedia, compareTitle } from '@/utils/compare';
// import { ScrapeContext } from '@/utils/context';
// export async function getFlixhqId(ctx: ScrapeContext, media: MovieMedia | ShowMedia): Promise<string | null> {
// const searchResults = await ctx.proxiedFetcher<string>(`/search/${media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
// baseUrl: flixHqBase,
// });
// const doc = load(searchResults);
// const items = doc('.film_list-wrap > div.flw-item')
// .toArray()
// .map((el) => {
// const query = doc(el);
// const id = query.find('div.film-poster > a').attr('href')?.slice(1);
// const title = query.find('div.film-detail > h2 > a').attr('title');
// const year = query.find('div.film-detail > div.fd-infor > span:nth-child(1)').text();
// const seasons = year.includes('SS') ? year.split('SS')[1] : '0';
// if (!id || !title || !year) return null;
// return {
// id,
// title,
// year: parseInt(year, 10),
// seasons: parseInt(seasons, 10),
// };
// });
// const matchingItem = items.find((v) => {
// if (!v) return false;
// if (media.type === 'movie') {
// return compareMedia(media, v.title, v.year);
// }
// return compareTitle(media.title, v.title) && media.season.number < v.seasons + 1;
// });
// if (!matchingItem) return null;
// return matchingItem.id;
// }

View file

@ -0,0 +1,225 @@
// import { load } from 'cheerio';
// import { flags } from '@/entrypoint/utils/targets';
// import { makeSourcerer } from '@/providers/base';
// import { doodScraper } from '@/providers/embeds/dood';
// import { mixdropScraper } from '@/providers/embeds/mixdrop';
// import { upcloudScraper } from '@/providers/embeds/upcloud';
// import { upstreamScraper } from '@/providers/embeds/upstream';
// import { vidCloudScraper } from '@/providers/embeds/vidcloud';
// import { voeScraper } from '@/providers/embeds/voe';
// import { NotFoundError } from '@/utils/errors';
// import { getSource } from './source';
// export const gomoviesBase = `https://gomovies.sx`;
// export const goMoviesScraper = makeSourcerer({
// id: 'gomovies',
// name: 'GOmovies',
// rank: 50,
// disabled: true,
// flags: [flags.CORS_ALLOWED],
// async scrapeShow(ctx) {
// const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
// method: 'GET',
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// const searchPage = load(search);
// const mediaElements = searchPage('div.film-detail');
// const mediaData = mediaElements.toArray().map((movieEl) => {
// const name = searchPage(movieEl).find('h2.film-name a')?.text();
// const year = searchPage(movieEl).find('span.fdi-item:first')?.text();
// const path = searchPage(movieEl).find('h2.film-name a').attr('href');
// return { name, year, path };
// });
// const targetMedia = mediaData.find((m) => m.name === ctx.media.title);
// if (!targetMedia?.path) throw new NotFoundError('Media not found');
// // Example movie path: /movie/watch-{slug}-{id}
// // Example series path: /tv/watch-{slug}-{id}
// let mediaId = targetMedia.path.split('-').pop()?.replace('/', '');
// const seasons = await ctx.proxiedFetcher<string>(`/ajax/v2/tv/seasons/${mediaId}`, {
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// const seasonsEl = load(seasons)('.ss-item');
// const seasonsData = seasonsEl.toArray().map((season) => ({
// number: load(season).text().replace('Season ', ''),
// dataId: season.attribs['data-id'],
// }));
// const seasonNumber = ctx.media.season.number;
// const targetSeason = seasonsData.find((season) => +season.number === seasonNumber);
// if (!targetSeason) throw new NotFoundError('Season not found');
// const episodes = await ctx.proxiedFetcher<string>(`/ajax/v2/season/episodes/${targetSeason.dataId}`, {
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// const episodesPage = load(episodes);
// const episodesEl = episodesPage('.eps-item');
// const episodesData = episodesEl.toArray().map((ep) => ({
// dataId: ep.attribs['data-id'],
// number: episodesPage(ep).find('strong').text().replace('Eps', '').replace(':', '').trim(),
// }));
// const episodeNumber = ctx.media.episode.number;
// const targetEpisode = episodesData.find((ep) => (ep.number ? +ep.number === episodeNumber : false));
// if (!targetEpisode?.dataId) throw new NotFoundError('Episode not found');
// mediaId = targetEpisode.dataId;
// const sources = await ctx.proxiedFetcher<string>(`ajax/v2/episode/servers/${mediaId}`, {
// baseUrl: gomoviesBase,
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// });
// const upcloudSource = await getSource(ctx, sources, 'upcloud');
// const vidcloudSource = await getSource(ctx, sources, 'vidcloud');
// const voeSource = await getSource(ctx, sources, 'voe');
// const doodSource = await getSource(ctx, sources, 'doodstream');
// const upstreamSource = await getSource(ctx, sources, 'upstream');
// const mixdropSource = await getSource(ctx, sources, 'mixdrop');
// const embeds = [
// {
// embedId: upcloudScraper.id,
// url: upcloudSource?.link,
// },
// {
// embedId: vidCloudScraper.id,
// url: vidcloudSource?.link,
// },
// {
// embedId: voeScraper.id,
// url: voeSource?.link,
// },
// {
// embedId: doodScraper.id,
// url: doodSource?.link,
// },
// {
// embedId: upstreamScraper.id,
// url: upstreamSource?.link,
// },
// {
// embedId: mixdropScraper.id,
// url: mixdropSource?.link,
// },
// ];
// const filteredEmbeds = embeds
// .filter((embed) => embed.url)
// .map((embed) => ({
// embedId: embed.embedId,
// url: embed.url as string,
// }));
// if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.');
// return {
// embeds: filteredEmbeds,
// };
// },
// async scrapeMovie(ctx) {
// const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
// method: 'GET',
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// const searchPage = load(search);
// const mediaElements = searchPage('div.film-detail');
// const mediaData = mediaElements.toArray().map((movieEl) => {
// const name = searchPage(movieEl).find('h2.film-name a')?.text();
// const year = searchPage(movieEl).find('span.fdi-item:first')?.text();
// const path = searchPage(movieEl).find('h2.film-name a').attr('href');
// return { name, year, path };
// });
// const targetMedia = mediaData.find(
// (m) => m.name === ctx.media.title && m.year === ctx.media.releaseYear.toString(),
// );
// if (!targetMedia?.path) throw new NotFoundError('Media not found');
// // Example movie path: /movie/watch-{slug}-{id}
// // Example series path: /tv/watch-{slug}-{id}
// const mediaId = targetMedia.path.split('-').pop()?.replace('/', '');
// const sources = await ctx.proxiedFetcher<string>(`ajax/movie/episodes/${mediaId}`, {
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// const upcloudSource = await getSource(ctx, sources, 'upcloud');
// const vidcloudSource = await getSource(ctx, sources, 'vidcloud');
// const voeSource = await getSource(ctx, sources, 'voe');
// const doodSource = await getSource(ctx, sources, 'doodstream');
// const upstreamSource = await getSource(ctx, sources, 'upstream');
// const mixdropSource = await getSource(ctx, sources, 'mixdrop');
// const embeds = [
// {
// embedId: upcloudScraper.id,
// url: upcloudSource?.link,
// },
// {
// embedId: vidCloudScraper.id,
// url: vidcloudSource?.link,
// },
// {
// embedId: voeScraper.id,
// url: voeSource?.link,
// },
// {
// embedId: doodScraper.id,
// url: doodSource?.link,
// },
// {
// embedId: upstreamScraper.id,
// url: upstreamSource?.link,
// },
// {
// embedId: mixdropScraper.id,
// url: mixdropSource?.link,
// },
// ];
// const filteredEmbeds = embeds
// .filter((embed) => embed.url)
// .map((embed) => ({
// embedId: embed.embedId,
// url: embed.url as string,
// }));
// if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.');
// return {
// embeds: filteredEmbeds,
// };
// },
// });

View file

@ -0,0 +1,30 @@
// import { load } from 'cheerio';
// import { ScrapeContext } from '@/utils/context';
// import { gomoviesBase } from '.';
// export async function getSource(ctx: ScrapeContext, sources: any, title: string) {
// const source = load(sources)(`a[title*=${title} i]`);
// const sourceDataId = source?.attr('data-id') ?? source?.attr('data-linkid');
// if (!sourceDataId) return undefined;
// const sourceData = await ctx.proxiedFetcher<{
// type: 'iframe' | string;
// link: string;
// sources: [];
// title: string;
// tracks: [];
// }>(`/ajax/sources/${sourceDataId}`, {
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// baseUrl: gomoviesBase,
// });
// if (!sourceData.link || sourceData.type !== 'iframe') return undefined;
// return sourceData;
// }

View file

@ -0,0 +1,15 @@
// import { mp4uploadScraper } from '@/providers/embeds/mp4upload';
// import { streamsbScraper } from '@/providers/embeds/streamsb';
// export const kissasianBase = 'https://kissasian.sh';
// export const embedProviders = [
// {
// type: mp4uploadScraper.id,
// id: 'mp',
// },
// {
// type: streamsbScraper.id,
// id: 'sb',
// },
// ];

View file

@ -0,0 +1,55 @@
// import { load } from 'cheerio';
// import type { ScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// import { embedProviders, kissasianBase } from './common';
// export async function getEmbeds(
// ctx: ScrapeContext,
// targetEpisode: {
// number: string;
// url?: string;
// },
// ) {
// let embeds = await Promise.all(
// embedProviders.map(async (provider) => {
// if (!targetEpisode.url) throw new NotFoundError('Episode not found');
// const watch = await ctx.proxiedFetcher<string>(`${targetEpisode.url}&s=${provider.id}`, {
// baseUrl: kissasianBase,
// headers: {
// accept:
// 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
// 'accept-language': 'en-US,en;q=0.9',
// 'cache-control': 'no-cache',
// pragma: 'no-cache',
// 'sec-ch-ua': '"Not)A;Brand";v="24", "Chromium";v="116"',
// 'sec-ch-ua-mobile': '?0',
// 'sec-ch-ua-platform': '"macOS"',
// 'sec-fetch-dest': 'document',
// 'sec-fetch-mode': 'navigate',
// 'sec-fetch-site': 'cross-site',
// 'sec-fetch-user': '?1',
// 'upgrade-insecure-requests': '1',
// cookie:
// '__rd=; ASP.NET_SessionId=jwnl2kmlw5h4mfdaxvpk30q0; k_token=OKbJDFNx3rUtaw7iAA6UxMKSJb79lgZ2X2rVC9aupJhycYQKVSLaW1y2B4K%2f%2fo3i6BuzhXgfkJGmKlKH6LpNlKPPpZUk31n9DapfMdJgjlLExgrPS3jpSKwGnNUI%2bOpNpZu9%2fFnkLZRxvVKCa8APMxrck1tYkKXWqfyJJh8%2b7hQTI1wfAOU%2fLEouHhtQGL%2fReTzElw2LQ0XSL1pjs%2fkWW3rM3of2je7Oo13I%2f7olLFuiJUVWyNbn%2fYKSgNrm%2bQ3p',
// },
// });
// const watchPage = load(watch);
// const embedUrl = watchPage('#my_video_1').attr('src');
// if (!embedUrl) throw new Error('Embed not found');
// return {
// embedId: provider.id,
// url: embedUrl,
// };
// }),
// );
// embeds = embeds.filter((e) => !!e.url);
// return embeds;
// }

View file

@ -0,0 +1,76 @@
// import { load } from 'cheerio';
// import { flags } from '@/entrypoint/utils/targets';
// import { makeSourcerer } from '@/providers/base';
// import { NotFoundError } from '@/utils/errors';
// import { kissasianBase } from './common';
// import { getEmbeds } from './getEmbeds';
// import { getEpisodes } from './getEpisodes';
// import { search } from './search';
// export const kissAsianScraper = makeSourcerer({
// id: 'kissasian',
// name: 'KissAsian',
// rank: 40,
// flags: [flags.CORS_ALLOWED],
// disabled: true,
// async scrapeShow(ctx) {
// const seasonNumber = ctx.media.season.number;
// const episodeNumber = ctx.media.episode.number;
// const dramas = await search(ctx, ctx.media.title, seasonNumber);
// const targetDrama = dramas.find((d) => d.name?.toLowerCase() === ctx.media.title.toLowerCase()) ?? dramas[0];
// if (!targetDrama) throw new NotFoundError('Drama not found');
// ctx.progress(30);
// const drama = await ctx.proxiedFetcher<string>(targetDrama.url, {
// baseUrl: kissasianBase,
// });
// const dramaPage = load(drama);
// const episodes = await getEpisodes(dramaPage);
// const targetEpisode = episodes.find((e) => e.number === `${episodeNumber}`);
// if (!targetEpisode?.url) throw new NotFoundError('Episode not found');
// ctx.progress(70);
// const embeds = await getEmbeds(ctx, targetEpisode);
// return {
// embeds,
// };
// },
// async scrapeMovie(ctx) {
// const dramas = await search(ctx, ctx.media.title, undefined);
// const targetDrama = dramas.find((d) => d.name?.toLowerCase() === ctx.media.title.toLowerCase()) ?? dramas[0];
// if (!targetDrama) throw new NotFoundError('Drama not found');
// ctx.progress(30);
// const drama = await ctx.proxiedFetcher<string>(targetDrama.url, {
// baseUrl: kissasianBase,
// });
// const dramaPage = load(drama);
// const episodes = getEpisodes(dramaPage);
// const targetEpisode = episodes[0];
// if (!targetEpisode?.url) throw new NotFoundError('Episode not found');
// ctx.progress(70);
// const embeds = await getEmbeds(ctx, targetEpisode);
// return {
// embeds,
// };
// },
// });

View file

@ -0,0 +1,27 @@
// import { load } from 'cheerio';
// import FormData from 'form-data';
// import { ScrapeContext } from '@/utils/context';
// import { kissasianBase } from './common';
// export async function search(ctx: ScrapeContext, title: string, seasonNumber?: number) {
// const searchForm = new FormData();
// searchForm.append('keyword', `${title} ${seasonNumber ?? ''}`.trim());
// searchForm.append('type', 'Drama');
// const searchResults = await ctx.proxiedFetcher<string>('/Search/SearchSuggest', {
// baseUrl: kissasianBase,
// method: 'POST',
// body: searchForm,
// });
// const searchPage = load(searchResults);
// return Array.from(searchPage('a')).map((drama) => {
// return {
// name: searchPage(drama).text(),
// url: drama.attribs.href,
// };
// });
// }

View file

@ -0,0 +1,49 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { SourcererOutput, makeSourcerer } from '@/providers/base';
// import { febboxMp4Scraper } from '@/providers/embeds/febbox/mp4';
// import { compareTitle } from '@/utils/compare';
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// import { sendRequest } from './sendRequest';
// async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
// const searchQuery = {
// module: 'Search4',
// page: '1',
// type: 'all',
// keyword: ctx.media.title,
// pagelimit: '20',
// };
// const searchRes = (await sendRequest(ctx, searchQuery, true)).data.list;
// ctx.progress(50);
// const showboxEntry = searchRes.find(
// (res: any) => compareTitle(res.title, ctx.media.title) && res.year === Number(ctx.media.releaseYear),
// );
// if (!showboxEntry) throw new NotFoundError('No entry found');
// const id = showboxEntry.id;
// const season = ctx.media.type === 'show' ? ctx.media.season.number : '';
// const episode = ctx.media.type === 'show' ? ctx.media.episode.number : '';
// return {
// embeds: [
// {
// embedId: febboxMp4Scraper.id,
// url: `/${ctx.media.type}/${id}/${season}/${episode}`,
// },
// ],
// };
// }
// export const showboxScraper = makeSourcerer({
// id: 'showbox',
// name: 'Showbox',
// rank: 250,
// disabled: true,
// flags: [flags.CORS_ALLOWED, flags.CF_BLOCKED],
// scrapeShow: comboScraper,
// scrapeMovie: comboScraper,
// });

View file

@ -0,0 +1,38 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { SourcererOutput, makeSourcerer } from '@/providers/base';
// import { smashyStreamOScraper } from '@/providers/embeds/smashystream/opstream';
// import { smashyStreamFScraper } from '@/providers/embeds/smashystream/video1';
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
// const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> => {
// // theres no point in fetching the player page
// // because it too just calls the api with the tmdb id
// // thats the only way to find out if the embed has any streams
// const query =
// ctx.media.type === 'movie'
// ? `?tmdb=${ctx.media.tmdbId}`
// : `?tmdb=${ctx.media.tmdbId}&season=${ctx.media.season.number}&episode=${ctx.media.episode.number}`;
// return {
// embeds: [
// {
// embedId: smashyStreamFScraper.id,
// url: `https://embed.smashystream.com/videofeee.php${query}`,
// },
// {
// embedId: smashyStreamOScraper.id,
// url: `https://embed.smashystream.com/shortmoviec.php${query}`,
// },
// ],
// };
// };
// export const smashyStreamScraper = makeSourcerer({
// id: 'smashystream',
// name: 'SmashyStream',
// rank: 20,
// disabled: true,
// flags: [flags.CORS_ALLOWED],
// scrapeMovie: universalScraper,
// scrapeShow: universalScraper,
// });

View file

@ -0,0 +1,65 @@
// /* eslint-disable no-console */
// import { flags } from '@/entrypoint/utils/targets';
// import { Caption, labelToLanguageCode } from '@/providers/captions';
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// import { SourcererOutput, makeSourcerer } from '../base';
// async function comboScraper(ctx: MovieScrapeContext | ShowScrapeContext): Promise<SourcererOutput> {
// const embedUrl =
// ctx.media.type === 'movie'
// ? `https://vidlink.pro/movie/${ctx.media.tmdbId}`
// : `https://vidlink.pro/tv/${ctx.media.tmdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`;
// const apiResponse = await ctx.proxiedFetcher<{
// results: Array<{
// video_urls: string[];
// subtitles: Array<{ label: string; file: string }>;
// }>;
// }>('https://psvl.api.pstream.org/api/video-url', {
// query: { embedUrl },
// });
// if (!apiResponse?.results?.length) throw new NotFoundError('No results found');
// const primaryResult = apiResponse.results[0];
// if (!primaryResult.video_urls?.length) throw new NotFoundError('No video URLs found');
// const captions: Caption[] = [];
// for (const sub of primaryResult.subtitles) {
// const language = labelToLanguageCode(sub.label.split('-')[0].trim());
// if (!language) continue;
// captions.push({
// id: sub.file,
// url: sub.file,
// type: 'vtt',
// hasCorsRestrictions: false,
// language,
// });
// }
// return {
// embeds: [],
// stream: [
// {
// id: 'primary',
// playlist: primaryResult.video_urls[0],
// type: 'hls',
// flags: [flags.CORS_ALLOWED],
// captions,
// },
// ],
// };
// }
// export const vidlinkScraper = makeSourcerer({
// id: 'vidlink',
// name: 'PSVL',
// rank: 113,
// disabled: true,
// flags: [flags.CORS_ALLOWED],
// scrapeMovie: comboScraper,
// scrapeShow: comboScraper,
// });

View file

@ -0,0 +1,13 @@
// import { makeSourcerer } from '@/providers/base';
// import { scrapeMovie } from '@/providers/sources/vidsrc/scrape-movie';
// import { scrapeShow } from '@/providers/sources/vidsrc/scrape-show';
// export const vidsrcScraper = makeSourcerer({
// id: 'vidsrc',
// name: 'VidSrc',
// rank: 116,
// disabled: true,
// flags: [],
// scrapeMovie,
// scrapeShow,
// });

View file

@ -0,0 +1,8 @@
// import { getVidSrcMovieSources } from '@/providers/sources/vidsrc/scrape';
// import { MovieScrapeContext } from '@/utils/context';
// export async function scrapeMovie(ctx: MovieScrapeContext) {
// return {
// embeds: await getVidSrcMovieSources(ctx),
// };
// }

View file

@ -0,0 +1,8 @@
// import { getVidSrcShowSources } from '@/providers/sources/vidsrc/scrape';
// import { ShowScrapeContext } from '@/utils/context';
// export async function scrapeShow(ctx: ShowScrapeContext) {
// return {
// embeds: await getVidSrcShowSources(ctx),
// };
// }

View file

@ -0,0 +1,133 @@
// import { load } from 'cheerio';
// import { SourcererEmbed } from '@/providers/base';
// import { streambucketScraper } from '@/providers/embeds/streambucket';
// import { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
// import { vidsrcBase, vidsrcRCPBase } from '@/providers/sources/vidsrc/common';
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
// function decodeSrc(encoded: string, seed: string) {
// let decoded = '';
// const seedLength = seed.length;
// for (let i = 0; i < encoded.length; i += 2) {
// const byte = parseInt(encoded.substr(i, 2), 16);
// const seedChar = seed.charCodeAt((i / 2) % seedLength);
// decoded += String.fromCharCode(byte ^ seedChar);
// }
// return decoded;
// }
// async function getVidSrcEmbeds(ctx: MovieScrapeContext | ShowScrapeContext, startingURL: string) {
// // VidSrc works by using hashes and a redirect system.
// // The hashes are stored in the html, and VidSrc will
// // make requests to their servers with the hash. This
// // will trigger a 302 response with a Location header
// // sending the user to the correct embed. To get the
// // real embed links, we must do the same. Slow, but
// // required
// const embeds: SourcererEmbed[] = [];
// let html = await ctx.proxiedFetcher<string>(startingURL, {
// baseUrl: vidsrcBase,
// });
// let $ = load(html);
// const sourceHashes = $('.server[data-hash]')
// .toArray()
// .map((el) => $(el).attr('data-hash'))
// .filter((hash) => hash !== undefined);
// for (const hash of sourceHashes) {
// html = await ctx.proxiedFetcher<string>(`/rcp/${hash}`, {
// baseUrl: vidsrcRCPBase,
// headers: {
// referer: vidsrcBase,
// },
// });
// $ = load(html);
// const encoded = $('#hidden').attr('data-h');
// const seed = $('body').attr('data-i');
// if (!encoded || !seed) {
// throw new Error('Failed to find encoded iframe src');
// }
// let redirectURL = decodeSrc(encoded, seed);
// if (redirectURL.startsWith('//')) {
// redirectURL = `https:${redirectURL}`;
// }
// const { finalUrl } = await ctx.proxiedFetcher.full(redirectURL, {
// method: 'HEAD',
// headers: {
// referer: vidsrcBase,
// },
// });
// const embed: SourcererEmbed = {
// embedId: '',
// url: finalUrl,
// };
// const parsedUrl = new URL(finalUrl);
// switch (parsedUrl.host) {
// case 'vidsrc.stream':
// embed.embedId = vidsrcembedScraper.id;
// break;
// case 'streambucket.net':
// embed.embedId = streambucketScraper.id;
// break;
// case '2embed.cc':
// case 'www.2embed.cc':
// // Just ignore this. This embed just sources from other embeds we can scrape as a 'source'
// break;
// case 'player-cdn.com':
// // Just ignore this. This embed streams video over a custom WebSocket connection
// break;
// default:
// throw new Error(`Failed to find VidSrc embed source for ${finalUrl}`);
// }
// // Since some embeds are ignored on purpose, check if a valid one was found
// if (embed.embedId !== '') {
// embeds.push(embed);
// }
// }
// return embeds;
// }
// export async function getVidSrcMovieSources(ctx: MovieScrapeContext) {
// return getVidSrcEmbeds(ctx, `/embed/${ctx.media.tmdbId}`);
// }
// export async function getVidSrcShowSources(ctx: ShowScrapeContext) {
// // VidSrc will always default to season 1 episode 1
// // no matter what embed URL is used. It sends back
// // a list of ALL the shows episodes, in order, for
// // all seasons. To get the real embed URL, have to
// // parse this from the response
// const html = await ctx.proxiedFetcher<string>(`/embed/${ctx.media.tmdbId}`, {
// baseUrl: vidsrcBase,
// });
// const $ = load(html);
// const episodeElement = $(`.ep[data-s="${ctx.media.season.number}"][data-e="${ctx.media.episode.number}"]`).first();
// if (episodeElement.length === 0) {
// throw new Error('failed to find episode element');
// }
// const startingURL = episodeElement.attr('data-iframe');
// if (!startingURL) {
// throw new Error('failed to find episode starting URL');
// }
// return getVidSrcEmbeds(ctx, startingURL);
// }

View file

@ -0,0 +1,71 @@
// import { mixdropScraper } from '@/providers/embeds/mixdrop';
// import { upcloudScraper } from '@/providers/embeds/upcloud';
// import { upstreamScraper } from '@/providers/embeds/upstream';
// import { vidCloudScraper } from '@/providers/embeds/vidcloud';
// import { getZoeChipSourceURL, getZoeChipSources } from '@/providers/sources/zoechip/scrape';
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
// export const zoeBase = 'https://zoechip.cc';
// export type ZoeChipSourceDetails = {
// type: string; // Only seen "iframe" so far
// link: string;
// sources: string[]; // Never seen this populated, assuming it's a string array
// tracks: string[]; // Never seen this populated, assuming it's a string array
// title: string;
// };
// export async function formatSource(
// ctx: MovieScrapeContext | ShowScrapeContext,
// source: { embed: string; episodeId: string },
// ) {
// const link = await getZoeChipSourceURL(ctx, source.episodeId);
// if (link) {
// const embed = {
// embedId: '',
// url: link,
// };
// const parsedUrl = new URL(link);
// switch (parsedUrl.host) {
// case 'rabbitstream.net':
// embed.embedId = upcloudScraper.id;
// break;
// case 'upstream.to':
// embed.embedId = upstreamScraper.id;
// break;
// case 'mixdrop.co':
// embed.embedId = mixdropScraper.id;
// break;
// default:
// return null;
// }
// return embed;
// }
// }
// export async function createZoeChipStreamData(ctx: MovieScrapeContext | ShowScrapeContext, id: string) {
// const sources = await getZoeChipSources(ctx, id);
// const embeds: {
// embedId: string;
// url: string;
// }[] = [];
// for (const source of sources) {
// const formatted = await formatSource(ctx, source);
// if (formatted) {
// // Zoechip does not return titles for their sources, so we can not check if a source is upcloud or vidcloud because the domain is the same.
// const upCloudAlreadyExists = embeds.find((e) => e.embedId === upcloudScraper.id);
// if (formatted.embedId === upcloudScraper.id && upCloudAlreadyExists) {
// formatted.embedId = vidCloudScraper.id;
// }
// embeds.push(formatted);
// }
// }
// return {
// embeds,
// };
// }

View file

@ -0,0 +1,14 @@
// import { flags } from '@/entrypoint/utils/targets';
// import { makeSourcerer } from '@/providers/base';
// import { scrapeMovie } from '@/providers/sources/zoechip/scrape-movie';
// import { scrapeShow } from '@/providers/sources/zoechip/scrape-show';
// export const zoechipScraper = makeSourcerer({
// id: 'zoechip',
// name: 'ZoeChip',
// rank: 240,
// flags: [flags.CORS_ALLOWED],
// disabled: true,
// scrapeMovie,
// scrapeShow,
// });

View file

@ -0,0 +1,13 @@
// import { createZoeChipStreamData } from '@/providers/sources/zoechip/common';
// import { getZoeChipMovieID } from '@/providers/sources/zoechip/search';
// import { MovieScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// export async function scrapeMovie(ctx: MovieScrapeContext) {
// const movieID = await getZoeChipMovieID(ctx, ctx.media);
// if (!movieID) {
// throw new NotFoundError('no search results match');
// }
// return createZoeChipStreamData(ctx, movieID);
// }

View file

@ -0,0 +1,24 @@
// import { createZoeChipStreamData } from '@/providers/sources/zoechip/common';
// import { getZoeChipEpisodeID, getZoeChipSeasonID } from '@/providers/sources/zoechip/scrape';
// import { getZoeChipShowID } from '@/providers/sources/zoechip/search';
// import { ShowScrapeContext } from '@/utils/context';
// import { NotFoundError } from '@/utils/errors';
// export async function scrapeShow(ctx: ShowScrapeContext) {
// const showID = await getZoeChipShowID(ctx, ctx.media);
// if (!showID) {
// throw new NotFoundError('no search results match');
// }
// const seasonID = await getZoeChipSeasonID(ctx, ctx.media, showID);
// if (!seasonID) {
// throw new NotFoundError('no season found');
// }
// const episodeID = await getZoeChipEpisodeID(ctx, ctx.media, seasonID);
// if (!episodeID) {
// throw new NotFoundError('no episode found');
// }
// return createZoeChipStreamData(ctx, episodeID);
// }

View file

@ -0,0 +1,126 @@
// import { load } from 'cheerio';
// import { ShowMedia } from '@/entrypoint/utils/media';
// import { ZoeChipSourceDetails, zoeBase } from '@/providers/sources/zoechip/common';
// import { MovieScrapeContext, ScrapeContext, ShowScrapeContext } from '@/utils/context';
// export async function getZoeChipSources(ctx: MovieScrapeContext | ShowScrapeContext, id: string) {
// // Movies use /ajax/episode/list/ID
// // Shows use /ajax/episode/servers/ID
// const endpoint = ctx.media.type === 'movie' ? 'list' : 'servers';
// const html = await ctx.proxiedFetcher<string>(`/ajax/episode/${endpoint}/${id}`, {
// baseUrl: zoeBase,
// });
// const $ = load(html);
// return $('.nav-item a')
// .toArray()
// .map((el) => {
// // Movies use data-linkid
// // Shows use data-id
// const idAttribute = ctx.media.type === 'movie' ? 'data-linkid' : 'data-id';
// const element = $(el);
// const embedTitle = element.attr('title');
// const linkId = element.attr(idAttribute);
// if (!embedTitle || !linkId) {
// throw new Error('invalid sources');
// }
// return {
// embed: embedTitle,
// episodeId: linkId,
// };
// });
// }
// export async function getZoeChipSourceURL(ctx: ScrapeContext, sourceID: string): Promise<string | null> {
// const details = await ctx.proxiedFetcher<ZoeChipSourceDetails>(`/ajax/sources/${sourceID}`, {
// baseUrl: zoeBase,
// });
// // TODO - Support non-iframe sources
// if (details.type !== 'iframe') {
// return null;
// }
// // TODO - Extract the other data from the source
// return details.link;
// }
// export async function getZoeChipSeasonID(ctx: ScrapeContext, media: ShowMedia, showID: string): Promise<string | null> {
// const html = await ctx.proxiedFetcher<string>(`/ajax/season/list/${showID}`, {
// baseUrl: zoeBase,
// });
// const $ = load(html);
// const seasons = $('.dropdown-menu a')
// .toArray()
// .map((el) => {
// const element = $(el);
// const seasonID = element.attr('data-id');
// const seasonNumber = element.html()?.split(' ')[1];
// if (!seasonID || !seasonNumber || Number.isNaN(Number(seasonNumber))) {
// throw new Error('invalid season');
// }
// return {
// id: seasonID,
// season: Number(seasonNumber),
// };
// });
// const foundSeason = seasons.find((season) => season.season === media.season.number);
// if (!foundSeason) {
// return null;
// }
// return foundSeason.id;
// }
// export async function getZoeChipEpisodeID(
// ctx: ScrapeContext,
// media: ShowMedia,
// seasonID: string,
// ): Promise<string | null> {
// const episodeNumberRegex = /Eps (\d*):/;
// const html = await ctx.proxiedFetcher<string>(`/ajax/season/episodes/${seasonID}`, {
// baseUrl: zoeBase,
// });
// const $ = load(html);
// const episodes = $('.eps-item')
// .toArray()
// .map((el) => {
// const element = $(el);
// const episodeID = element.attr('data-id');
// const title = element.attr('title');
// if (!episodeID || !title) {
// throw new Error('invalid episode');
// }
// const regexResult = title.match(episodeNumberRegex);
// if (!regexResult || Number.isNaN(Number(regexResult[1]))) {
// throw new Error('invalid episode');
// }
// return {
// id: episodeID,
// episode: Number(regexResult[1]),
// };
// });
// const foundEpisode = episodes.find((episode) => episode.episode === media.episode.number);
// if (!foundEpisode) {
// return null;
// }
// return foundEpisode.id;
// }

View file

@ -0,0 +1,111 @@
// import { load } from 'cheerio';
// import { MovieMedia, ShowMedia } from '@/entrypoint/utils/media';
// import { zoeBase } from '@/providers/sources/zoechip/common';
// import { compareMedia } from '@/utils/compare';
// import { ScrapeContext } from '@/utils/context';
// export async function getZoeChipSearchResults(ctx: ScrapeContext, media: MovieMedia | ShowMedia) {
// const titleCleaned = media.title.toLocaleLowerCase().replace(/ /g, '-');
// const html = await ctx.proxiedFetcher<string>(`/search/${titleCleaned}`, {
// baseUrl: zoeBase,
// });
// const $ = load(html);
// return $('.film_list-wrap .flw-item .film-detail')
// .toArray()
// .map((element) => {
// const movie = $(element);
// const anchor = movie.find('.film-name a');
// const info = movie.find('.fd-infor');
// const title = anchor.attr('title');
// const href = anchor.attr('href');
// const type = info.find('.fdi-type').html();
// let year = info.find('.fdi-item').html();
// const id = href?.split('-').pop();
// if (!title) {
// return null;
// }
// if (!href) {
// return null;
// }
// if (!type) {
// return null;
// }
// // TV shows on ZoeChip do not have a year in their search results
// // Allow TV shows to pass this failure
// if (!year || Number.isNaN(Number(year))) {
// if (type === 'TV') {
// year = '0';
// } else {
// return null;
// }
// }
// if (!id) {
// return null;
// }
// return {
// title,
// year: Number(year),
// id,
// type,
// href,
// };
// });
// }
// export async function getZoeChipMovieID(ctx: ScrapeContext, media: MovieMedia): Promise<string | null> {
// const searchResults = await getZoeChipSearchResults(ctx, media);
// const matchingItem = searchResults.find((v) => v && v.type === 'Movie' && compareMedia(media, v.title, v.year));
// if (!matchingItem) {
// return null;
// }
// return matchingItem.id;
// }
// export async function getZoeChipShowID(ctx: ScrapeContext, media: ShowMedia): Promise<string | null> {
// // ZoeChip TV shows don't have a year on their search results
// // This makes it hard to filter between shows with the same name
// // To find the year, we must query each shows details page
// // This is slower, but more reliable
// const releasedRegex = /<\/strong><\/span> (\d.*)-\d.*-\d.*/;
// const searchResults = await getZoeChipSearchResults(ctx, media);
// const filtered = searchResults.filter((v) => v && v.type === 'TV' && compareMedia(media, v.title));
// for (const result of filtered) {
// // This gets filtered above but the linter Gods don't think so
// if (!result) {
// continue;
// }
// const html = await ctx.proxiedFetcher<string>(result.href, {
// baseUrl: zoeBase,
// });
// // The HTML is not structured in a way that makes using Cheerio clean
// // There are no unique IDs or classes to query, resulting in long ugly queries
// // Regex is faster and cleaner in this case
// const regexResult = html.match(releasedRegex);
// if (regexResult) {
// const year = Number(regexResult[1]);
// if (!Number.isNaN(year) && compareMedia(media, result.title, year)) {
// return result.id;
// }
// }
// }
// return null;
// }

BIN
src/providers/embeds/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,52 +0,0 @@
import { unpack } from 'unpacker';
import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '../base';
const evalCodeRegex = /eval\((.*)\)/g;
const fileRegex = /file:"(.*?)"/g;
const tracksRegex = /\{file:"([^"]+)",kind:"thumbnails"\}/g;
export const droploadScraper = makeEmbed({
id: 'dropload',
name: 'Dropload',
rank: 120,
scrape: async (ctx) => {
const mainPageRes = await ctx.proxiedFetcher.full<string>(ctx.url, {
headers: {
referer: ctx.url,
},
});
const mainPageUrl = new URL(mainPageRes.finalUrl);
const mainPage = mainPageRes.body;
const evalCode = mainPage.match(evalCodeRegex);
if (!evalCode) throw new Error('Failed to find eval code');
const unpacked = unpack(evalCode[1]);
const file = fileRegex.exec(unpacked);
const thumbnailTrack = tracksRegex.exec(unpacked);
if (!file?.[1]) throw new Error('Failed to find file');
return {
stream: [
{
id: 'primary',
type: 'hls',
playlist: file[1],
flags: [flags.IP_LOCKED, flags.CORS_ALLOWED],
captions: [],
...(thumbnailTrack
? {
thumbnailTrack: {
type: 'vtt',
url: mainPageUrl.origin + thumbnailTrack[1],
},
}
: {}),
},
],
};
},
});

View file

@ -1,24 +0,0 @@
import { MediaTypes } from '@/entrypoint/utils/media';
export const febBoxBase = `https://www.febbox.com`;
export interface FebboxFileList {
file_name: string;
ext: string;
fid: number;
oss_fid: number;
is_dir: 0 | 1;
}
export function parseInputUrl(url: string) {
const [type, id, seasonId, episodeId] = url.slice(1).split('/');
const season = seasonId ? parseInt(seasonId, 10) : undefined;
const episode = episodeId ? parseInt(episodeId, 10) : undefined;
return {
type: type as MediaTypes,
id,
season,
episode,
};
}

View file

@ -1,69 +0,0 @@
import { MediaTypes } from '@/entrypoint/utils/media';
import { FebboxFileList, febBoxBase } from '@/providers/embeds/febbox/common';
import { EmbedScrapeContext } from '@/utils/context';
export async function getFileList(
ctx: EmbedScrapeContext,
shareKey: string,
parentId?: number,
): Promise<FebboxFileList[]> {
const query: Record<string, string> = {
share_key: shareKey,
pwd: '',
};
if (parentId) {
query.parent_id = parentId.toString();
query.page = '1';
}
const streams = await ctx.proxiedFetcher<{
data?: {
file_list?: FebboxFileList[];
};
}>('/file/file_share_list', {
headers: {
'accept-language': 'en', // without this header, the request is marked as a webscraper
},
baseUrl: febBoxBase,
query,
});
return streams.data?.file_list ?? [];
}
function isValidStream(file: FebboxFileList): boolean {
return file.ext === 'mp4' || file.ext === 'mkv';
}
export async function getStreams(
ctx: EmbedScrapeContext,
shareKey: string,
type: MediaTypes,
season?: number,
episode?: number,
): Promise<FebboxFileList[]> {
const streams = await getFileList(ctx, shareKey);
if (type === 'show') {
const seasonFolder = streams.find((v) => {
if (!v.is_dir) return false;
return v.file_name.toLowerCase() === `season ${season}`;
});
if (!seasonFolder) return [];
const episodes = await getFileList(ctx, shareKey, seasonFolder.fid);
const s = season?.toString() ?? '0';
const e = episode?.toString() ?? '0';
const episodeRegex = new RegExp(`[Ss]0*${s}[Ee]0*${e}`);
return episodes
.filter((file) => {
if (file.is_dir) return false;
const match = file.file_name.match(episodeRegex);
if (!match) return false;
return true;
})
.filter(isValidStream);
}
return streams.filter((v) => !v.is_dir).filter(isValidStream);
}

View file

@ -1,50 +0,0 @@
import { MediaTypes } from '@/entrypoint/utils/media';
import { makeEmbed } from '@/providers/base';
import { parseInputUrl } from '@/providers/embeds/febbox/common';
import { getStreams } from '@/providers/embeds/febbox/fileList';
import { getSubtitles } from '@/providers/embeds/febbox/subtitles';
import { showboxBase } from '@/providers/sources/showbox/common';
// structure: https://www.febbox.com/share/<random_key>
export function extractShareKey(url: string): string {
const parsedUrl = new URL(url);
const shareKey = parsedUrl.pathname.split('/')[2];
return shareKey;
}
export const febboxHlsScraper = makeEmbed({
id: 'febbox-hls',
name: 'Febbox (HLS)',
rank: 160,
disabled: true,
async scrape(ctx) {
const { type, id, season, episode } = parseInputUrl(ctx.url);
const sharelinkResult = await ctx.proxiedFetcher<{
data?: { link?: string };
}>('/index/share_link', {
baseUrl: showboxBase,
query: {
id,
type: type === 'movie' ? '1' : '2',
},
});
if (!sharelinkResult?.data?.link) throw new Error('No embed url found');
ctx.progress(30);
const shareKey = extractShareKey(sharelinkResult.data.link);
const fileList = await getStreams(ctx, shareKey, type, season, episode);
const firstStream = fileList[0];
if (!firstStream) throw new Error('No playable mp4 stream found');
ctx.progress(70);
return {
stream: [
{
id: 'primary',
type: 'hls',
flags: [],
captions: await getSubtitles(ctx, id, firstStream.fid, type as MediaTypes, season, episode),
playlist: `https://www.febbox.com/hls/main/${firstStream.oss_fid}.m3u8`,
},
],
};
},
});

View file

@ -1,53 +0,0 @@
import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base';
import { parseInputUrl } from '@/providers/embeds/febbox/common';
import { getStreamQualities } from '@/providers/embeds/febbox/qualities';
import { getSubtitles } from '@/providers/embeds/febbox/subtitles';
export const febboxMp4Scraper = makeEmbed({
id: 'febbox-mp4',
name: 'Febbox (MP4)',
rank: 190,
async scrape(ctx) {
const { type, id, season, episode } = parseInputUrl(ctx.url);
let apiQuery: object | null = null;
if (type === 'movie') {
apiQuery = {
uid: '',
module: 'Movie_downloadurl_v3',
mid: id,
oss: '1',
group: '',
};
} else if (type === 'show') {
apiQuery = {
uid: '',
module: 'TV_downloadurl_v3',
tid: id,
season,
episode,
oss: '1',
group: '',
};
}
if (!apiQuery) throw Error('Incorrect type');
const { qualities, fid } = await getStreamQualities(ctx, apiQuery);
if (fid === undefined) throw new Error('No streamable file found');
ctx.progress(70);
return {
stream: [
{
id: 'primary',
captions: await getSubtitles(ctx, id, fid, type, episode, season),
qualities,
type: 'file',
flags: [flags.CORS_ALLOWED],
},
],
};
},
});

View file

@ -1,44 +0,0 @@
import { sendRequest } from '@/providers/sources/showbox/sendRequest';
import { StreamFile } from '@/providers/streams';
import { ScrapeContext } from '@/utils/context';
const allowedQualities = ['360', '480', '720', '1080', '4k'];
interface FebboxQuality {
path: string;
real_quality: string;
fid?: number;
}
function mapToQuality(quality: FebboxQuality): FebboxQuality | null {
const q = quality.real_quality.replace('p', '').toLowerCase();
if (!allowedQualities.includes(q)) return null;
return {
real_quality: q,
path: quality.path,
fid: quality.fid,
};
}
export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) {
const mediaRes: { list: FebboxQuality[] } = (await sendRequest(ctx, apiQuery)).data;
const qualityMap = mediaRes.list.map((v) => mapToQuality(v)).filter((v): v is FebboxQuality => !!v);
const qualities: Record<string, StreamFile> = {};
allowedQualities.forEach((quality) => {
const foundQuality = qualityMap.find((q) => q.real_quality === quality && q.path);
if (foundQuality) {
qualities[quality] = {
type: 'mp4',
url: foundQuality.path,
};
}
});
return {
qualities,
fid: mediaRes.list[0]?.fid,
};
}

View file

@ -1,75 +0,0 @@
import {
Caption,
getCaptionTypeFromUrl,
isValidLanguageCode,
removeDuplicatedLanguages as removeDuplicateLanguages,
} from '@/providers/captions';
import { captionsDomains } from '@/providers/sources/showbox/common';
import { sendRequest } from '@/providers/sources/showbox/sendRequest';
import { ScrapeContext } from '@/utils/context';
interface CaptionApiResponse {
data: {
list: {
subtitles: {
order: number;
lang: string;
file_path: string;
}[];
}[];
};
}
export async function getSubtitles(
ctx: ScrapeContext,
id: string,
fid: number | undefined,
type: 'show' | 'movie',
episodeId?: number,
seasonId?: number,
): Promise<Caption[]> {
const module = type === 'movie' ? 'Movie_srt_list_v2' : 'TV_srt_list_v2';
const subtitleApiQuery = {
fid,
uid: '',
module,
mid: type === 'movie' ? id : undefined,
tid: type !== 'movie' ? id : undefined,
episode: episodeId?.toString(),
season: seasonId?.toString(),
};
const subResult = (await sendRequest(ctx, subtitleApiQuery)) as CaptionApiResponse;
const subtitleList = subResult.data.list;
let output: Caption[] = [];
subtitleList.forEach((sub) => {
const subtitle = sub.subtitles.sort((a, b) => b.order - a.order)[0];
if (!subtitle) return;
const subtitleFilePath = subtitle.file_path
.replace(captionsDomains[0], captionsDomains[1])
.replace(/\s/g, '+')
.replace(/[()]/g, (c) => {
return `%${c.charCodeAt(0).toString(16)}`;
});
const subtitleType = getCaptionTypeFromUrl(subtitleFilePath);
if (!subtitleType) return;
const validCode = isValidLanguageCode(subtitle.lang);
if (!validCode) return;
output.push({
id: subtitleFilePath,
language: subtitle.lang,
hasCorsRestrictions: true,
type: subtitleType,
url: subtitleFilePath,
});
});
output = removeDuplicateLanguages(output);
return output;
}

View file

@ -1,62 +0,0 @@
import { load } from 'cheerio';
import { unpack } from 'unpacker';
import { flags } from '@/entrypoint/utils/targets';
import { SubtitleResult } from './types';
import { makeEmbed } from '../../base';
import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '../../captions';
const evalCodeRegex = /eval\((.*)\)/g;
const fileRegex = /file:"(.*?)"/g;
export const fileMoonScraper = makeEmbed({
id: 'filemoon',
name: 'Filemoon',
rank: 300,
scrape: async (ctx) => {
const embedRes = await ctx.proxiedFetcher<string>(ctx.url, {
headers: {
referer: ctx.url,
},
});
const embedHtml = load(embedRes);
const evalCode = embedHtml('script').text().match(evalCodeRegex);
if (!evalCode) throw new Error('Failed to find eval code');
const unpacked = unpack(evalCode[0]);
const file = fileRegex.exec(unpacked);
if (!file?.[1]) throw new Error('Failed to find file');
const url = new URL(ctx.url);
const subtitlesLink = url.searchParams.get('sub.info');
const captions: Caption[] = [];
if (subtitlesLink) {
const captionsResult = await ctx.proxiedFetcher<SubtitleResult>(subtitlesLink);
for (const caption of captionsResult) {
const language = labelToLanguageCode(caption.label);
const captionType = getCaptionTypeFromUrl(caption.file);
if (!language || !captionType) continue;
captions.push({
id: caption.file,
url: caption.file,
type: captionType,
language,
hasCorsRestrictions: false,
});
}
}
return {
stream: [
{
id: 'primary',
type: 'hls',
playlist: file[1],
flags: [flags.IP_LOCKED],
captions,
},
],
};
},
});

View file

@ -1,38 +0,0 @@
import { flags } from '@/entrypoint/utils/targets';
import { NotFoundError } from '@/utils/errors';
import { makeEmbed } from '../../base';
import { fileMoonScraper } from './index';
export const fileMoonMp4Scraper = makeEmbed({
id: 'filemoon-mp4',
name: 'Filemoon MP4',
rank: 400,
scrape: async (ctx) => {
const result = await fileMoonScraper.scrape(ctx);
if (!result.stream) throw new NotFoundError('Failed to find result');
if (result.stream[0].type !== 'hls') throw new NotFoundError('Failed to find hls stream');
const url = result.stream[0].playlist.replace(/\/hls2\//, '/download/').replace(/\.m3u8/, '.mp4');
return {
stream: [
{
id: 'primary',
type: 'file',
qualities: {
unknown: {
type: 'mp4',
url,
},
},
flags: [flags.IP_LOCKED],
captions: result.stream[0].captions,
},
],
};
},
});

View file

@ -1,57 +0,0 @@
import { makeFullUrl } from '@/fetchers/common';
import { decodeData } from '@/providers/sources/vidsrcto/common';
import { EmbedScrapeContext } from '@/utils/context';
export const vidplayBase = 'https://vidplay.online';
export const referer = `${vidplayBase}/`;
// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16
// Full credits to @Ciarands!
export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise<string[]> => {
const res = await ctx.proxiedFetcher<string>('https://github.com/Ciarands/vidsrc-keys/blob/main/keys.json');
const regex = /"rawLines":\s*\[([\s\S]*?)\]/;
const rawLines = res.match(regex)?.[1];
if (!rawLines) throw new Error('No keys found');
const keys = JSON.parse(`${rawLines.substring(1).replace(/\\"/g, '"')}]`);
return keys;
};
export const getEncodedId = async (ctx: EmbedScrapeContext) => {
const url = new URL(ctx.url);
const id = url.pathname.replace('/e/', '');
const keyList = await getDecryptionKeys(ctx);
const decodedId = decodeData(keyList[0], id);
const encodedResult = decodeData(keyList[1], decodedId);
const b64encoded = btoa(encodedResult);
return b64encoded.replace('/', '_');
};
export const getFuTokenKey = async (ctx: EmbedScrapeContext) => {
const id = await getEncodedId(ctx);
const fuTokenRes = await ctx.proxiedFetcher<string>('/futoken', {
baseUrl: vidplayBase,
headers: {
referer: ctx.url,
},
});
const fuKey = fuTokenRes.match(/var\s+k\s*=\s*'([^']+)'/)?.[1];
if (!fuKey) throw new Error('No fuKey found');
const tokens = [];
for (let i = 0; i < id.length; i += 1) {
tokens.push(fuKey.charCodeAt(i % fuKey.length) + id.charCodeAt(i));
}
return `${fuKey},${tokens.join(',')}`;
};
export const getFileUrl = async (ctx: EmbedScrapeContext) => {
const fuToken = await getFuTokenKey(ctx);
return makeFullUrl(`/mediainfo/${fuToken}`, {
baseUrl: vidplayBase,
query: {
...Object.fromEntries(new URL(ctx.url).searchParams.entries()),
autostart: 'true',
},
});
};

View file

@ -1,68 +0,0 @@
import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base';
import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions';
import { getFileUrl } from './common';
import { SubtitleResult, ThumbnailTrack, VidplaySourceResponse } from './types';
export const vidplayScraper = makeEmbed({
id: 'vidplay',
name: 'VidPlay',
rank: 401,
scrape: async (ctx) => {
const fileUrl = await getFileUrl(ctx);
const fileUrlRes = await ctx.proxiedFetcher<VidplaySourceResponse>(fileUrl, {
headers: {
referer: ctx.url,
},
});
if (typeof fileUrlRes.result === 'number') throw new Error('File not found');
const source = fileUrlRes.result.sources[0].file;
const thumbnailSource = fileUrlRes.result.tracks.find((track) => track.kind === 'thumbnails');
let thumbnailTrack: ThumbnailTrack | undefined;
if (thumbnailSource) {
thumbnailTrack = {
type: 'vtt',
url: thumbnailSource.file,
};
}
const url = new URL(ctx.url);
const subtitlesLink = url.searchParams.get('sub.info');
const captions: Caption[] = [];
if (subtitlesLink) {
const captionsResult = await ctx.proxiedFetcher<SubtitleResult>(subtitlesLink);
for (const caption of captionsResult) {
const language = labelToLanguageCode(caption.label);
const captionType = getCaptionTypeFromUrl(caption.file);
if (!language || !captionType) continue;
captions.push({
id: caption.file,
url: caption.file,
type: captionType,
language,
hasCorsRestrictions: false,
});
}
}
return {
stream: [
{
id: 'primary',
type: 'hls',
playlist: source,
flags: [flags.PROXY_BLOCKED],
headers: {
Referer: url.origin,
Origin: url.origin,
},
captions,
thumbnailTrack,
},
],
};
},
});

Some files were not shown because too many files have changed in this diff Show more