diff --git a/sync-server/server.js b/sync-server/server.js
index 0e5926f..027ecb9 100644
--- a/sync-server/server.js
+++ b/sync-server/server.js
@@ -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
- 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
+ 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);
});
});
-}
+}