-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.mjs
More file actions
94 lines (84 loc) · 3.27 KB
/
Copy pathserver.mjs
File metadata and controls
94 lines (84 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import http from 'node:http';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { openSession, serveMaster, servePlaylist, pipeCdn } from './lib/core/proxy.mjs';
const ROOT = path.dirname(fileURLToPath(import.meta.url));
const PUBLIC = path.join(ROOT, 'public');
const PORT = Number(process.env.PORT) || 3000;
const CORS = { 'Access-Control-Allow-Origin': '*' };
function requestOrigin(req) {
const host = req.headers.host || `localhost:${PORT}`;
return `${req.headers['x-forwarded-proto'] || 'http'}://${host}`;
}
const indexHtml = await readFile(path.join(PUBLIC, 'index.html'));
const staticFiles = new Map([
['/', { type: 'text/html; charset=utf-8', body: indexHtml }],
['/index.html', { type: 'text/html; charset=utf-8', body: indexHtml }],
['/ui.css', { type: 'text/css; charset=utf-8', body: await readFile(path.join(PUBLIC, 'ui.css')) }],
['/ui.js', { type: 'application/javascript; charset=utf-8', body: await readFile(path.join(PUBLIC, 'ui.js')) }],
]);
const server = http.createServer(async (req, res) => {
try {
if (req.method !== 'GET') {
res.writeHead(404);
res.end('not found');
return;
}
const origin = requestOrigin(req);
const url = new URL(req.url, origin);
const staticFile = staticFiles.get(url.pathname);
if (staticFile) {
res.writeHead(200, { 'Content-Type': staticFile.type });
res.end(staticFile.body);
return;
}
if (url.pathname === '/api/resolve') {
const watchUrl = url.searchParams.get('url');
if (!watchUrl) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'missing url' }));
return;
}
const session = await openSession(watchUrl);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
watchUrl: session.watchUrl,
playUrl: `${origin}/play/${session.playId}/master.m3u8`,
signedMasterUrl: session.signedMasterUrl,
videoId: session.videoId,
sessionId: session.sessionId,
fingerprint: session.fingerprint,
headers: session.headers,
}));
return;
}
const masterMatch = url.pathname.match(/^\/play\/([^/]+)\/master\.m3u8$/);
if (masterMatch) {
const body = await serveMaster(masterMatch[1], origin);
res.writeHead(200, { 'Content-Type': 'application/vnd.apple.mpegurl', ...CORS });
res.end(body);
return;
}
const cdnMatch = url.pathname.match(/^\/play\/([^/]+)\/x\/([^/]+)(\/.*)$/);
if (cdnMatch) {
const cdnUrl = `https://${cdnMatch[2]}${cdnMatch[3]}${url.search}`;
if (cdnUrl.includes('.m3u8')) {
const { contentType, buffer } = await servePlaylist(cdnMatch[1], origin, cdnUrl);
res.writeHead(200, { 'Content-Type': contentType, ...CORS });
res.end(buffer);
return;
}
await pipeCdn(cdnMatch[1], cdnUrl, res);
return;
}
res.writeHead(404);
res.end('not found');
} catch (err) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: err.message || String(err) }));
}
});
server.listen(PORT, () => {
console.error(`anime-nexus hls stream resolver http://localhost:${PORT}`);
});