Spaces:
Running
Running
Upload server.js
Browse files
server.js
CHANGED
|
@@ -76,7 +76,7 @@ async function fetchRaw(url, 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 |
-
'accept-encoding': 'gzip, deflate, br',
|
| 80 |
'connection': 'close',
|
| 81 |
...headers
|
| 82 |
},
|
|
@@ -88,29 +88,29 @@ async function fetchRaw(url, headers = {}) {
|
|
| 88 |
resolve(fetchRaw(loc, headers));
|
| 89 |
return;
|
| 90 |
}
|
| 91 |
-
let data = [];
|
| 92 |
-
res.on('data', (chunk) => data.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
| 93 |
-
res.on('end', () => {
|
| 94 |
-
try {
|
| 95 |
-
const buf = Buffer.concat(data);
|
| 96 |
-
const enc = String(res.headers['content-encoding'] || '').toLowerCase();
|
| 97 |
-
if (enc.includes('br')) {
|
| 98 |
-
const zlib = require('zlib');
|
| 99 |
-
return resolve(zlib.brotliDecompressSync(buf).toString('utf8'));
|
| 100 |
-
}
|
| 101 |
-
if (enc.includes('gzip')) {
|
| 102 |
-
const zlib = require('zlib');
|
| 103 |
-
return resolve(zlib.gunzipSync(buf).toString('utf8'));
|
| 104 |
-
}
|
| 105 |
-
if (enc.includes('deflate')) {
|
| 106 |
-
const zlib = require('zlib');
|
| 107 |
-
return resolve(zlib.inflateSync(buf).toString('utf8'));
|
| 108 |
-
}
|
| 109 |
-
resolve(buf.toString('utf8'));
|
| 110 |
-
} catch (e) {
|
| 111 |
-
resolve(Buffer.concat(data).toString('utf8'));
|
| 112 |
-
}
|
| 113 |
-
});
|
| 114 |
});
|
| 115 |
req.on('error', reject);
|
| 116 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
@@ -490,17 +490,17 @@ function handleHls(req, res) {
|
|
| 490 |
|
| 491 |
const server = http.createServer(async (req, res) => {
|
| 492 |
const { pathname, query } = parseQuery(req.url);
|
| 493 |
-
const startedAt = Date.now();
|
| 494 |
-
const shouldLog = pathname === '/health' || pathname.startsWith('/uakinogo/') || pathname.startsWith('/alloha/') || pathname.startsWith('/turbo/');
|
| 495 |
-
if (shouldLog) {
|
| 496 |
-
try {
|
| 497 |
-
const q = (pathname === '/uakinogo/search' && query.q) ? (' q=' + String(query.q).slice(0, 120)) : '';
|
| 498 |
-
console.log('[req]', req.method, pathname + q);
|
| 499 |
-
} catch (e) {}
|
| 500 |
-
res.on('finish', () => {
|
| 501 |
-
try { console.log('[res]', req.method, pathname, res.statusCode, (Date.now() - startedAt) + 'ms'); } catch (e) {}
|
| 502 |
-
});
|
| 503 |
-
}
|
| 504 |
|
| 505 |
if (req.method === 'OPTIONS') {
|
| 506 |
res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': '*' });
|
|
@@ -671,58 +671,94 @@ const server = http.createServer(async (req, res) => {
|
|
| 671 |
if (pathname === '/uakinogo/search') {
|
| 672 |
const q = query.q || '';
|
| 673 |
if (!q) { jsonResp(res, { ok: false, error: 'no query' }, 400); return; }
|
| 674 |
-
const
|
| 675 |
-
const
|
|
|
|
|
|
|
| 676 |
const results = [];
|
| 677 |
-
const addResult = (rawUrl, id, slug) => {
|
| 678 |
-
if (!rawUrl || !id) return;
|
| 679 |
-
let url = rawUrl;
|
| 680 |
-
if (url.startsWith('//')) url = 'https:' + url;
|
| 681 |
-
if (url.startsWith('/')) url = 'https://uakinogo.io' + url;
|
| 682 |
-
if (!url.startsWith('http')) return;
|
| 683 |
-
if (!url.includes('uakinogo.io/')) return;
|
| 684 |
-
if (url.includes('do=search') || url.includes('/index.php')) return;
|
| 685 |
-
if (!results.find(r => r.id === id)) results.push({ url, id, slug: slug || '' });
|
| 686 |
-
};
|
| 687 |
-
|
| 688 |
-
const
|
| 689 |
-
const
|
|
|
|
| 690 |
let m;
|
| 691 |
-
while ((m =
|
| 692 |
-
while ((m =
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
if (t
|
| 700 |
-
if (
|
| 701 |
-
if (
|
| 702 |
-
return
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
const
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
await
|
| 719 |
-
results
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 726 |
return;
|
| 727 |
}
|
| 728 |
|
|
|
|
| 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 |
+
'accept-encoding': 'gzip, deflate, br',
|
| 80 |
'connection': 'close',
|
| 81 |
...headers
|
| 82 |
},
|
|
|
|
| 88 |
resolve(fetchRaw(loc, headers));
|
| 89 |
return;
|
| 90 |
}
|
| 91 |
+
let data = [];
|
| 92 |
+
res.on('data', (chunk) => data.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
| 93 |
+
res.on('end', () => {
|
| 94 |
+
try {
|
| 95 |
+
const buf = Buffer.concat(data);
|
| 96 |
+
const enc = String(res.headers['content-encoding'] || '').toLowerCase();
|
| 97 |
+
if (enc.includes('br')) {
|
| 98 |
+
const zlib = require('zlib');
|
| 99 |
+
return resolve(zlib.brotliDecompressSync(buf).toString('utf8'));
|
| 100 |
+
}
|
| 101 |
+
if (enc.includes('gzip')) {
|
| 102 |
+
const zlib = require('zlib');
|
| 103 |
+
return resolve(zlib.gunzipSync(buf).toString('utf8'));
|
| 104 |
+
}
|
| 105 |
+
if (enc.includes('deflate')) {
|
| 106 |
+
const zlib = require('zlib');
|
| 107 |
+
return resolve(zlib.inflateSync(buf).toString('utf8'));
|
| 108 |
+
}
|
| 109 |
+
resolve(buf.toString('utf8'));
|
| 110 |
+
} catch (e) {
|
| 111 |
+
resolve(Buffer.concat(data).toString('utf8'));
|
| 112 |
+
}
|
| 113 |
+
});
|
| 114 |
});
|
| 115 |
req.on('error', reject);
|
| 116 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
|
|
| 490 |
|
| 491 |
const server = http.createServer(async (req, res) => {
|
| 492 |
const { pathname, query } = parseQuery(req.url);
|
| 493 |
+
const startedAt = Date.now();
|
| 494 |
+
const shouldLog = pathname === '/health' || pathname.startsWith('/uakinogo/') || pathname.startsWith('/alloha/') || pathname.startsWith('/turbo/');
|
| 495 |
+
if (shouldLog) {
|
| 496 |
+
try {
|
| 497 |
+
const q = (pathname === '/uakinogo/search' && query.q) ? (' q=' + String(query.q).slice(0, 120)) : '';
|
| 498 |
+
console.log('[req]', req.method, pathname + q);
|
| 499 |
+
} catch (e) {}
|
| 500 |
+
res.on('finish', () => {
|
| 501 |
+
try { console.log('[res]', req.method, pathname, res.statusCode, (Date.now() - startedAt) + 'ms'); } catch (e) {}
|
| 502 |
+
});
|
| 503 |
+
}
|
| 504 |
|
| 505 |
if (req.method === 'OPTIONS') {
|
| 506 |
res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': '*' });
|
|
|
|
| 671 |
if (pathname === '/uakinogo/search') {
|
| 672 |
const q = query.q || '';
|
| 673 |
if (!q) { jsonResp(res, { ok: false, error: 'no query' }, 400); return; }
|
| 674 |
+
const qEnc = encodeURIComponent(q);
|
| 675 |
+
const searchUrlSeo = 'https://uakinogo.io/search/' + qEnc;
|
| 676 |
+
const searchUrlLegacy = 'https://uakinogo.io/index.php?do=search&subaction=search&story=' + qEnc;
|
| 677 |
+
const parseSearchHtml = (html) => {
|
| 678 |
const results = [];
|
| 679 |
+
const addResult = (rawUrl, id, slug) => {
|
| 680 |
+
if (!rawUrl || !id) return;
|
| 681 |
+
let url = rawUrl;
|
| 682 |
+
if (url.startsWith('//')) url = 'https:' + url;
|
| 683 |
+
if (url.startsWith('/')) url = 'https://uakinogo.io' + url;
|
| 684 |
+
if (!url.startsWith('http')) return;
|
| 685 |
+
if (!url.includes('uakinogo.io/')) return;
|
| 686 |
+
if (url.includes('do=search') || url.includes('/index.php')) return;
|
| 687 |
+
if (!results.find(r => r.id === id)) results.push({ url, id, slug: slug || '' });
|
| 688 |
+
};
|
| 689 |
+
|
| 690 |
+
const reHref = /href\s*=\s*["']([^"']*?\/(\d+)-([^"'/]+)\.html(?:#[^"']*)?)["']/g;
|
| 691 |
+
const reEscAbs = /(https?:\\\\\/\\\\\/uakinogo\.io\\\\\/[^"']*?\\\\\/(\d+)-([^"\\\/]+)\.html)/g;
|
| 692 |
+
const reEscRel = /(\\\\\/[^"']*?\\\\\/(\d+)-([^"\\\/]+)\.html)/g;
|
| 693 |
let m;
|
| 694 |
+
while ((m = reHref.exec(html)) !== null) addResult(m[1], m[2], m[3]);
|
| 695 |
+
while ((m = reEscAbs.exec(html)) !== null) addResult(m[1].replace(/\\\\\//g, '/'), m[2], m[3]);
|
| 696 |
+
while ((m = reEscRel.exec(html)) !== null) addResult(m[1].replace(/\\\\\//g, '/'), m[2], m[3]);
|
| 697 |
+
return results;
|
| 698 |
+
};
|
| 699 |
+
|
| 700 |
+
const looksLikeChallenge = (html) => {
|
| 701 |
+
const t = String(html || '');
|
| 702 |
+
if (!t) return true;
|
| 703 |
+
if (t.includes('cf-challenge') || t.includes('cf-browser-verification')) return true;
|
| 704 |
+
if (/Just a moment/i.test(t) && /Cloudflare/i.test(t)) return true;
|
| 705 |
+
if (t.includes('data-sitekey') && t.includes('cf-turnstile')) return true;
|
| 706 |
+
return false;
|
| 707 |
+
};
|
| 708 |
+
|
| 709 |
+
const fetchAndParse = async (url) => {
|
| 710 |
+
const html = await fetchRaw(url, { 'user-agent': 'Mozilla/5.0 Chrome/120', 'referer': 'https://uakinogo.io/' });
|
| 711 |
+
return { html, results: parseSearchHtml(html), challenged: looksLikeChallenge(html) };
|
| 712 |
+
};
|
| 713 |
+
|
| 714 |
+
fetchAndParse(searchUrlSeo).then(async (first) => {
|
| 715 |
+
let html = first.html;
|
| 716 |
+
let results = first.results;
|
| 717 |
+
let challenged = first.challenged;
|
| 718 |
+
|
| 719 |
+
if (challenged || results.length === 0) {
|
| 720 |
+
try {
|
| 721 |
+
const second = await fetchAndParse(searchUrlLegacy);
|
| 722 |
+
if (second.results.length > results.length) {
|
| 723 |
+
html = second.html;
|
| 724 |
+
results = second.results;
|
| 725 |
+
challenged = challenged || second.challenged;
|
| 726 |
+
}
|
| 727 |
+
} catch (e) {}
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
if ((challenged || results.length === 0) && typeof getStreams === 'function' && getStreams.toString) {
|
| 731 |
+
try {
|
| 732 |
+
const { getBrowser } = require('./alloha');
|
| 733 |
+
const browser = await getBrowser();
|
| 734 |
+
const page = await browser.newPage();
|
| 735 |
+
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
|
| 736 |
+
await page.setExtraHTTPHeaders({ 'Accept-Language': 'ru-RU,ru;q=0.9,en;q=0.8', 'Referer': 'https://uakinogo.io/' });
|
| 737 |
+
|
| 738 |
+
await page.goto(searchUrlSeo, { waitUntil: 'networkidle2', timeout: 25000 });
|
| 739 |
+
try { await page.waitForSelector('a[href*=".html"]', { timeout: 8000 }); } catch (e) {}
|
| 740 |
+
await new Promise(r => setTimeout(r, 1200));
|
| 741 |
+
const html2 = await page.content();
|
| 742 |
+
await page.close();
|
| 743 |
+
|
| 744 |
+
const parsed = parseSearchHtml(html2);
|
| 745 |
+
if (parsed.length >= results.length) {
|
| 746 |
+
html = html2;
|
| 747 |
+
results = parsed;
|
| 748 |
+
challenged = looksLikeChallenge(html2);
|
| 749 |
+
}
|
| 750 |
+
} catch (e) {}
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
try { console.log('[uakinogo/search] results:', results.length, results[0] ? results[0].url : 'none'); } catch (e) {}
|
| 754 |
+
if (results.length === 0) {
|
| 755 |
+
try {
|
| 756 |
+
const t = String(html || '');
|
| 757 |
+
console.log('[uakinogo/search] html:', 'len=' + t.length, 'cf=' + (looksLikeChallenge(t) ? '1' : '0'), 'sample=' + t.slice(0, 160).replace(/\s+/g, ' '));
|
| 758 |
+
} catch (e) {}
|
| 759 |
+
}
|
| 760 |
+
jsonResp(res, { ok: true, results: results.slice(0, 10) });
|
| 761 |
+
}).catch(e => jsonResp(res, { ok: false, error: e.message }, 500));
|
| 762 |
return;
|
| 763 |
}
|
| 764 |
|