Fixed bundling into an exe not working as intended.

This commit is contained in:
2025-12-14 13:16:35 -05:00
parent 76c9eb38f6
commit 5d8441bf27
124 changed files with 279314 additions and 103 deletions

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleProxy = handleProxy;
const proxy_service_1 = require("./proxy.service");
async function handleProxy(req, reply) {
const { url, referer, origin, userAgent } = req.query;
if (!url) {
return reply.code(400).send({ error: "No URL provided" });
}
try {
const { response, contentType, isM3U8, contentLength } = await (0, proxy_service_1.proxyRequest)(url, {
referer,
origin,
userAgent
});
reply.header('Access-Control-Allow-Origin', '*');
reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
reply.header('Access-Control-Allow-Headers', 'Content-Type, Range');
reply.header('Access-Control-Expose-Headers', 'Content-Length, Content-Range, Accept-Ranges');
if (contentType) {
reply.header('Content-Type', contentType);
}
if (contentLength) {
reply.header('Content-Length', contentLength);
}
if (contentType?.startsWith('image/') || contentType?.startsWith('video/')) {
reply.header('Cache-Control', 'public, max-age=31536000, immutable');
}
reply.header('Accept-Ranges', 'bytes');
if (isM3U8) {
const text = await response.text();
const baseUrl = new URL(response.url);
const processedContent = (0, proxy_service_1.processM3U8Content)(text, baseUrl, {
referer,
origin,
userAgent
});
return reply.send(processedContent);
}
return reply.send((0, proxy_service_1.streamToReadable)(response.body));
}
catch (err) {
req.server.log.error(err);
if (!reply.sent) {
return reply.code(500).send({ error: "Internal Server Error" });
}
}
}

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const proxy_controller_1 = require("./proxy.controller");
async function proxyRoutes(fastify) {
fastify.get('/proxy', proxy_controller_1.handleProxy);
}
exports.default = proxyRoutes;

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.proxyRequest = proxyRequest;
exports.processM3U8Content = processM3U8Content;
exports.streamToReadable = streamToReadable;
const stream_1 = require("stream");
async function proxyRequest(url, { referer, origin, userAgent }) {
const headers = {
'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',
'Accept-Encoding': 'identity',
'Connection': 'keep-alive'
};
if (referer)
headers['Referer'] = referer;
if (origin)
headers['Origin'] = origin;
let lastError = null;
const maxRetries = 2;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 60000);
const response = await fetch(url, {
headers,
redirect: 'follow',
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
if (response.status === 404 || response.status === 403) {
throw new Error(`Proxy Error: ${response.status} ${response.statusText}`);
}
if (attempt < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 500));
continue;
}
throw new Error(`Proxy Error: ${response.status} ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
const contentLength = response.headers.get('content-length');
const isM3U8 = (contentType && contentType.includes('mpegurl')) || url.includes('.m3u8');
return {
response,
contentType,
isM3U8,
contentLength
};
}
catch (error) {
lastError = error;
if (attempt === maxRetries - 1) {
throw lastError;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
throw lastError || new Error('Unknown error in proxyRequest');
}
function processM3U8Content(text, baseUrl, { referer, origin, userAgent }) {
return text.replace(/^(?!#)(?!\s*$).+/gm, (line) => {
line = line.trim();
let absoluteUrl;
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()}`;
});
}
function streamToReadable(webStream) {
const reader = webStream.getReader();
let readTimeout;
return new stream_1.Readable({
async read() {
try {
const timeoutPromise = new Promise((_, reject) => {
readTimeout = setTimeout(() => reject(new Error('Stream read timeout')), 10000);
});
const readPromise = reader.read();
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
clearTimeout(readTimeout);
if (done) {
this.push(null);
}
else {
this.push(Buffer.from(value));
}
}
catch (error) {
clearTimeout(readTimeout);
this.destroy(error);
}
},
destroy(error, callback) {
clearTimeout(readTimeout);
reader.cancel().then(() => callback(error)).catch(callback);
}
});
}