Spaces:
Running
Running
Upload server.js
Browse files
server.js
CHANGED
|
@@ -60,23 +60,36 @@ function readBody(req) {
|
|
| 60 |
}
|
| 61 |
|
| 62 |
async function fetchRaw(url, headers = {}) {
|
| 63 |
-
let retries =
|
| 64 |
while (retries > 0) {
|
| 65 |
try {
|
| 66 |
return await new Promise((resolve, reject) => {
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
const mod = pu.protocol === 'https:' ? https : http;
|
| 69 |
const req = mod.get(url, {
|
| 70 |
headers: {
|
| 71 |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
|
|
|
|
|
| 72 |
'connection': 'close',
|
| 73 |
...headers
|
| 74 |
},
|
| 75 |
-
timeout:
|
| 76 |
}, (res) => {
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
});
|
| 81 |
req.on('error', reject);
|
| 82 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
@@ -85,7 +98,7 @@ async function fetchRaw(url, headers = {}) {
|
|
| 85 |
console.log(`[fetchRaw] error for ${url}: ${e.message}, retries left: ${retries - 1}`);
|
| 86 |
retries--;
|
| 87 |
if (retries === 0) throw e;
|
| 88 |
-
await new Promise(r => setTimeout(r,
|
| 89 |
}
|
| 90 |
}
|
| 91 |
}
|
|
@@ -392,10 +405,14 @@ function handleHls(req, res) {
|
|
| 392 |
port: pu.port || (pu.protocol === 'https:' ? 443 : 80),
|
| 393 |
path: pu.pathname + pu.search,
|
| 394 |
headers: {
|
| 395 |
-
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
| 396 |
'accept': '*/*',
|
| 397 |
-
'
|
| 398 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
},
|
| 400 |
};
|
| 401 |
if (req.headers['range']) opts.headers['range'] = req.headers['range'];
|
|
@@ -466,12 +483,18 @@ const server = http.createServer(async (req, res) => {
|
|
| 466 |
if (!embedUrl || !embedUrl.includes('kinoserial')) {
|
| 467 |
jsonResp(res, { ok: false, error: 'invalid url' }, 400); return;
|
| 468 |
}
|
|
|
|
| 469 |
fetchRaw(embedUrl, {
|
| 470 |
-
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
| 471 |
'referer': 'https://veoveo.ru/',
|
| 472 |
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
| 473 |
-
'
|
|
|
|
|
|
|
| 474 |
}).then(text => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
res.writeHead(200, {
|
| 476 |
'Access-Control-Allow-Origin': '*',
|
| 477 |
'Content-Type': 'text/html; charset=utf-8'
|
|
@@ -487,10 +510,13 @@ const server = http.createServer(async (req, res) => {
|
|
| 487 |
if (pathname === '/veoveo/balancer') {
|
| 488 |
const contentId = query.id;
|
| 489 |
if (!contentId) { jsonResp(res, { ok: false, error: 'no id' }, 400); return; }
|
|
|
|
| 490 |
fetchRaw('https://api.rstprgapipt.com/balancer-api/proxy/playlists/catalog-api/episodes?content-id=' + contentId, {
|
| 491 |
'referer': 'https://veoveo.ru/',
|
| 492 |
-
'
|
| 493 |
-
'
|
|
|
|
|
|
|
| 494 |
}).then(text => {
|
| 495 |
res.writeHead(200, {
|
| 496 |
'Access-Control-Allow-Origin': '*',
|
|
|
|
| 60 |
}
|
| 61 |
|
| 62 |
async function fetchRaw(url, headers = {}) {
|
| 63 |
+
let retries = 3;
|
| 64 |
while (retries > 0) {
|
| 65 |
try {
|
| 66 |
return await new Promise((resolve, reject) => {
|
| 67 |
+
let pu;
|
| 68 |
+
try {
|
| 69 |
+
pu = new URL(url);
|
| 70 |
+
} catch (e) {
|
| 71 |
+
return reject(new Error('Invalid URL: ' + url));
|
| 72 |
+
}
|
| 73 |
const mod = pu.protocol === 'https:' ? https : http;
|
| 74 |
const req = mod.get(url, {
|
| 75 |
headers: {
|
| 76 |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
| 77 |
+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
| 78 |
+
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
|
| 79 |
'connection': 'close',
|
| 80 |
...headers
|
| 81 |
},
|
| 82 |
+
timeout: 15000
|
| 83 |
}, (res) => {
|
| 84 |
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
| 85 |
+
let loc = res.headers.location;
|
| 86 |
+
if (!loc.startsWith('http')) loc = new URL(loc, url).href;
|
| 87 |
+
resolve(fetchRaw(loc, headers));
|
| 88 |
+
return;
|
| 89 |
+
}
|
| 90 |
+
let data = [];
|
| 91 |
+
res.on('data', (chunk) => data.push(chunk));
|
| 92 |
+
res.on('end', () => resolve(Buffer.concat(data).toString('utf8')));
|
| 93 |
});
|
| 94 |
req.on('error', reject);
|
| 95 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
|
|
| 98 |
console.log(`[fetchRaw] error for ${url}: ${e.message}, retries left: ${retries - 1}`);
|
| 99 |
retries--;
|
| 100 |
if (retries === 0) throw e;
|
| 101 |
+
await new Promise(r => setTimeout(r, 1500));
|
| 102 |
}
|
| 103 |
}
|
| 104 |
}
|
|
|
|
| 405 |
port: pu.port || (pu.protocol === 'https:' ? 443 : 80),
|
| 406 |
path: pu.pathname + pu.search,
|
| 407 |
headers: {
|
| 408 |
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
| 409 |
'accept': '*/*',
|
| 410 |
+
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
|
| 411 |
+
'origin': isCinemap ? 'https://uakinogo.io' : isObrut ? 'https://kinojump.com' : isAlloha ? 'https://streamalloha.live' : isRstprg ? 'https://tv-1-kinoserial.net' : 'https://streamalloha.live',
|
| 412 |
+
'referer': isCinemap ? 'https://uakinogo.io/' : isObrut ? 'https://kinojump.com/' : isAlloha ? 'https://streamalloha.live/' : isRstprg ? 'https://tv-1-kinoserial.net/' : 'https://streamalloha.live/',
|
| 413 |
+
'sec-fetch-dest': isRstprg ? 'video' : 'empty',
|
| 414 |
+
'sec-fetch-mode': 'cors',
|
| 415 |
+
'sec-fetch-site': 'cross-site',
|
| 416 |
},
|
| 417 |
};
|
| 418 |
if (req.headers['range']) opts.headers['range'] = req.headers['range'];
|
|
|
|
| 483 |
if (!embedUrl || !embedUrl.includes('kinoserial')) {
|
| 484 |
jsonResp(res, { ok: false, error: 'invalid url' }, 400); return;
|
| 485 |
}
|
| 486 |
+
console.log('[veoveo/embed] fetching:', embedUrl);
|
| 487 |
fetchRaw(embedUrl, {
|
|
|
|
| 488 |
'referer': 'https://veoveo.ru/',
|
| 489 |
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
| 490 |
+
'sec-fetch-dest': 'document',
|
| 491 |
+
'sec-fetch-mode': 'navigate',
|
| 492 |
+
'sec-fetch-site': 'cross-site',
|
| 493 |
}).then(text => {
|
| 494 |
+
if (text.includes('id="cf-challenge"') || text.includes('cf-browser-verification')) {
|
| 495 |
+
console.log('[veoveo/embed] Cloudflare challenge detected');
|
| 496 |
+
// Could potentially fallback to puppeteer here if needed
|
| 497 |
+
}
|
| 498 |
res.writeHead(200, {
|
| 499 |
'Access-Control-Allow-Origin': '*',
|
| 500 |
'Content-Type': 'text/html; charset=utf-8'
|
|
|
|
| 510 |
if (pathname === '/veoveo/balancer') {
|
| 511 |
const contentId = query.id;
|
| 512 |
if (!contentId) { jsonResp(res, { ok: false, error: 'no id' }, 400); return; }
|
| 513 |
+
console.log('[veoveo/balancer] contentId:', contentId);
|
| 514 |
fetchRaw('https://api.rstprgapipt.com/balancer-api/proxy/playlists/catalog-api/episodes?content-id=' + contentId, {
|
| 515 |
'referer': 'https://veoveo.ru/',
|
| 516 |
+
'accept': 'application/json, text/plain, */*',
|
| 517 |
+
'sec-fetch-dest': 'empty',
|
| 518 |
+
'sec-fetch-mode': 'cors',
|
| 519 |
+
'sec-fetch-site': 'cross-site',
|
| 520 |
}).then(text => {
|
| 521 |
res.writeHead(200, {
|
| 522 |
'Access-Control-Allow-Origin': '*',
|