Download app/record-sim.mjs from FormaLau/microduck-reachy-simulator: direct link, hf CLI and curl.
- Browser
- Download file 3.08 kB
-
https://huggingface.co/spaces/FormaLau/microduck-reachy-simulator/resolve/432303e147df37294717ea23c242be5e7c1b6719/app/record-sim.mjs
- Command line
-
hf download hf://spaces/FormaLau/microduck-reachy-simulator@432303e147df37294717ea23c242be5e7c1b6719/app/record-sim.mjs
-
curl -L -o record-sim.mjs https://huggingface.co/spaces/FormaLau/microduck-reachy-simulator/resolve/432303e147df37294717ea23c242be5e7c1b6719/app/record-sim.mjs
3.08 kB
| import { chromium } from '@playwright/test'; | |
| import fs from 'node:fs'; | |
| const browser = await chromium.launch({ channel: 'msedge', headless: true, args: ['--enable-unsafe-swiftshader'] }); | |
| const context = await browser.newContext({ viewport: { width: 1440, height: 900 } }); | |
| const page = await context.newPage(); | |
| await page.goto('http://127.0.0.1:5175/?boot=1'); | |
| await page.waitForFunction(() => window.__store?.getState().bootDone); | |
| await page.evaluate(() => window.__gameApi.startEntrance()); | |
| await page.waitForFunction(() => window.rl && !window.rl.inputLocked); | |
| await page.waitForFunction(() => window.__audioCaptureStream?.active); | |
| const script = JSON.parse(fs.readFileSync('public/scripts/reachy-microduck-conversation.json', 'utf8')); | |
| await page.evaluate(() => { | |
| const canvas = document.querySelector('canvas'); | |
| const videoStream = canvas.captureStream(30); | |
| const tracks = [...videoStream.getVideoTracks(), ...window.__audioCaptureStream.getAudioTracks()]; | |
| const recorder = new MediaRecorder(new MediaStream(tracks), { mimeType: 'video/webm;codecs=vp8,opus' }); | |
| window.__filmChunks = []; | |
| recorder.ondataavailable = (event) => { if (event.data.size) window.__filmChunks.push(event.data); }; | |
| recorder.start(250); | |
| window.__filmRecorder = recorder; | |
| }); | |
| await page.evaluate((s) => { | |
| window.rl.chaseCam = false; | |
| window.__speechMarks = []; | |
| window.__filmStart = performance.now(); | |
| window.addEventListener('reachy-speech-start', (event) => window.__speechMarks.push({ text: event.detail?.text || '', at: (performance.now() - window.__filmStart) / 1000 })); | |
| window.__gameApi.script.load(s); | |
| window.__gameApi.script.run(); | |
| }, script); | |
| await page.evaluate(async () => { | |
| const start = performance.now(); | |
| await new Promise((resolve) => { | |
| const frame = (now) => { | |
| const t = (now - start) / 1000; | |
| // One close, continuous orbit keeps both robots large in frame and | |
| // avoids the hard viewpoint jumps between emotion sections. | |
| const a = t * 0.28; | |
| const x = Math.sin(a) * 1.55; | |
| const z = Math.cos(a) * 1.55; | |
| const y = 0.72 + Math.sin(t * 0.22) * 0.04; | |
| window.rl.camera.position.set(x, y, z); | |
| window.rl.controls.target.set(0, 0.23, 0); | |
| window.rl.controls.update(); | |
| if (t < 55) requestAnimationFrame(frame); else resolve(); | |
| }; | |
| requestAnimationFrame(frame); | |
| }); | |
| }); | |
| await page.waitForTimeout(1200); | |
| const capture = await page.evaluate(() => new Promise((resolve) => { | |
| const recorder = window.__filmRecorder; | |
| recorder.onstop = () => { | |
| const blob = new Blob(window.__filmChunks, { type: 'video/webm' }); | |
| const reader = new FileReader(); | |
| reader.onload = () => resolve({ b64: reader.result.split(',')[1], marks: window.__speechMarks }); | |
| reader.readAsDataURL(blob); | |
| }; | |
| recorder.stop(); | |
| })); | |
| fs.writeFileSync('reachy-microduck-synced.webm', Buffer.from(capture.b64, 'base64')); | |
| fs.writeFileSync('reachy-microduck-speech-marks.json', JSON.stringify(capture.marks, null, 2)); | |
| await context.close(); | |
| await browser.close(); | |
| console.log('reachy-microduck-synced.webm'); | |