取消了后台的music-dl.sayqz代理
This commit is contained in:
@@ -17,39 +17,39 @@ if (!fs.existsSync(MUSIC_DIR)) {
|
||||
fs.mkdirSync(MUSIC_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// CORS headers
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
const pathname = parsedUrl.pathname;
|
||||
|
||||
// --- Music API Proxy ---
|
||||
// Proxy requests to music-dl.sayqz.com to avoid CORS issues
|
||||
if (pathname === '/music-api' || pathname === '/music-api/') {
|
||||
const queryString = parsedUrl.search || '';
|
||||
const targetUrl = `https://music-dl.sayqz.com/api/${queryString}`;
|
||||
|
||||
proxyRequest(targetUrl, req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Path format: /kv/:key?token=...
|
||||
const match = pathname.match(/^\/kv\/([a-zA-Z0-9_-]+)$/);
|
||||
|
||||
if (!match) {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Invalid path' }));
|
||||
return;
|
||||
}
|
||||
const server = http.createServer((req, res) => {
|
||||
// CORS headers
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
const pathname = parsedUrl.pathname;
|
||||
|
||||
// --- Music API Proxy ---
|
||||
// Proxy requests to music-dl.sayqz.com to avoid CORS issues
|
||||
// if (pathname === '/music-api' || pathname === '/music-api/') {
|
||||
// const queryString = parsedUrl.search || '';
|
||||
// const targetUrl = `https://music-dl.sayqz.com/api/${queryString}`;
|
||||
|
||||
// proxyRequest(targetUrl, req, res);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Path format: /kv/:key?token=...
|
||||
const match = pathname.match(/^\/kv\/([a-zA-Z0-9_-]+)$/);
|
||||
|
||||
if (!match) {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Invalid path' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const key = match[1];
|
||||
const token = parsedUrl.query.token;
|
||||
@@ -308,224 +308,224 @@ function downloadFile(url, dest) {
|
||||
});
|
||||
}
|
||||
|
||||
// --- Music API Proxy Function ---
|
||||
function proxyRequest(targetUrl, req, res) {
|
||||
const parsedTarget = url.parse(targetUrl, true);
|
||||
const requestType = parsedTarget.query && parsedTarget.query.type;
|
||||
const isPicRequest = requestType === 'pic';
|
||||
const isUrlRequest = requestType === 'url';
|
||||
const maxRedirects = 5;
|
||||
|
||||
const buildHeaders = () => {
|
||||
const headers = {
|
||||
'User-Agent': '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-Encoding': 'identity'
|
||||
};
|
||||
if (req.headers.range) headers['Range'] = req.headers.range;
|
||||
if (req.headers['if-range']) headers['If-Range'] = req.headers['if-range'];
|
||||
return headers;
|
||||
};
|
||||
|
||||
const requestStream = (nextUrl, redirectCount = 0) => {
|
||||
if (redirectCount > maxRedirects) {
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Too many redirects' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedNext = url.parse(nextUrl);
|
||||
const isHttps = parsedNext.protocol === 'https:';
|
||||
const requestModule = isHttps ? https : http;
|
||||
const options = {
|
||||
hostname: parsedNext.hostname,
|
||||
port: parsedNext.port || (isHttps ? 443 : 80),
|
||||
path: parsedNext.path,
|
||||
method: req.method,
|
||||
headers: buildHeaders()
|
||||
};
|
||||
|
||||
const proxyReq = requestModule.request(options, (proxyRes) => {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
const resolved = url.resolve(nextUrl, proxyRes.headers.location);
|
||||
proxyRes.resume();
|
||||
requestStream(resolved, redirectCount + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
res.writeHead(proxyRes.statusCode || 200, headers);
|
||||
proxyRes.pipe(res);
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Stream error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy stream failed', message: e.message }));
|
||||
});
|
||||
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
};
|
||||
|
||||
const options = {
|
||||
hostname: parsedTarget.hostname,
|
||||
port: 443,
|
||||
path: parsedTarget.path,
|
||||
method: req.method,
|
||||
headers: buildHeaders()
|
||||
};
|
||||
|
||||
if (isUrlRequest) {
|
||||
const proxyReq = https.request(options, (proxyRes) => {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
const resolved = url.resolve(targetUrl, proxyRes.headers.location);
|
||||
proxyRes.resume();
|
||||
requestStream(resolved, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = proxyRes.headers['content-type'] || '';
|
||||
const shouldInspectBody = contentType.includes('application/json') || contentType.startsWith('text/');
|
||||
|
||||
if (shouldInspectBody) {
|
||||
let body = '';
|
||||
proxyRes.setEncoding('utf8');
|
||||
proxyRes.on('data', chunk => body += chunk);
|
||||
proxyRes.on('end', () => {
|
||||
try {
|
||||
const parsedBody = JSON.parse(body);
|
||||
const extractUrl = (payload) => {
|
||||
if (!payload || typeof payload !== 'object') return null;
|
||||
if (typeof payload.url === 'string') return payload.url;
|
||||
if (typeof payload.data === 'string') return payload.data;
|
||||
if (payload.data && typeof payload.data === 'object') {
|
||||
if (typeof payload.data.url === 'string') return payload.data.url;
|
||||
if (typeof payload.data.link === 'string') return payload.data.link;
|
||||
if (payload.data.data && typeof payload.data.data.url === 'string') return payload.data.data.url;
|
||||
if (Array.isArray(payload.data) && payload.data[0] && typeof payload.data[0].url === 'string') {
|
||||
return payload.data[0].url;
|
||||
}
|
||||
}
|
||||
if (payload.result && typeof payload.result.url === 'string') return payload.result.url;
|
||||
return null;
|
||||
};
|
||||
const resolvedUrl = extractUrl(parsedBody);
|
||||
if (resolvedUrl) {
|
||||
requestStream(url.resolve(targetUrl, resolvedUrl), 1);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through to return original body
|
||||
}
|
||||
|
||||
res.writeHead(proxyRes.statusCode || 200, {
|
||||
'Content-Type': proxyRes.headers['content-type'] || 'application/json'
|
||||
});
|
||||
res.end(body);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
res.writeHead(proxyRes.statusCode || 200, headers);
|
||||
proxyRes.pipe(res);
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Request error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy request failed', message: e.message }));
|
||||
});
|
||||
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyReq = https.request(options, (proxyRes) => {
|
||||
const passThrough = () => {
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
|
||||
res.writeHead(proxyRes.statusCode, headers);
|
||||
proxyRes.pipe(res);
|
||||
};
|
||||
|
||||
// pic endpoint now returns JSON, turn it into a redirect for <img>
|
||||
if (isPicRequest) {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
res.writeHead(302, { Location: proxyRes.headers.location });
|
||||
proxyRes.resume();
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = proxyRes.headers['content-type'] || '';
|
||||
if (contentType.startsWith('image/')) {
|
||||
passThrough();
|
||||
return;
|
||||
}
|
||||
|
||||
let body = '';
|
||||
proxyRes.setEncoding('utf8');
|
||||
proxyRes.on('data', chunk => body += chunk);
|
||||
proxyRes.on('end', () => {
|
||||
try {
|
||||
const parsedBody = JSON.parse(body);
|
||||
if (parsedBody && parsedBody.url) {
|
||||
res.writeHead(302, { Location: parsedBody.url });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
res.writeHead(proxyRes.statusCode || 200, {
|
||||
'Content-Type': proxyRes.headers['content-type'] || 'application/json'
|
||||
});
|
||||
res.end(body);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle redirects
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
// For redirects, return the redirect URL to client
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ url: proxyRes.headers.location }));
|
||||
return;
|
||||
}
|
||||
|
||||
passThrough();
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Request error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy request failed', message: e.message }));
|
||||
});
|
||||
|
||||
// Forward request body for POST requests
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Music API Proxy Function ---
|
||||
function proxyRequest(targetUrl, req, res) {
|
||||
const parsedTarget = url.parse(targetUrl, true);
|
||||
const requestType = parsedTarget.query && parsedTarget.query.type;
|
||||
const isPicRequest = requestType === 'pic';
|
||||
const isUrlRequest = requestType === 'url';
|
||||
const maxRedirects = 5;
|
||||
|
||||
const buildHeaders = () => {
|
||||
const headers = {
|
||||
'User-Agent': '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-Encoding': 'identity'
|
||||
};
|
||||
if (req.headers.range) headers['Range'] = req.headers.range;
|
||||
if (req.headers['if-range']) headers['If-Range'] = req.headers['if-range'];
|
||||
return headers;
|
||||
};
|
||||
|
||||
const requestStream = (nextUrl, redirectCount = 0) => {
|
||||
if (redirectCount > maxRedirects) {
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Too many redirects' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedNext = url.parse(nextUrl);
|
||||
const isHttps = parsedNext.protocol === 'https:';
|
||||
const requestModule = isHttps ? https : http;
|
||||
const options = {
|
||||
hostname: parsedNext.hostname,
|
||||
port: parsedNext.port || (isHttps ? 443 : 80),
|
||||
path: parsedNext.path,
|
||||
method: req.method,
|
||||
headers: buildHeaders()
|
||||
};
|
||||
|
||||
const proxyReq = requestModule.request(options, (proxyRes) => {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
const resolved = url.resolve(nextUrl, proxyRes.headers.location);
|
||||
proxyRes.resume();
|
||||
requestStream(resolved, redirectCount + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
res.writeHead(proxyRes.statusCode || 200, headers);
|
||||
proxyRes.pipe(res);
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Stream error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy stream failed', message: e.message }));
|
||||
});
|
||||
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
};
|
||||
|
||||
const options = {
|
||||
hostname: parsedTarget.hostname,
|
||||
port: 443,
|
||||
path: parsedTarget.path,
|
||||
method: req.method,
|
||||
headers: buildHeaders()
|
||||
};
|
||||
|
||||
if (isUrlRequest) {
|
||||
const proxyReq = https.request(options, (proxyRes) => {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
const resolved = url.resolve(targetUrl, proxyRes.headers.location);
|
||||
proxyRes.resume();
|
||||
requestStream(resolved, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = proxyRes.headers['content-type'] || '';
|
||||
const shouldInspectBody = contentType.includes('application/json') || contentType.startsWith('text/');
|
||||
|
||||
if (shouldInspectBody) {
|
||||
let body = '';
|
||||
proxyRes.setEncoding('utf8');
|
||||
proxyRes.on('data', chunk => body += chunk);
|
||||
proxyRes.on('end', () => {
|
||||
try {
|
||||
const parsedBody = JSON.parse(body);
|
||||
const extractUrl = (payload) => {
|
||||
if (!payload || typeof payload !== 'object') return null;
|
||||
if (typeof payload.url === 'string') return payload.url;
|
||||
if (typeof payload.data === 'string') return payload.data;
|
||||
if (payload.data && typeof payload.data === 'object') {
|
||||
if (typeof payload.data.url === 'string') return payload.data.url;
|
||||
if (typeof payload.data.link === 'string') return payload.data.link;
|
||||
if (payload.data.data && typeof payload.data.data.url === 'string') return payload.data.data.url;
|
||||
if (Array.isArray(payload.data) && payload.data[0] && typeof payload.data[0].url === 'string') {
|
||||
return payload.data[0].url;
|
||||
}
|
||||
}
|
||||
if (payload.result && typeof payload.result.url === 'string') return payload.result.url;
|
||||
return null;
|
||||
};
|
||||
const resolvedUrl = extractUrl(parsedBody);
|
||||
if (resolvedUrl) {
|
||||
requestStream(url.resolve(targetUrl, resolvedUrl), 1);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through to return original body
|
||||
}
|
||||
|
||||
res.writeHead(proxyRes.statusCode || 200, {
|
||||
'Content-Type': proxyRes.headers['content-type'] || 'application/json'
|
||||
});
|
||||
res.end(body);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
res.writeHead(proxyRes.statusCode || 200, headers);
|
||||
proxyRes.pipe(res);
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Request error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy request failed', message: e.message }));
|
||||
});
|
||||
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyReq = https.request(options, (proxyRes) => {
|
||||
const passThrough = () => {
|
||||
const headers = { ...proxyRes.headers };
|
||||
delete headers['content-encoding'];
|
||||
delete headers['transfer-encoding'];
|
||||
|
||||
res.writeHead(proxyRes.statusCode, headers);
|
||||
proxyRes.pipe(res);
|
||||
};
|
||||
|
||||
// pic endpoint now returns JSON, turn it into a redirect for <img>
|
||||
if (isPicRequest) {
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
res.writeHead(302, { Location: proxyRes.headers.location });
|
||||
proxyRes.resume();
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = proxyRes.headers['content-type'] || '';
|
||||
if (contentType.startsWith('image/')) {
|
||||
passThrough();
|
||||
return;
|
||||
}
|
||||
|
||||
let body = '';
|
||||
proxyRes.setEncoding('utf8');
|
||||
proxyRes.on('data', chunk => body += chunk);
|
||||
proxyRes.on('end', () => {
|
||||
try {
|
||||
const parsedBody = JSON.parse(body);
|
||||
if (parsedBody && parsedBody.url) {
|
||||
res.writeHead(302, { Location: parsedBody.url });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
res.writeHead(proxyRes.statusCode || 200, {
|
||||
'Content-Type': proxyRes.headers['content-type'] || 'application/json'
|
||||
});
|
||||
res.end(body);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle redirects
|
||||
if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) {
|
||||
// For redirects, return the redirect URL to client
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ url: proxyRes.headers.location }));
|
||||
return;
|
||||
}
|
||||
|
||||
passThrough();
|
||||
});
|
||||
|
||||
proxyReq.on('error', (e) => {
|
||||
console.error('[Proxy] Request error:', e.message);
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy request failed', message: e.message }));
|
||||
});
|
||||
|
||||
// Forward request body for POST requests
|
||||
if (req.method === 'POST') {
|
||||
req.pipe(proxyReq);
|
||||
} else {
|
||||
proxyReq.end();
|
||||
}
|
||||
}
|
||||
|
||||
function writeMetadata(inputPath, outputPath, metadata) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = ['-i', inputPath];
|
||||
@@ -585,4 +585,4 @@ function writeMetadata(inputPath, outputPath, metadata) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user