chore: rework referer header passing and mediaflow proxy URL creation

This commit is contained in:
WebStreamr 2025-09-25 20:00:39 +00:00
parent 447153ca3c
commit 25b326ee48
No known key found for this signature in database
36 changed files with 103 additions and 85 deletions

View file

@ -24,7 +24,9 @@ export class DoodStream extends Extractor {
};
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
const passMd5Match = html.match(/\/pass_md5\/[\w-]+\/([\w-]+)/);
if (!passMd5Match) {
@ -33,7 +35,7 @@ export class DoodStream extends Extractor {
const token = passMd5Match[1] as string;
const baseUrl = await this.fetcher.text(ctx, new URL(passMd5Match[0], url.origin));
const baseUrl = await this.fetcher.text(ctx, new URL(passMd5Match[0], url.origin), { headers: { Referer: url.href } });
const $ = cheerio.load(html);
const title = $('title').text().trim().replace(/ - DoodStream$/, '').trim();
@ -44,7 +46,7 @@ export class DoodStream extends Extractor {
mp4Url = new URL(baseUrl);
} else {
mp4Url = new URL(`${baseUrl}${randomstring.generate(10)}?token=${token}&expiry=${Date.now()}`);
bytes = await guessSizeFromMp4(ctx, this.fetcher, mp4Url, { headers: { Referer: url.origin } });
bytes = await guessSizeFromMp4(ctx, this.fetcher, mp4Url, { headers: { Referer: url.href } });
}
return [

View file

@ -19,7 +19,9 @@ export class Dropload extends Extractor {
public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/d/', '/').replace('/e/', '/').replace('/embed-', '/'));
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
if (html.includes('File Not Found') || html.includes('Pending in queue')) {
throw new NotFoundError();

View file

@ -15,9 +15,11 @@ export class ExternalUrl extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
try {
// Make sure the URL is accessible, but avoid causing noise and delays doing this
await this.fetcher.head(ctx, url, { noFlareSolverr: true, timeout: 1000, headers: { Referer: url.origin } });
await this.fetcher.head(ctx, url, { noFlareSolverr: true, timeout: 1000, headers });
} catch (error) {
/* istanbul ignore if */
if (!(error instanceof BlockedError)) {

View file

@ -41,7 +41,7 @@ export abstract class Extractor {
error,
label: this.label,
sourceId: `${this.id}`,
...(meta && meta),
meta,
},
];
}

View file

@ -19,10 +19,12 @@ export class Fastream extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Fastream', url);
const headers = { Referer: meta.referer ?? url.href };
const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Fastream', url, headers);
const downloadUrl = new URL(url.href.replace('/embed-', '/d/'));
const html = await this.fetcher.text(ctx, downloadUrl);
const html = await this.fetcher.text(ctx, downloadUrl, { headers });
const heightAndSizeMatch = html.match(/\d{3,}x(\d{3,}), ([\d.]+ ?[GM]B)/) as string[];
const titleMatch = html.match(/>Download (.*?)</) as string[];

View file

@ -59,7 +59,7 @@ export class FileLions extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { ...(meta.referer && { Referer: meta.referer }) };
const headers = { Referer: meta.referer ?? url.href };
const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'FileLions', url, headers);
@ -80,7 +80,7 @@ export class FileLions extends Extractor {
ttl: this.ttl,
meta: {
...meta,
height: await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl),
height: await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl, { headers: { Referer: url.href } }),
...(sizeMatch && {
bytes: bytes.parse(sizeMatch[1] as string) as number,
}),

View file

@ -12,7 +12,7 @@ export class Fsst extends Extractor {
};
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { Referer: meta.referer ?? url.origin };
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });

View file

@ -15,10 +15,12 @@ export class HubCloud extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const redirectHtml = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const redirectHtml = await this.fetcher.text(ctx, url, { headers });
const redirectUrlMatch = redirectHtml.match(/var url ?= ?'(.*?)'/) as string[];
const linksHtml = await this.fetcher.text(ctx, new URL(redirectUrlMatch[1] as string));
const linksHtml = await this.fetcher.text(ctx, new URL(redirectUrlMatch[1] as string), { headers: { Referer: url.href } });
const $ = cheerio.load(linksHtml);
const urlResults = [

View file

@ -36,7 +36,7 @@ export class KinoGer extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { 'Referer': meta.referer ?? url.origin, 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' };
const headers = { 'Referer': meta.referer ?? url.href, 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' };
const hexData = await this.fetcher.text(ctx, url, { headers });
@ -59,7 +59,7 @@ export class KinoGer extends Extractor {
ttl: this.ttl,
meta: {
...meta,
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers }),
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: url.href } }),
title,
},
requestHeaders: {

View file

@ -7,7 +7,7 @@ import { Mixdrop } from './Mixdrop';
const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] });
const extractorRegistry = new ExtractorRegistry(logger, [new Mixdrop(new FetcherMock(`${__dirname}/__fixtures__/Mixdrop`))]);
const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow-proxy.test', mediaFlowProxyPassword: 'asdfg' });
const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow.test.org', mediaFlowProxyPassword: 'test' });
describe('Mixdrop', () => {
test('mixdrop.my /e/', async () => {

View file

@ -2,7 +2,7 @@ import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { buildMediaFlowProxyExtractorStreamUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
export class Mixdrop extends Extractor {
@ -19,8 +19,10 @@ export class Mixdrop extends Extractor {
public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/f/', '/e/'));
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const fileUrl = new URL(url.href.replace('/e/', '/f/'));
const html = await this.fetcher.text(ctx, fileUrl);
const html = await this.fetcher.text(ctx, fileUrl, { headers });
if (/can't find the (file|video)/.test(html)) {
throw new NotFoundError();
@ -33,7 +35,7 @@ export class Mixdrop extends Extractor {
return [
{
url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Mixdrop', url),
url: await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Mixdrop', url, headers),
format: Format.mp4,
label: this.label,
sourceId: `${this.id}_${meta.countryCodes?.join('_')}`,

View file

@ -19,7 +19,9 @@ export class SaveFiles extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
if (/file was locked|file was deleted/i.test(html)) {
throw new NotFoundError();

View file

@ -52,7 +52,7 @@ export class Soaper extends Extractor {
ttl: this.ttl,
meta: {
...meta,
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url),
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: url.href } }),
},
},
];

View file

@ -11,7 +11,9 @@ export class StreamEmbed extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
const video = JSON.parse((html.match(/video ?= ?(.*);/) as string[])[1] as string);

View file

@ -11,6 +11,6 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow.test.org',
describe('Streamtape', () => {
test('streamtape.com /e', async () => {
expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/84Kb3mkoYAiokmW'))).toMatchSnapshot();
expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/DjmZBG0K9gHkJ7z/WALL-E.2008.1080p.BrRip.x264.YIFY.mp4'))).toMatchSnapshot();
});
});

View file

@ -1,7 +1,7 @@
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import {
buildMediaFlowProxyExtractorRedirectUrl,
buildMediaFlowProxyExtractorStreamUrl,
supportsMediaFlowProxy,
} from '../utils';
import { guessSizeFromMp4 } from '../utils/size';
@ -21,12 +21,14 @@ export class Streamtape extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
const $ = cheerio.load(html);
const title = $('meta[name="og:title"]').attr('content') as string;
const mp4Url = buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url);
const mp4Url = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Streamtape', url, headers);
return [
{
@ -38,7 +40,7 @@ export class Streamtape extends Extractor {
meta: {
...meta,
title,
bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url),
bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url, { headers: { Referer: url.href } }),
},
},
];

View file

@ -21,7 +21,7 @@ export class SuperVideo extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const headers = { Referer: meta.referer ?? url.origin };
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -39,7 +39,7 @@ export class SuperVideo extends Extractor {
const size = heightAndSizeMatch ? bytes.parse(heightAndSizeMatch[2] as string) as number : undefined;
const height = heightAndSizeMatch
? parseInt(heightAndSizeMatch[1] as string)
: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers });
: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: url.href } });
const $ = cheerio.load(html);
const title = $('.download__title').text().trim();

View file

@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { buildMediaFlowProxyExtractorStreamUrl, supportsMediaFlowProxy } from '../utils';
import { guessSizeFromMp4 } from '../utils/size';
import { Extractor } from './Extractor';
@ -20,14 +20,16 @@ export class Uqload extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
const heightMatch = html.match(/\d{3,}x(\d{3,})/);
const $ = cheerio.load(html);
const title = $('h1').text().trim();
const mp4Url = buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Uqload', url);
const mp4Url = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Uqload', url, headers);
return [
{
@ -39,7 +41,7 @@ export class Uqload extends Extractor {
meta: {
...meta,
title,
bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url),
bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url, { headers: { Referer: url.href } }),
...(heightMatch && {
height: parseInt(heightMatch[1] as string),
}),

View file

@ -79,7 +79,7 @@ export class VidSrc extends Extractor {
ttl: this.ttl,
meta: {
...meta,
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url),
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: iframeUrl.href } }),
title,
},
};

