organisation & minor fixes
This commit is contained in:
45
src/api/proxy/proxy.controller.ts
Normal file
45
src/api/proxy/proxy.controller.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { proxyRequest, processM3U8Content, streamToReadable } from './proxy.service';
|
||||
import { ProxyRequest } from '../types';
|
||||
|
||||
export async function handleProxy(req: ProxyRequest, reply: FastifyReply) {
|
||||
const { url, referer, origin, userAgent } = req.query;
|
||||
|
||||
if (!url) {
|
||||
return reply.code(400).send({ error: "No URL provided" });
|
||||
}
|
||||
|
||||
try {
|
||||
const { response, contentType, isM3U8 } = await proxyRequest(url, {
|
||||
referer,
|
||||
origin,
|
||||
userAgent
|
||||
});
|
||||
|
||||
reply.header('Access-Control-Allow-Origin', '*');
|
||||
reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||
|
||||
if (contentType) {
|
||||
reply.header('Content-Type', contentType);
|
||||
}
|
||||
|
||||
if (isM3U8) {
|
||||
const text = await response.text();
|
||||
const baseUrl = new URL(response.url);
|
||||
|
||||
const processed = processM3U8Content(text, baseUrl, {
|
||||
referer,
|
||||
origin,
|
||||
userAgent
|
||||
});
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
return reply.send(streamToReadable(response.body!));
|
||||
|
||||
} catch (err) {
|
||||
req.server.log.error(err);
|
||||
return reply.code(500).send({ error: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
8
src/api/proxy/proxy.routes.ts
Normal file
8
src/api/proxy/proxy.routes.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import { handleProxy } from './proxy.controller';
|
||||
|
||||
async function proxyRoutes(fastify: FastifyInstance) {
|
||||
fastify.get('/proxy', handleProxy);
|
||||
}
|
||||
|
||||
export default proxyRoutes;
|
||||
68
src/api/proxy/proxy.service.ts
Normal file
68
src/api/proxy/proxy.service.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Readable } from 'stream';
|
||||
|
||||
interface ProxyHeaders {
|
||||
referer?: string;
|
||||
origin?: string;
|
||||
userAgent?: string;
|
||||
}
|
||||
|
||||
interface ProxyResponse {
|
||||
response: Response;
|
||||
contentType: string | null;
|
||||
isM3U8: boolean;
|
||||
}
|
||||
|
||||
export async function proxyRequest(url: string, { referer, origin, userAgent }: ProxyHeaders): Promise<ProxyResponse> {
|
||||
const headers: Record<string, string> = {
|
||||
'User-Agent': userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
'Accept': '*/*',
|
||||
'Accept-Language': 'en-US,en;q=0.9'
|
||||
};
|
||||
|
||||
if (referer) headers['Referer'] = referer;
|
||||
if (origin) headers['Origin'] = origin;
|
||||
|
||||
const response = await fetch(url, { headers, redirect: 'follow' });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Proxy Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
const isM3U8 = (contentType && contentType.includes('mpegurl')) || url.includes('.m3u8');
|
||||
|
||||
return {
|
||||
response,
|
||||
contentType,
|
||||
isM3U8
|
||||
};
|
||||
}
|
||||
|
||||
export function processM3U8Content(
|
||||
text: string,
|
||||
baseUrl: URL,
|
||||
{ referer, origin, userAgent }: ProxyHeaders
|
||||
): string {
|
||||
return text.replace(/^(?!#)(?!\s*$).+/gm, (line) => {
|
||||
line = line.trim();
|
||||
let absoluteUrl: string;
|
||||
|
||||
try {
|
||||
absoluteUrl = new URL(line, baseUrl).href;
|
||||
} catch (e) {
|
||||
return line;
|
||||
}
|
||||
|
||||
const proxyParams = new URLSearchParams();
|
||||
proxyParams.set('url', absoluteUrl);
|
||||
if (referer) proxyParams.set('referer', referer);
|
||||
if (origin) proxyParams.set('origin', origin);
|
||||
if (userAgent) proxyParams.set('userAgent', userAgent);
|
||||
|
||||
return `/api/proxy?${proxyParams.toString()}`;
|
||||
});
|
||||
}
|
||||
|
||||
export function streamToReadable(webStream: ReadableStream): Readable {
|
||||
return Readable.fromWeb(webStream as any);
|
||||
}
|
||||
Reference in New Issue
Block a user