71 lines
2.6 KiB
JavaScript
71 lines
2.6 KiB
JavaScript
// Dev-only: CDP screencast while the scout-dest dialog is open — checks
|
|
// whether ANY live frame paints the dialog plate (vs. the stale-capture
|
|
// hypothesis). Usage:
|
|
// node dev/cdp-cast.mjs <url> <dir> '<readyExpr>' [ms]
|
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
|
const [url, dir, readyExpr, ms] = process.argv.slice(2);
|
|
const CDP = process.env.CDP_PORT ? `127.0.0.1:${CDP_PORT}` : '127.0.0.1:9333';
|
|
const dur = Number(ms ?? 4000);
|
|
|
|
let target = await (await fetch(`http://${CDP}/json/list`)).json()
|
|
.then((ts) => ts.find((t) => t.type === 'page'));
|
|
if (!target) {
|
|
target = await (await fetch(`http://${CDP}/json/new?${encodeURIComponent(url)}`, { method: 'PUT' })).json();
|
|
}
|
|
const ws = new WebSocket(target.webSocketDebuggerUrl);
|
|
let id = 0;
|
|
const pending = new Map();
|
|
const send = (method, params = {}) => new Promise((res, rej) => {
|
|
const mid = ++id;
|
|
pending.set(mid, { res, rej });
|
|
ws.send(JSON.stringify({ id: mid, method, params }));
|
|
});
|
|
const frames = [];
|
|
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = (e) => rej(new Error('ws')); });
|
|
ws.onmessage = (m) => {
|
|
const msg = JSON.parse(m.data);
|
|
if (msg.id && pending.has(msg.id)) {
|
|
const { res, rej } = pending.get(msg.id);
|
|
pending.delete(msg.id);
|
|
msg.error ? rej(new Error(msg.error.message)) : res(msg.result);
|
|
} else if (msg.method === 'Page.screencastFrame') {
|
|
frames.push(msg.params.data);
|
|
send('Page.screencastFrameAck', { sessionId: msg.params.sessionId });
|
|
}
|
|
};
|
|
|
|
await send('Page.enable');
|
|
await send('Runtime.enable');
|
|
await send('Network.enable');
|
|
await send('Network.setCacheDisabled', { cacheDisabled: true });
|
|
await send('Page.navigate', { url: 'about:blank' });
|
|
await new Promise((r) => setTimeout(r, 300));
|
|
await send('Page.navigate', { url });
|
|
|
|
const evaluate = async (expr) => {
|
|
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true });
|
|
if (r.exceptionDetails) throw new Error('page: ' + (r.exceptionDetails.exception?.description ?? r.exceptionDetails.text));
|
|
return r.result?.value;
|
|
};
|
|
|
|
const deadline = Date.now() + 120000;
|
|
while (Date.now() < deadline) {
|
|
const v = await evaluate(readyExpr).catch(() => null);
|
|
if (v) break;
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
}
|
|
|
|
await send('Page.startScreencast', { format: 'png', everyNthFrame: 1 });
|
|
await new Promise((r) => setTimeout(r, dur));
|
|
await send('Page.stopScreencast');
|
|
|
|
await mkdir(dir, { recursive: true });
|
|
let i = 0;
|
|
for (const data of frames) {
|
|
await writeFile(`${dir}/frame-${String(i).padStart(3, '0')}.png`, Buffer.from(data, 'base64'));
|
|
i++;
|
|
}
|
|
console.log(`captured ${i} frames in ${dir}`);
|
|
ws.close();
|