FormaLau commited on
Commit
34066d2
·
1 Parent(s): 2822e78

Capture simulator video and audio in sync

Browse files
Files changed (1) hide show
  1. app/record-sim.mjs +62 -19
app/record-sim.mjs CHANGED
@@ -1,22 +1,65 @@
1
- import { chromium } from '@playwright/test';
2
  import fs from 'node:fs';
3
- const out='video-recordings'; fs.mkdirSync(out,{recursive:true});
4
- const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
5
- const context=await browser.newContext({viewport:{width:1440,height:900},recordVideo:{dir:out,size:{width:1440,height:900}}});
6
- const page=await context.newPage();
7
  await page.goto('http://127.0.0.1:5175/?boot=1');
8
- await page.waitForFunction(()=>window.__store?.getState().bootDone);
9
- await page.evaluate(()=>window.__gameApi.startEntrance());
10
- await page.waitForFunction(()=>window.rl&&!window.rl.inputLocked);
11
- await page.waitForFunction(()=>window.__audioCaptureStream?.active);
12
- await page.evaluate(()=>{
13
- const r=new MediaRecorder(window.__audioCaptureStream,{mimeType:'audio/webm;codecs=opus'});
14
- window.__simAudioChunks=[]; r.ondataavailable=e=>e.data.size&&window.__simAudioChunks.push(e.data); r.start(250); window.__simAudioRecorder=r;
 
 
 
 
 
 
 
 
15
  });
16
- const script=JSON.parse(fs.readFileSync('public/scripts/reachy-microduck-conversation.json','utf8'));
17
- await page.evaluate(s=>{window.rl.chaseCam=false;window.__gameApi.script.load(s);window.__gameApi.script.run();},script);
18
- await page.evaluate(async()=>{const start=performance.now();await new Promise(resolve=>{const f=now=>{const t=(now-start)/1000;let x=0,y=0.82,z=2.8,tx=0,ty=0.25,tz=0;if(t<13){const a=t*.42;x=Math.sin(a)*2.35;z=Math.cos(a)*2.35;tx=0;ty=.24;}else if(t<28){const a=(t-13)*.5;x=1.55+Math.sin(a)*.35;z=2.05+Math.cos(a)*.25;y=.68;tx=0;ty=.2;}else{const a=(t-28)*.75;x=Math.sin(a)*2.45;z=Math.cos(a)*2.45;y=.88+Math.sin(t*.5)*.08;tx=0;ty=.23;}window.rl.camera.position.set(x,y,z);window.rl.controls.target.set(tx,ty,tz);window.rl.controls.update();if(t<55)requestAnimationFrame(f);else resolve();};requestAnimationFrame(f);});});
19
- await page.waitForTimeout(1500);
20
- const audioB64=await page.evaluate(()=>new Promise(resolve=>{const r=window.__simAudioRecorder;r.onstop=()=>{const b=new Blob(window.__simAudioChunks,{type:'audio/webm'});const fr=new FileReader();fr.onload=()=>resolve(fr.result.split(',')[1]);fr.readAsDataURL(b);};r.stop();}));
21
- fs.writeFileSync('sim-audio.webm',Buffer.from(audioB64,'base64'));
22
- const video=page.video(); await context.close(); const path=await video.path(); const final='reachy-microduck-conversation.webm'; fs.copyFileSync(path,final); console.log(final); await browser.close();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { chromium } from '@playwright/test';
2
  import fs from 'node:fs';
3
+
4
+ const browser = await chromium.launch({ channel: 'msedge', headless: true, args: ['--enable-unsafe-swiftshader'] });
5
+ const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
6
+ const page = await context.newPage();
7
  await page.goto('http://127.0.0.1:5175/?boot=1');
8
+ await page.waitForFunction(() => window.__store?.getState().bootDone);
9
+ await page.evaluate(() => window.__gameApi.startEntrance());
10
+ await page.waitForFunction(() => window.rl && !window.rl.inputLocked);
11
+ await page.waitForFunction(() => window.__audioCaptureStream?.active);
12
+
13
+ const script = JSON.parse(fs.readFileSync('public/scripts/reachy-microduck-conversation.json', 'utf8'));
14
+ await page.evaluate(() => {
15
+ const canvas = document.querySelector('canvas');
16
+ const videoStream = canvas.captureStream(30);
17
+ const tracks = [...videoStream.getVideoTracks(), ...window.__audioCaptureStream.getAudioTracks()];
18
+ const recorder = new MediaRecorder(new MediaStream(tracks), { mimeType: 'video/webm;codecs=vp8,opus' });
19
+ window.__filmChunks = [];
20
+ recorder.ondataavailable = (event) => { if (event.data.size) window.__filmChunks.push(event.data); };
21
+ recorder.start(250);
22
+ window.__filmRecorder = recorder;
23
  });
24
+ await page.evaluate((s) => {
25
+ window.rl.chaseCam = false;
26
+ window.__gameApi.script.load(s);
27
+ window.__gameApi.script.run();
28
+ }, script);
29
+
30
+ await page.evaluate(async () => {
31
+ const start = performance.now();
32
+ await new Promise((resolve) => {
33
+ const frame = (now) => {
34
+ const t = (now - start) / 1000;
35
+ let x = 0, y = 0.82, z = 2.8;
36
+ if (t < 13) {
37
+ const a = t * 0.42; x = Math.sin(a) * 2.35; z = Math.cos(a) * 2.35;
38
+ } else if (t < 28) {
39
+ const a = (t - 13) * 0.5; x = 1.55 + Math.sin(a) * 0.35; z = 2.05 + Math.cos(a) * 0.25; y = 0.68;
40
+ } else {
41
+ const a = (t - 28) * 0.75; x = Math.sin(a) * 2.45; z = Math.cos(a) * 2.45; y = 0.88 + Math.sin(t * 0.5) * 0.08;
42
+ }
43
+ window.rl.camera.position.set(x, y, z);
44
+ window.rl.controls.target.set(0, 0.23, 0);
45
+ window.rl.controls.update();
46
+ if (t < 55) requestAnimationFrame(frame); else resolve();
47
+ };
48
+ requestAnimationFrame(frame);
49
+ });
50
+ });
51
+ await page.waitForTimeout(1200);
52
+ const filmB64 = await page.evaluate(() => new Promise((resolve) => {
53
+ const recorder = window.__filmRecorder;
54
+ recorder.onstop = () => {
55
+ const blob = new Blob(window.__filmChunks, { type: 'video/webm' });
56
+ const reader = new FileReader();
57
+ reader.onload = () => resolve(reader.result.split(',')[1]);
58
+ reader.readAsDataURL(blob);
59
+ };
60
+ recorder.stop();
61
+ }));
62
+ fs.writeFileSync('reachy-microduck-synced.webm', Buffer.from(filmB64, 'base64'));
63
+ await context.close();
64
+ await browser.close();
65
+ console.log('reachy-microduck-synced.webm');