Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

58 рядки
3.8 KiB

  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import { chromium } from '@playwright/test';
  4. const root = path.resolve('../..');
  5. const sourceReport = path.resolve('reports/instructors-v2-20260914');
  6. const report = path.resolve(process.env.REPORT_DIR || sourceReport);
  7. fs.mkdirSync(report,{recursive:true});
  8. const stable = process.argv.includes('--stable');
  9. const jobs = JSON.parse(fs.readFileSync(`${sourceReport}/jobs.json`, 'utf8'));
  10. const origin = process.env.BASE_URL || 'http://127.0.0.1:8003';
  11. const audio = fs.readFileSync(`${root}/unreal_tran/ute2e/reports/bridge-lipsync-diagnosis-20260914/sample.wav`).toString('base64');
  12. const browser = await chromium.launch({ channel: 'msedge', headless: true,
  13. args: ['--autoplay-policy=no-user-gesture-required', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
  14. const results = [];
  15. try {
  16. for (const job of jobs) {
  17. const id = stable ? job.stable_id : job.avatar_id;
  18. const context = await browser.newContext({ viewport: { width: 560, height: 760 } });
  19. await context.route(`${origin}/__instructor-v2-check`, route => route.fulfill({ contentType: 'text/html', body:
  20. `<html><meta charset="utf-8"><body style="margin:16px;background:#f3f8f7;font:16px sans-serif"><h3>教员${job.number} · v2 语音驱动预览</h3><iframe style="border:0;width:480px;height:640px" src="/human/viewer/${id}" allow="autoplay"></iframe><script>window.events=[];addEventListener('message',e=>{if(e.origin===location.origin&&e.data?.type?.startsWith('digital-human.'))window.events.push(e.data)});</script></body></html>` }));
  21. const page = await context.newPage();
  22. const errors = [];
  23. page.on('pageerror', error => errors.push(error.message));
  24. await page.goto(`${origin}/__instructor-v2-check`);
  25. const frame = page.frames().find(frame => frame.url().includes('/human/viewer/'));
  26. await frame.waitForFunction(() => window.DH_RUNTIME_READY, {}, { timeout: 120000 });
  27. await frame.evaluate(() => {
  28. window.v2Check = { frames: 0, active: 0, maxBlend: 0 };
  29. const original = Module._updateBlendShape;
  30. Module._updateBlendShape = function(pointer, count) {
  31. const result = original(pointer, count);
  32. const max = Math.max(...Array.from(new Float32Array(Module.HEAPU8.buffer, pointer, 12)).map(Math.abs));
  33. window.v2Check.frames++;
  34. window.v2Check.maxBlend = Math.max(window.v2Check.maxBlend, max);
  35. if (max > .001) window.v2Check.active++;
  36. return result;
  37. };
  38. });
  39. await page.screenshot({ path: `${report}/${stable ? 'stable' : 'generated'}-${job.number}-idle.png` });
  40. await page.evaluate(audioBase64 => document.querySelector('iframe').contentWindow.postMessage({
  41. type: 'digital-human.play-audio', requestId: 'v2-validation', audioBase64,
  42. }, location.origin), audio);
  43. await page.waitForFunction(() => window.events.some(event => event.type === 'digital-human.speaking-start'), {}, { timeout: 15000 });
  44. await frame.waitForFunction(() => window.v2Check.active > 30, {}, { timeout: 15000 });
  45. await page.screenshot({ path: `${report}/${stable ? 'stable' : 'generated'}-${job.number}-speaking.png` });
  46. await page.waitForFunction(() => window.events.some(event => event.type === 'digital-human.speaking-end'), {}, { timeout: 20000 });
  47. const events = await page.evaluate(() => window.events);
  48. const driver = await frame.evaluate(() => window.v2Check);
  49. if (errors.length || events.some(event => event.type === 'digital-human.error')) throw new Error(JSON.stringify({ errors, events }));
  50. const result = { number: job.number, avatarId: id, driver, events, errors };
  51. results.push(result);
  52. console.log(JSON.stringify(result));
  53. fs.writeFileSync(`${report}/${stable ? 'stable' : 'generated'}-viewer-validation.json`, JSON.stringify(results, null, 2));
  54. await context.close();
  55. }
  56. } finally { await browser.close(); }