View file

@ -1,6 +1,6 @@
import { Context, CountryCode, Format, Meta, UrlResult } from '../types';
import {
buildMediaFlowProxyExtractorStreamUrl, guessHeightFromPlaylist,
buildMediaFlowProxyExtractorStreamUrl, CustomRequestInit, guessHeightFromPlaylist,
hasMultiEnabled,
iso639FromCountryCode,
supportsMediaFlowProxy,
@ -20,7 +20,7 @@ export class VixSrc extends Extractor {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'VixCloud', url);
const countryCodes = await this.determineCountryCodesFromPlaylist(ctx, playlistUrl);
const countryCodes = await this.determineCountryCodesFromPlaylist(ctx, playlistUrl, { headers: { Referer: url.href } });
if (!hasMultiEnabled(ctx.config) && !countryCodes.some(countryCode => countryCode in ctx.config)) {
return [];
@ -35,14 +35,14 @@ export class VixSrc extends Extractor {
ttl: this.ttl,
meta: {
countryCodes,
height: await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl),
height: await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl, { headers: { Referer: url.href } }),
},
},
];
};
private async determineCountryCodesFromPlaylist(ctx: Context, playlistUrl: URL): Promise<CountryCode[]> {
const playlist = await this.fetcher.text(ctx, playlistUrl);
private async determineCountryCodesFromPlaylist(ctx: Context, playlistUrl: URL, init?: CustomRequestInit): Promise<CountryCode[]> {
const playlist = await this.fetcher.text(ctx, playlistUrl, init);
const countryCodes: CountryCode[] = [CountryCode.it];

View file

@ -13,7 +13,9 @@ export class YouTube extends Extractor {
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
const titleMatch = html.match(/"title":{"runs":\[{"text":"(.*?)"/) as string[];

View file

@ -0,0 +1 @@
{"destination_url":"https://s-delivery29.mxcontent.net/v2/knq0kj8waq44l8.mp4?s=85VD8DA80IfkbMa3zZR3XA&e=1758851244&_t=1758837735","request_headers":{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36","referer":"https://mixdrop.my/e/knq0kj8waq44l8"},"mediaflow_proxy_url":"https://mediaflow.test.org/proxy/stream","query_params":{"api_password":"test"}}

View file

@ -0,0 +1 @@
{"connection":"close","content-length":"1293552945","content-range":"bytes 0-1293552944/1293552945","content-type":"video/mp4","date":"Thu, 25 Sep 2025 19:52:25 GMT","etag":"\"6878008a-4d1a0d31\"","last-modified":"Wed, 16 Jul 2025 19:42:02 GMT","server":"nginx/1.24.0 (Ubuntu)"}

View file

@ -1 +0,0 @@
{"connection":"close","content-length":"1187340184","content-range":"bytes 0-1187340183/1187340184","content-type":"video/mp4","date":"Thu, 28 Aug 2025 19:09:50 GMT","etag":"\"68acb5cc-46c55f98\"","last-modified":"Mon, 25 Aug 2025 19:13:16 GMT","server":"nginx/1.24.0 (Ubuntu)"}

View file

@ -0,0 +1 @@
{"destination_url":"https://streamtape.com/get_video?id=DjmZBG0K9gHkJ7z&expires=1758899945&ip=F0AOKRISKxSHDN&token=uQu83SOzkLwi","request_headers":{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36","referer":"https://streamtape.com/e/DjmZBG0K9gHkJ7z/WALL-E.2008.1080p.BrRip.x264.YIFY.mp4"},"mediaflow_proxy_url":"https://mediaflow.test.org/proxy/stream","query_params":{"api_password":"test"}}

View file

@ -1 +1 @@
{"connection":"close","content-length":"431690639","content-range":"bytes 0-431690638/431690639","content-type":"video/mp4","date":"Thu, 28 Aug 2025 19:12:32 GMT","etag":"\"680029e2-19bb138f\"","last-modified":"Wed, 16 Apr 2025 22:06:26 GMT","server":"nginx/1.24.0 (Ubuntu)"}
{"connection":"close","content-length":"431690639","content-range":"bytes 0-431690638/431690639","content-type":"video/mp4","date":"Thu, 25 Sep 2025 19:55:43 GMT","etag":"\"680029e2-19bb138f\"","last-modified":"Wed, 16 Apr 2025 22:06:26 GMT","server":"nginx/1.24.0 (Ubuntu)"}

View file

@ -0,0 +1 @@
{"destination_url":"https://m60.uqload.cx/3rfkr3ep2fw2q4drdinp7ppqco2xtt5ele76uwwvy24rwjscvdv3lq62jd3a/v.mp4","request_headers":{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36","referer":"https://uqload.net/"},"mediaflow_proxy_url":"https://mediaflow.test.org/proxy/stream","query_params":{"api_password":"test"}}

View file

@ -21,12 +21,14 @@ exports[`ExtractorRegistry returns external URLs if enabled by config 1`] = `
exports[`ExtractorRegistry returns external url for error 1`] = `
[
{
"countryCodes": [],
"error": [Error: Fetcher error: 403: Forbidden
],
"format": "unknown",
"isExternal": true,
"label": "Dropload",
"meta": {
"countryCodes": [],
},
"sourceId": "dropload",
"url": "https://dropload.io/mocked-blocked.html",
},

View file

@ -14,7 +14,7 @@ exports[`Mixdrop mixdrop.my /e/ 1`] = `
},
"sourceId": "mixdrop_",
"ttl": 900000,
"url": "https://mediaflow-proxy.test/extractor/video?host=Mixdrop&api_password=asdfg&d=https%3A%2F%2Fmixdrop.my%2Fe%2Fknq0kj8waq44l8&redirect_stream=true",
"url": "https://mediaflow.test.org/proxy/stream?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmixdrop.my%2Fe%2Fknq0kj8waq44l8&d=https%3A%2F%2Fs-delivery29.mxcontent.net%2Fv2%2Fknq0kj8waq44l8.mp4%3Fs%3D85VD8DA80IfkbMa3zZR3XA%26e%3D1758851244%26_t%3D1758837735",
},
]
`;

View file

@ -6,13 +6,13 @@ exports[`Streamtape streamtape.com /e 1`] = `
"format": "mp4",
"label": "Streamtape (via MediaFlow Proxy)",
"meta": {
"bytes": 1187340184,
"bytes": 1293552945,
"countryCodes": [],
"title": "mickey-17-2025-[latino].mp4",
"title": "WALL-E.2008.1080p.BrRip.x264.YIFY.mp4",
},
"sourceId": "streamtape_",
"ttl": 900000,
"url": "https://mediaflow.test.org/extractor/video?host=Streamtape&api_password=test&d=https%3A%2F%2Fstreamtape.com%2Fe%2F84Kb3mkoYAiokmW&redirect_stream=true",
"url": "https://mediaflow.test.org/proxy/stream?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fstreamtape.com%2Fe%2FDjmZBG0K9gHkJ7z%2FWALL-E.2008.1080p.BrRip.x264.YIFY.mp4&d=https%3A%2F%2Fstreamtape.com%2Fget_video%3Fid%3DDjmZBG0K9gHkJ7z%26expires%3D1758899945%26ip%3DF0AOKRISKxSHDN%26token%3DuQu83SOzkLwi",
},
]
`;

View file

@ -13,7 +13,7 @@ exports[`Uqload uqload.net /embed- 1`] = `
},
"sourceId": "uqload_",
"ttl": 900000,
"url": "https://mediaflow.test.org/extractor/video?host=Uqload&api_password=test&d=https%3A%2F%2Fuqload.net%2Fz0xbr87oz637.html&redirect_stream=true",
"url": "https://mediaflow.test.org/proxy/stream?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fuqload.net%2F&d=https%3A%2F%2Fm60.uqload.cx%2F3rfkr3ep2fw2q4drdinp7ppqco2xtt5ele76uwwvy24rwjscvdv3lq62jd3a%2Fv.mp4",
},
]
`;

View file

@ -41,6 +41,7 @@ exports[`VidSrc blocking issues are retried and fail if no tlds are left 1`] = `
"format": "unknown",
"isExternal": true,
"label": "VidSrc",
"meta": {},
"sourceId": "vidsrc",
"url": "https://vidsrc.xyz/embed/movie/tt33043892/1/1",
},
@ -56,6 +57,7 @@ exports[`VidSrc rate limit issues are retried and fail if no tlds are left 1`] =
"format": "unknown",
"isExternal": true,
"label": "VidSrc",
"meta": {},
"sourceId": "vidsrc",
"url": "https://vidsrc.xyz/embed/movie/tt33043892/1/1",
},

View file

@ -10,29 +10,22 @@ interface ExtractResult {
export const supportsMediaFlowProxy = (ctx: Context): boolean => !!ctx.config['mediaFlowProxyUrl'];
const buildMediaFlowProxyExtractorUrl = (ctx: Context, host: string, url: URL): URL => {
const buildMediaFlowProxyExtractorUrl = (ctx: Context, host: string, url: URL, headers: Record<string, string>): URL => {
const mediaFlowProxyUrl = new URL('/extractor/video', ctx.config.mediaFlowProxyUrl);
mediaFlowProxyUrl.searchParams.append('host', host);
mediaFlowProxyUrl.searchParams.append('api_password', `${ctx.config.mediaFlowProxyPassword}`);
mediaFlowProxyUrl.searchParams.append('d', url.href);
return mediaFlowProxyUrl;
};
export const buildMediaFlowProxyExtractorRedirectUrl = (ctx: Context, host: string, url: URL): URL => {
const mediaFlowProxyUrl = buildMediaFlowProxyExtractorUrl(ctx, host, url);
mediaFlowProxyUrl.searchParams.append('redirect_stream', 'true');
for (const headerKey in headers) {
mediaFlowProxyUrl.searchParams.set('h_' + headerKey.toLowerCase(), headers[headerKey] as string);
}
return mediaFlowProxyUrl;
};
export const buildMediaFlowProxyExtractorStreamUrl = async (ctx: Context, fetcher: Fetcher, host: string, url: URL, headers: Record<string, string> = {}): Promise<URL> => {
const mediaFlowProxyUrl = buildMediaFlowProxyExtractorUrl(ctx, host, url);
for (const headerKey in headers) {
mediaFlowProxyUrl.searchParams.set('h_' + headerKey.toLowerCase(), headers[headerKey] as string);
}
const mediaFlowProxyUrl = buildMediaFlowProxyExtractorUrl(ctx, host, url, headers);
const extractResult: ExtractResult = await fetcher.json(ctx, mediaFlowProxyUrl);
@ -44,9 +37,6 @@ export const buildMediaFlowProxyExtractorStreamUrl = async (ctx: Context, fetche
for (const requestHeadersKey in extractResult.request_headers) {
streamUrl.searchParams.append(`h_${requestHeadersKey}`, extractResult.request_headers[requestHeadersKey] as string);
}
for (const headerKey in headers) {
streamUrl.searchParams.set('h_' + headerKey.toLowerCase(), headers[headerKey] as string);
}
streamUrl.searchParams.append('d', extractResult.destination_url);
return streamUrl;