Add Swoole alternative backend
This commit is contained in:
43
alt/compare.mjs
Normal file
43
alt/compare.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
const base = process.argv[2];
|
||||
if (!base) { console.error('usage: node alt/compare.mjs <bun-base-url>'); process.exit(2); }
|
||||
const alt = (process.env.ALT_URL || 'http://127.0.0.1:3000').replace(/\/$/, '');
|
||||
const video = process.env.VIDEO_ID || 'dQw4w9WgXcQ';
|
||||
const generation = process.env.MEDIA_GEN || '1';
|
||||
const query = process.env.SEARCH_QUERY || 'worship';
|
||||
const paths = [
|
||||
['search', `/api/search?q=${encodeURIComponent(query)}`],
|
||||
['streams', `/api/streams?v=${encodeURIComponent(video)}`],
|
||||
['download-prepare', `/api/download/${encodeURIComponent(video)}/prepare`],
|
||||
['media-range', `/api/media/${encodeURIComponent(video)}?g=${encodeURIComponent(generation)}`],
|
||||
];
|
||||
function jsonPaths(value, prefix = '$', out = []) {
|
||||
if (Array.isArray(value)) { out.push(`${prefix}[]`); if (value.length) jsonPaths(value[0], `${prefix}[]`, out); }
|
||||
else if (value && typeof value === 'object') for (const key of Object.keys(value).sort()) { out.push(`${prefix}.${key}`); jsonPaths(value[key], `${prefix}.${key}`, out); }
|
||||
return out.sort();
|
||||
}
|
||||
async function request(url, range = false) {
|
||||
const headers = range ? { Range: 'bytes=0-1023' } : {};
|
||||
const response = await fetch(url, { headers, signal: AbortSignal.timeout(Number(process.env.COMPARE_TIMEOUT_MS || 30000)) });
|
||||
const type = response.headers.get('content-type') || '';
|
||||
const body = type.includes('json') ? await response.json() : await response.arrayBuffer();
|
||||
return { status: response.status, headers: Object.fromEntries(['content-type','cache-control','accept-ranges','content-range','etag'].map(k => [k,response.headers.get(k)])), body };
|
||||
}
|
||||
let failed = false;
|
||||
for (const [name, path] of paths) {
|
||||
try {
|
||||
const [a,b] = await Promise.all([request(base.replace(/\/$/,'')+path,name==='media-range'),request(alt+path,name==='media-range')]);
|
||||
const isJson = typeof a.body === 'object' && !ArrayBuffer.isView(a.body) && !(a.body instanceof ArrayBuffer);
|
||||
const result = { name, path, bun: {status:a.status,headers:a.headers}, swoole:{status:b.status,headers:b.headers} };
|
||||
if (isJson) { result.bun.shape=jsonPaths(a.body); result.swoole.shape=jsonPaths(b.body); result.shapeMatches=JSON.stringify(result.bun.shape)===JSON.stringify(result.swoole.shape); }
|
||||
const headerKeys=['content-type','cache-control','accept-ranges','content-range','etag'];
|
||||
const norm=(key,value)=>value===null?null:(key==='content-type'?value.toLowerCase().replace(/;\s*charset=utf-8/i,'; charset=utf-8'):value.trim());
|
||||
result.headerMatches=headerKeys.filter((key)=>norm(key,a.headers[key])===norm(key,b.headers[key]));
|
||||
result.headerDiffs=headerKeys.filter((key)=>norm(key,a.headers[key])!==norm(key,b.headers[key])).map((key)=>({key,bun:a.headers[key] ?? null,swoole:b.headers[key] ?? null}));
|
||||
if(name==='media-range') result.rangeBytes={bun:a.body.byteLength,swoole:b.body.byteLength};
|
||||
result.statusMatches=a.status===b.status;
|
||||
console.log(JSON.stringify(result));
|
||||
if(!result.statusMatches || (result.shapeMatches===false) || result.headerDiffs.length)failed=true;
|
||||
} catch(error) { failed=true; console.error(JSON.stringify({name,path,error:String(error)})); }
|
||||
}
|
||||
if(failed) process.exitCode=1;
|
||||
Reference in New Issue
Block a user