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