use ext to proxy m3u8 streams with createM3U8ProxyUrl func

This commit is contained in:
Pas 2025-10-21 16:35:32 -06:00
parent e0b924e6f5
commit 1f98cd3636
18 changed files with 59 additions and 25 deletions

View file

@ -5,14 +5,14 @@ import { NotFoundError } from '@/utils/errors';
import { createM3U8ProxyUrl, updateM3U8ProxyUrl } from '@/utils/proxy';
// REQUIRES A PROXY FOR MOST SERVERS set it up here https://github.com/Pasithea0/M3U8-Proxy
function createProxyUrl(originalUrl: string, referer: string): string {
function createProxyUrl(originalUrl: string, referer: string, features?: any): string {
const headers = {
referer,
};
return createM3U8ProxyUrl(originalUrl, headers);
return createM3U8ProxyUrl(originalUrl, features, headers);
}
function processProxiedURL(url: string): string {
function processProxiedURL(url: string, ctx: MovieScrapeContext | ShowScrapeContext): string {
// Handle orbitproxy URLs
if (url.includes('orbitproxy')) {
try {
@ -25,7 +25,7 @@ function processProxiedURL(url: string): string {
const originalUrl = jsonData.u;
const referer = jsonData.r || '';
return createProxyUrl(originalUrl, referer);
return createProxyUrl(originalUrl, referer, ctx.features);
} catch (jsonError) {
console.error('Error decoding/parsing orbitproxy data:', jsonError);
}
@ -85,7 +85,7 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis
const processedServers = servers.map((server) => ({
...server,
url: processProxiedURL(server.url),
url: processProxiedURL(server.url, ctx),
}));
const embeds: SourcererEmbed[] = processedServers.map((server) => ({

View file

@ -123,7 +123,8 @@ export function makeCinemaOSHexaEmbed(id: string, rank: number = 100) {
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(directUrl, headers),
playlist: createM3U8ProxyUrl(directUrl, ctx.features, headers),
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -48,7 +48,8 @@ export const madplayBaseEmbed = makeEmbed({
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(stream.file, headers),
playlist: createM3U8ProxyUrl(stream.file, ctx.features, headers),
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},
@ -92,7 +93,8 @@ export const madplayNsapiEmbed = makeEmbed({
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(stream.url, stream.headers || headers),
playlist: createM3U8ProxyUrl(stream.url, ctx.features, stream.headers || headers),
headers: stream.headers || headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},
@ -136,7 +138,8 @@ export const madplayRoperEmbed = makeEmbed({
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(stream.url, stream.headers || headers),
playlist: createM3U8ProxyUrl(stream.url, ctx.features, stream.headers || headers),
headers: stream.headers || headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},
@ -180,7 +183,8 @@ export const madplayNsapiVidFastEmbed = makeEmbed({
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(stream.url, stream.headers || headers),
playlist: createM3U8ProxyUrl(stream.url, ctx.features, stream.headers || headers),
headers: stream.headers || headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -33,9 +33,12 @@ export const myanimedubScraper = makeEmbed({
{
id: 'dub',
type: 'hls',
playlist: createM3U8ProxyUrl(streamData.results.streamingLink.link.file, {
playlist: createM3U8ProxyUrl(streamData.results.streamingLink.link.file, ctx.features, {
Referer: 'https://rapid-cloud.co/',
}),
headers: {
Referer: 'https://rapid-cloud.co/',
},
flags: [flags.CORS_ALLOWED],
captions:
(streamData.results.streamingLink.tracks

View file

@ -33,9 +33,12 @@ export const myanimesubScraper = makeEmbed({
{
id: 'sub',
type: 'hls',
playlist: createM3U8ProxyUrl(streamData.results.streamingLink.link.file, {
playlist: createM3U8ProxyUrl(streamData.results.streamingLink.link.file, ctx.features, {
Referer: 'https://rapid-cloud.co/',
}),
headers: {
Referer: 'https://rapid-cloud.co/',
},
flags: [flags.CORS_ALLOWED],
captions:
(streamData.results.streamingLink.tracks

View file

@ -224,7 +224,8 @@ function embed(provider: { id: string; name: string; rank: number }) {
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(videoUrl, videoHeaders),
playlist: createM3U8ProxyUrl(videoUrl, ctx.features, videoHeaders),
headers: videoHeaders,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -94,7 +94,8 @@ export const turbovidScraper = makeEmbed({
{
type: 'hls',
id: 'primary',
playlist: createM3U8ProxyUrl(playlist, streamHeaders),
playlist: createM3U8ProxyUrl(playlist, ctx.features, streamHeaders),
headers: streamHeaders,
flags: [],
captions: [],
},

View file

@ -132,7 +132,7 @@ export function makeVidifyEmbed(id: string, rank: number = 100) {
playlist = decodeURIComponent(playlistUrl);
} else {
console.log(`Found normal stream: `, playlistUrl);
playlist = createM3U8ProxyUrl(decodeURIComponent(playlistUrl), streamHeaders);
playlist = createM3U8ProxyUrl(decodeURIComponent(playlistUrl), ctx.features, streamHeaders);
}
ctx.progress(100);

View file

@ -19,7 +19,8 @@ export const vidnestHollymoviehdEmbed = makeEmbed({
streams.push({
id: `hollymoviehd-${source.label}`,
type: 'hls',
playlist: createM3U8ProxyUrl(source.file),
playlist: createM3U8ProxyUrl(source.file, ctx.features),
headers: {},
flags: [flags.CORS_ALLOWED],
captions: [],
} as HlsBasedStream);

View file

@ -37,7 +37,8 @@ function makeVidSrcEmbed(provider: { id: string; name: string; rank: number }) {
{
id: 'primary',
type: 'hls',
playlist: createM3U8ProxyUrl(ctx.url, headers),
playlist: createM3U8ProxyUrl(ctx.url, ctx.features, headers),
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -34,7 +34,8 @@ export const viperScraper = makeEmbed({
{
type: 'hls',
id: 'primary',
playlist: createM3U8ProxyUrl(playlistUrl, headers),
playlist: createM3U8ProxyUrl(playlistUrl, ctx.features, headers),
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -17,6 +17,8 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis
if (!apiRes.videoSource) throw new NotFoundError('No watchable item found');
let processedUrl = apiRes.videoSource;
let streamHeaders: Record<string, string> = {};
if (processedUrl.includes('orbitproxy')) {
try {
const urlParts = processedUrl.split(/orbitproxy\.[^/]+\//);
@ -31,8 +33,8 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis
const originalUrl = jsonData.u;
const referer = jsonData.r || '';
const headers = { referer };
processedUrl = createM3U8ProxyUrl(originalUrl, headers);
streamHeaders = { referer };
processedUrl = createM3U8ProxyUrl(originalUrl, ctx.features, streamHeaders);
} catch (jsonError) {
console.error('Error decoding/parsing orbitproxy data:', jsonError);
}
@ -54,6 +56,7 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis
captions: [],
playlist: processedUrl,
type: 'hls',
headers: streamHeaders,
flags: [flags.CORS_ALLOWED],
},
],

View file

@ -117,7 +117,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
cookie: makeCookieHeader({ hd: 'on' }),
};
const playlist = createM3U8ProxyUrl(`${baseUrl}${autoFile}`, headers);
const playlist = createM3U8ProxyUrl(`${baseUrl}${autoFile}`, ctx.features, headers);
ctx.progress(90);
return {
@ -127,6 +127,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
id: 'primary',
playlist,
type: 'hls',
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -114,7 +114,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
cookie: makeCookieHeader({ hd: 'on' }),
};
const playlist = createM3U8ProxyUrl(`${baseUrl}${autoFile}`, headers);
const playlist = createM3U8ProxyUrl(`${baseUrl}${autoFile}`, ctx.features, headers);
ctx.progress(90);
return {
@ -124,6 +124,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
id: 'primary',
playlist,
type: 'hls',
headers,
flags: [flags.CORS_ALLOWED],
captions: [],
},

View file

@ -31,6 +31,7 @@ export async function scrapeInvidualSource(
const contextBase: ScrapeContext = {
fetcher: ops.fetcher,
proxiedFetcher: ops.proxiedFetcher,
features: ops.features,
progress(val) {
ops.events?.update?.({
id: sourceScraper.id,
@ -107,6 +108,7 @@ export async function scrapeIndividualEmbed(
const output = await embedScraper.scrape({
fetcher: ops.fetcher,
proxiedFetcher: ops.proxiedFetcher,
features: ops.features,
url,
progress(val) {
ops.events?.update?.({

View file

@ -52,6 +52,7 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt
const contextBase: ScrapeContext = {
fetcher: ops.fetcher,
proxiedFetcher: ops.proxiedFetcher,
features: ops.features,
progress(val) {
ops.events?.update?.({
id: lastId,

View file

@ -1,10 +1,12 @@
import { MovieMedia, ShowMedia } from '@/entrypoint/utils/media';
import { FeatureMap } from '@/entrypoint/utils/targets';
import { UseableFetcher } from '@/fetchers/types';
export type ScrapeContext = {
proxiedFetcher: UseableFetcher;
fetcher: UseableFetcher;
progress(val: number): void;
features: FeatureMap;
};
export type EmbedInput = {

View file

@ -1,4 +1,4 @@
import { flags } from '@/entrypoint/utils/targets';
import { FeatureMap, flags } from '@/entrypoint/utils/targets';
import { Stream } from '@/providers/streams';
// Default proxy URL for general purpose proxying
@ -67,10 +67,18 @@ export function setupProxy(stream: Stream): Stream {
/**
* Creates a proxied M3U8 URL using the configured M3U8 proxy
* @param url - The original M3U8 URL to proxy
* @param features - Feature map to determine if local proxy (extension/native) is available
* @param headers - Headers to include with the request
* @returns The proxied M3U8 URL
* @returns The proxied M3U8 URL or original URL if local proxy is available
*/
export function createM3U8ProxyUrl(url: string, headers: Record<string, string> = {}): string {
export function createM3U8ProxyUrl(url: string, features?: FeatureMap, headers: Record<string, string> = {}): string {
// If we have features and local proxy is available (no CORS restrictions), return original URL
// The stream headers will handle the proxying through the extension/native environment
if (features && !features.requires.includes(flags.CORS_ALLOWED)) {
return url;
}
// Otherwise, use the external M3U8 proxy
const encodedUrl = encodeURIComponent(url);
const encodedHeaders = encodeURIComponent(JSON.stringify(headers));
return `${CONFIGURED_M3U8_PROXY_URL}/m3u8-proxy?url=${encodedUrl}${headers ? `&headers=${encodedHeaders}` : ''}`;