fix headers

This commit is contained in:
Pas 2025-05-14 17:37:17 -06:00
parent 29d35419d4
commit 28e6e3c632
2 changed files with 126 additions and 111 deletions

View file

@ -3,8 +3,6 @@
* @description Proxies m3u8 files and their segments
*/
import { setResponseHeaders } from 'h3';
// Helper function to parse URLs
function parseURL(req_url: string, baseUrl?: string) {
if (baseUrl) {
@ -42,34 +40,53 @@ function parseURL(req_url: string, baseUrl?: string) {
}
}
/**
* Proxies m3u8 files and replaces the content to point to the proxy
*/
async function proxyM3U8(event: any) {
const url = getQuery(event).url as string;
const headersParam = getQuery(event).headers as string;
if (!url) {
return sendError(event, createError({
statusCode: 400,
statusMessage: 'URL parameter is required'
}));
export default async function(request: Request, env: any, ctx: any) {
// Handle CORS preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': '*',
'Access-Control-Max-Age': '86400',
}
});
}
let headers = {};
// Parse URL parameters
const url = new URL(request.url);
const targetUrl = url.searchParams.get('url');
const headersParam = url.searchParams.get('headers');
if (!targetUrl) {
return new Response('URL parameter is required', { status: 400 });
}
console.log("Processing m3u8 request for URL:", targetUrl);
let customHeaders = {};
try {
headers = headersParam ? JSON.parse(headersParam) : {};
customHeaders = headersParam ? JSON.parse(headersParam) : {};
console.log("With headers:", JSON.stringify(customHeaders));
} catch (e) {
return sendError(event, createError({
statusCode: 400,
statusMessage: 'Invalid headers format'
}));
return new Response('Invalid headers format', { status: 400 });
}
try {
// Use native fetch instead of axios
const response = await fetch(url, {
headers: headers as HeadersInit
// Create base request headers
const requestHeaders = new Headers({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9',
...customHeaders
});
console.log("Making request to:", targetUrl);
console.log("With headers:", JSON.stringify(Object.fromEntries(requestHeaders.entries())));
// Use fetch API which is natively available in Workers
const response = await fetch(targetUrl, {
headers: requestHeaders
});
if (!response.ok) {
@ -79,9 +96,17 @@ async function proxyM3U8(event: any) {
const m3u8Content = await response.text();
// Get the base URL for the host
const host = getRequestHost(event);
const proto = getRequestProtocol(event);
const baseProxyUrl = `${proto}://${host}`;
const host = url.hostname;
const proto = url.protocol;
const baseProxyUrl = `${proto}//${host}`;
const responseHeaders = {
'Content-Type': 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Cache-Control': 'no-cache, no-store, must-revalidate'
};
if (m3u8Content.includes("RESOLUTION=")) {
// This is a master playlist with multiple quality variants
@ -95,7 +120,7 @@ async function proxyM3U8(event: any) {
const regex = /https?:\/\/[^\""\s]+/g;
const keyUrl = regex.exec(line)?.[0];
if (keyUrl) {
const proxyKeyUrl = `${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(keyUrl)}&headers=${encodeURIComponent(JSON.stringify(headers))}`;
const proxyKeyUrl = `${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(keyUrl)}&headers=${encodeURIComponent(JSON.stringify(customHeaders))}`;
newLines.push(line.replace(keyUrl, proxyKeyUrl));
} else {
newLines.push(line);
@ -105,7 +130,7 @@ async function proxyM3U8(event: any) {
const regex = /https?:\/\/[^\""\s]+/g;
const mediaUrl = regex.exec(line)?.[0];
if (mediaUrl) {
const proxyMediaUrl = `${baseProxyUrl}/m3u8-proxy?url=${encodeURIComponent(mediaUrl)}&headers=${encodeURIComponent(JSON.stringify(headers))}`;
const proxyMediaUrl = `${baseProxyUrl}/m3u8-proxy?url=${encodeURIComponent(mediaUrl)}&headers=${encodeURIComponent(JSON.stringify(customHeaders))}`;
newLines.push(line.replace(mediaUrl, proxyMediaUrl));
} else {
newLines.push(line);
@ -115,9 +140,9 @@ async function proxyM3U8(event: any) {
}
} else if (line.trim()) {
// This is a quality variant URL
const variantUrl = parseURL(line, url);
const variantUrl = parseURL(line, targetUrl);
if (variantUrl) {
newLines.push(`${baseProxyUrl}/m3u8-proxy?url=${encodeURIComponent(variantUrl)}&headers=${encodeURIComponent(JSON.stringify(headers))}`);
newLines.push(`${baseProxyUrl}/m3u8-proxy?url=${encodeURIComponent(variantUrl)}&headers=${encodeURIComponent(JSON.stringify(customHeaders))}`);
} else {
newLines.push(line);
}
@ -127,16 +152,9 @@ async function proxyM3U8(event: any) {
}
}
// Set appropriate headers
setResponseHeaders(event, {
'Content-Type': 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Cache-Control': 'no-cache, no-store, must-revalidate'
return new Response(newLines.join("\n"), {
headers: responseHeaders
});
return newLines.join("\n");
} else {
// This is a media playlist with segments
const lines = m3u8Content.split("\n");
@ -149,7 +167,7 @@ async function proxyM3U8(event: any) {
const regex = /https?:\/\/[^\""\s]+/g;
const keyUrl = regex.exec(line)?.[0];
if (keyUrl) {
const proxyKeyUrl = `${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(keyUrl)}&headers=${encodeURIComponent(JSON.stringify(headers))}`;
const proxyKeyUrl = `${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(keyUrl)}&headers=${encodeURIComponent(JSON.stringify(customHeaders))}`;
newLines.push(line.replace(keyUrl, proxyKeyUrl));
} else {
newLines.push(line);
@ -159,9 +177,9 @@ async function proxyM3U8(event: any) {
}
} else if (line.trim() && !line.startsWith("#")) {
// This is a segment URL (.ts file)
const segmentUrl = parseURL(line, url);
const segmentUrl = parseURL(line, targetUrl);
if (segmentUrl) {
newLines.push(`${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(segmentUrl)}&headers=${encodeURIComponent(JSON.stringify(headers))}`);
newLines.push(`${baseProxyUrl}/ts-proxy?url=${encodeURIComponent(segmentUrl)}&headers=${encodeURIComponent(JSON.stringify(customHeaders))}`);
} else {
newLines.push(line);
}
@ -171,29 +189,17 @@ async function proxyM3U8(event: any) {
}
}
// Set appropriate headers
setResponseHeaders(event, {
'Content-Type': 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Cache-Control': 'no-cache, no-store, must-revalidate'
return new Response(newLines.join("\n"), {
headers: responseHeaders
});
return newLines.join("\n");
}
} catch (error: any) {
console.error('Error proxying M3U8:', error);
return sendError(event, createError({
statusCode: 500,
statusMessage: error.message || 'Error proxying M3U8 file'
}));
return new Response(error.message || 'Error proxying M3U8 file', {
status: 500,
headers: {
'Access-Control-Allow-Origin': '*'
}
});
}
}
export default defineEventHandler(async (event) => {
// Handle CORS preflight requests
if (isPreflightRequest(event)) return handleCors(event, {});
return await proxyM3U8(event);
});
}

View file

@ -3,64 +3,73 @@
* @description Proxies TS (transport stream) files
*/
import { setResponseHeaders } from 'h3';
/**
* Proxies TS (transport stream) files
*/
export default defineEventHandler(async (event) => {
export default async function(request: Request, env: any, ctx: any) {
// Handle CORS preflight requests
if (isPreflightRequest(event)) return handleCors(event, {});
const url = getQuery(event).url as string;
const headersParam = getQuery(event).headers as string;
if (!url) {
return sendError(event, createError({
statusCode: 400,
statusMessage: 'URL parameter is required'
}));
}
let headers = {};
try {
headers = headersParam ? JSON.parse(headersParam) : {};
} catch (e) {
return sendError(event, createError({
statusCode: 400,
statusMessage: 'Invalid headers format'
}));
}
try {
const response = await fetch(url, {
method: 'GET',
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
...headers as HeadersInit,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': '*',
'Access-Control-Max-Age': '86400',
}
});
}
// Parse URL parameters
const url = new URL(request.url);
const targetUrl = url.searchParams.get('url');
const headersParam = url.searchParams.get('headers');
if (!targetUrl) {
return new Response('URL parameter is required', { status: 400 });
}
let customHeaders = {};
try {
customHeaders = headersParam ? JSON.parse(headersParam) : {};
} catch (e) {
return new Response('Invalid headers format', { status: 400 });
}
try {
// Create request headers
const requestHeaders = new Headers({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9',
...customHeaders
});
const response = await fetch(targetUrl, {
method: 'GET',
headers: requestHeaders
});
if (!response.ok) {
throw new Error(`Failed to fetch TS file: ${response.status} ${response.statusText}`);
}
// Set appropriate headers for each video segment
setResponseHeaders(event, {
'Content-Type': 'video/mp2t',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Cache-Control': 'public, max-age=3600' // Allow caching of TS segments
});
// Get response as arrayBuffer
const data = await response.arrayBuffer();
// Return the binary data directly - Nitro should handle this properly
return new Uint8Array(await response.arrayBuffer());
// Return the response with appropriate headers
return new Response(data, {
headers: {
'Content-Type': 'video/mp2t',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Cache-Control': 'public, max-age=3600' // Allow caching of TS segments
}
});
} catch (error: any) {
console.error('Error proxying TS file:', error);
return sendError(event, createError({
statusCode: error.response?.status || 500,
statusMessage: error.message || 'Error proxying TS file'
}));
return new Response(error.message || 'Error proxying TS file', {
status: 500,
headers: {
'Access-Control-Allow-Origin': '*'
}
});
}
});
}