|
- import fs from 'node:fs';
- import assert from 'node:assert/strict';
- import { login, call } from './role-permissions-live.mjs';
- import manifest from '../../unreal_tran_web/src/features/virtual-training-scripts/narration-manifest.js';
- const dir = 'reports/bridge-digital-video-20260914';
- const generated = JSON.parse(fs.readFileSync(`${dir}/generation.json`, 'utf8'));
- const session = await login('root');
- const api = async (path, body, method) => {
- const r = await call(`tran/v1/${path}`, session.accessToken, body, 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 codes = process.argv.slice(2);
- for (const pack of manifest.scripts.filter(p => !codes.length || codes.includes(p.code))) {
- const clips = generated.clips.filter(c => c.code === pack.code);
- assert.equal(clips.length, pack.steps.length); assert.ok(clips.every(c => c.status === 'SUCCEEDED'));
- let task = assignments.find(t => t.code === pack.code);
- let detail = await api(`content/projects/${task.contentProjectId}`);
- const backup = `${dir}/${pack.code}-before.json`;
- if (!fs.existsSync(backup)) fs.writeFileSync(backup, JSON.stringify({ task, detail,
- runs: await api(`teaching/runs?assignmentId=${task.id}&size=100`) }, null, 2));
- const content = structuredClone(detail.currentVersion.content), assets = [...detail.currentVersion.assets];
- let changed = false;
- for (const step of content.steps) {
- const clip = clips.find(c => c.stepCode === (step.stepCode || step.id)); assert.ok(clip);
- const existing = assets.find(a => a.code === step.audio?.narrationVideoAssetCode && String(a.metadata?.sourceVideoId) === clip.videoId);
- if (existing) continue;
- const asset = await api(`content/projects/${task.contentProjectId}/digital-human/videos/${clip.videoId}/import`,
- { version: detail.project.version, name: clip.title, appId: clip.appId });
- assets.push(asset);
- step.audio = { ...step.audio, narrationSource: 'digital-generate', narrationText: clip.text,
- narrationUrl: '', narrationAssetCode: '', narrationVideoAssetCode: asset.code,
- digitalHuman: { enabled: true, mode: 'video', videoSource: 'library', videoId: clip.videoId,
- videoName: clip.title, appId: clip.appId, videoUrl: '', generatedVideoUrl: '', generationStatus: 'generated' } };
- changed = true;
- console.log('IMPORTED', pack.code, clip.number, asset.code);
- }
- if (changed) {
- detail = await api(`content/projects/${task.contentProjectId}`, { ...detail.project, content,
- dependencies: detail.currentVersion.dependencies, assets, changeSummary: '39段数字人视频讲解;原语音保留可切换' }, 'PUT');
- }
- if (detail.project.currentVersionStatus !== 'PUBLISHED') {
- await api(`content/projects/${task.contentProjectId}/publish`, { version: detail.project.version });
- detail = await api(`content/projects/${task.contentProjectId}`);
- }
- if (task.contentVersionId !== detail.project.publishedVersionId) {
- task = await api(`teaching/assignments/${task.id}/narration`, { version: task.version,
- contentVersionId: detail.project.publishedVersionId, reason: '补充逐步数字人讲解视频,保留原语音,训练操作和评分不变' }, 'PUT');
- }
- const original = JSON.parse(fs.readFileSync(backup, 'utf8'));
- const runs = await api(`teaching/runs?assignmentId=${task.id}&size=100`);
- const stableRuns = value => JSON.parse(JSON.stringify(value, (key, item) => key === 'serverNow' ? undefined : item));
- assert.deepEqual(stableRuns(runs), stableRuns(original.runs), 'Existing training runs must not change');
- const result = { code: pack.code, taskId: task.id, contentVersionId: task.contentVersionId,
- clips: task.definitionSnapshot.steps.map(s => ({ stepCode: s.stepCode, videoAssetCode: s.audio.narrationVideoAssetCode })),
- runsUnchanged: true, assets: await api(`teaching/assignments/${task.id}/assets`) };
- fs.writeFileSync(`${dir}/${pack.code}-binding.json`, JSON.stringify(result, null, 2));
- console.log('BOUND', pack.code, task.contentVersionId, result.clips.length);
- }
|