您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

56 行
3.6 KiB

  1. import fs from 'node:fs';
  2. import assert from 'node:assert/strict';
  3. import { login, call } from './role-permissions-live.mjs';
  4. import narration from '../../unreal_tran_web/src/features/virtual-training-scripts/narration-manifest.js';
  5. const dir = 'reports/bridge-digital-video-20260914'; fs.mkdirSync(dir, { recursive: true });
  6. const manifestPath = `${dir}/generation.json`;
  7. const progress = fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, 'utf8')) : { avatarId: '55', avatarName: '教员1', voiceId: '49', voiceName: '温和男声', clips: [] };
  8. const save = () => fs.writeFileSync(manifestPath, JSON.stringify(progress, null, 2));
  9. let session = await login('root');
  10. const api = async (path, data, method) => {
  11. let r = await call(`tran/v1/${path}`, session.accessToken, data, method);
  12. if (r.status === 401) { session = await login('root'); r = await call(`tran/v1/${path}`, session.accessToken, data, method); }
  13. assert.equal(r.status, 200, `${path}: ${r.message}`); return r.data;
  14. };
  15. const assignments = (await api('teaching/assignments?channel=VIRTUAL&assignmentKind=TRAINING&size=100')).records;
  16. const limit = process.argv.includes('--all') ? 39 : 1;
  17. let count = 0;
  18. for (const script of narration.scripts) {
  19. const task = assignments.find(t => t.code === script.code); assert.ok(task);
  20. for (const step of script.steps) {
  21. if (count++ >= limit) break;
  22. const key = `${script.id}:${script.version}:${step.stepCode}`;
  23. let clip = progress.clips.find(c => c.key === key);
  24. if (!clip) {
  25. const detail = await api(`content/projects/${task.contentProjectId}`);
  26. const generation = { title: `${script.title} · ${step.number} ${step.expectedTitle}`.slice(0, 120), text: step.text,
  27. avatarId: progress.avatarId, voiceId: progress.voiceId, backgroundId: null, speed: .85, width: 480, height: 640, subtitles: false };
  28. const job = await api(`content/projects/${task.contentProjectId}/digital-human/jobs`, { version: detail.project.version, stepId: step.stepCode,
  29. requestKey: `bridge-video-20260914-${script.code}-${step.number}`, generation });
  30. clip = { key, code: script.code, projectId: task.contentProjectId, assignmentId: task.id, stepCode: step.stepCode,
  31. number: step.number, title: generation.title, text: step.text, jobId: job.id, appId: job.appId, status: job.status };
  32. progress.clips.push(clip); save();
  33. }
  34. if (clip.status !== 'SUCCEEDED') {
  35. const deadline = Date.now() + 15 * 60000;
  36. while (Date.now() < deadline) {
  37. const job = await api(`content/projects/${clip.projectId}/digital-human/jobs/${clip.jobId}`);
  38. Object.assign(clip, { status: job.status, stage: job.stage, videoId: job.videoId, errorMessage: job.errorMessage }); save();
  39. if (job.status === 'SUCCEEDED') break;
  40. if (['FAILED', 'CANCELLED'].includes(job.status)) throw new Error(`${clip.code} ${clip.number}: ${job.errorMessage}`);
  41. console.log(clip.code, clip.number, job.status, job.stage);
  42. await new Promise(resolve => setTimeout(resolve, 5000));
  43. }
  44. assert.equal(clip.status, 'SUCCEEDED', 'Generation timeout');
  45. }
  46. const output = `${dir}/${clip.code}-step-${String(clip.number).padStart(2, '0')}.mp4`;
  47. if (!fs.existsSync(output)) {
  48. const r = await fetch(`http://127.0.0.1:6180/api/tran/v1/content/digital-human/videos/${clip.videoId}/preview`,
  49. { headers: { Authorization: `Bearer ${session.accessToken}` }, signal: AbortSignal.timeout(180000) });
  50. assert.equal(r.status, 200); fs.writeFileSync(output, Buffer.from(await r.arrayBuffer()));
  51. }
  52. clip.previewFile = output; save(); console.log('READY', clip.code, clip.number, clip.videoId);
  53. }
  54. }