Decision #1: watch the real Discord screen share by running a real web client under a virtual display (Xvfb) and capturing rendered frames — not bot/selfbot protocol receive. PLAN.md records all locked decisions and the step list. PoC (poc/capture_xvfb.py + test_page.html) launches system Chrome non-headless under Xvfb via Playwright and captures 6 changing, non-blank frames -> de-risks the display+capture chain on the .9 host.
30 lines
931 B
HTML
30 lines
931 B
HTML
<!doctype html>
|
|
<html>
|
|
<head><meta charset="utf-8"><title>capture test</title>
|
|
<style>html,body{margin:0;background:#101418}</style></head>
|
|
<body>
|
|
<canvas id="c" width="1280" height="720"></canvas>
|
|
<script>
|
|
// An always-changing scene, so captured frames must differ frame-to-frame and
|
|
// be non-blank. This stands in for "a Discord screen share is playing".
|
|
const c = document.getElementById('c'), x = c.getContext('2d');
|
|
let t = 0;
|
|
function draw() {
|
|
t++;
|
|
x.fillStyle = '#101418'; x.fillRect(0, 0, 1280, 720);
|
|
// moving box
|
|
const px = (t * 7) % 1180;
|
|
x.fillStyle = `hsl(${(t*3)%360} 80% 55%)`;
|
|
x.fillRect(px, 300 + 120 * Math.sin(t / 15), 100, 100);
|
|
// big live counter text
|
|
x.fillStyle = '#e8eef2'; x.font = 'bold 90px monospace';
|
|
x.fillText('FRAME ' + t, 60, 140);
|
|
x.font = '28px monospace';
|
|
x.fillText(new Date().toISOString(), 60, 200);
|
|
requestAnimationFrame(draw);
|
|
}
|
|
draw();
|
|
</script>
|
|
</body>
|
|
</html>
|