|
- import fs from 'node:fs';
- import path from 'node:path';
- import assert from 'node:assert/strict';
- import crypto from 'node:crypto';
- const web=path.resolve('../unreal_tran_web'),dir=path.resolve('reports/bridge-narration-junhao-20260921');
- const source=path.join(web,'src/features/virtual-training-scripts');
- const audio=JSON.parse(fs.readFileSync(path.join(dir,'audio-manifest.json'),'utf8'));
- const video=JSON.parse(fs.readFileSync(path.join(dir,'video-manifest.json'),'utf8'));
- const quality=JSON.parse(fs.readFileSync(path.join(dir,'quality.json'),'utf8'));
- const hash=file=>crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
- assert.equal(quality.length,39);assert.ok(quality.every(c=>c.technicalPass));
- for(const old of JSON.parse(fs.readFileSync(path.join(dir,'previous-media.json'),'utf8')))assert.equal(hash(web+'/public'+old.url),old.sha256);
- assert.equal(audio.scripts.flatMap(p=>p.steps).length,39);
- for(const pack of audio.scripts)for(const clip of pack.steps){
- const v=video.scripts.find(p=>p.code===pack.code)?.steps.find(s=>s.stepCode===clip.stepCode);
- assert.equal(v.text,clip.text);assert.equal(v.expectedTitle,clip.expectedTitle);assert.equal(v.expectedInstruction,clip.expectedInstruction);
- assert.equal(hash(web+'/public'+clip.url),clip.stepSha256);assert.equal(hash(web+'/public'+v.url),v.sha256);
- assert.equal(quality.find(q=>q.key===`${pack.code}-step-${String(clip.number).padStart(2,'0')}`).sha256,v.sha256);
- if(clip.safetyUrl){assert.match(clip.safetyUrl,/junhao-v2-20260921/);assert.equal(hash(web+'/public'+clip.safetyUrl),clip.safetySha256);}
- }
- for(const [name,data]of [['narration-junhao-20260921.js',audio],['narration-video-junhao-20260921.js',video]])
- fs.writeFileSync(path.join(source,name),'// Generated by bridge-narration-current-voice.py; original release remains unchanged.\nexport default '+JSON.stringify(data,null,2)+';\n');
- fs.writeFileSync(path.join(source,'narration-release.js'),`import previousAudio from './narration-manifest.js';
- import previousVideo from './narration-video-manifest.js';
- import junhaoAudio from './narration-junhao-20260921.js';
- import junhaoVideo from './narration-video-junhao-20260921.js';
-
- // Build-time switch. Preserve both generations so rollback never regenerates media.
- export function selectNarrationRelease(name) {
- if (name === 'previous') return { manifest: previousAudio, videos: previousVideo };
- if (name === 'junhao-v2-20260921') return { manifest: junhaoAudio, videos: junhaoVideo };
- throw new Error('未知的桥梁讲解资源版本:' + name);
- }
- export const activeRelease = import.meta.env?.VITE_BRIDGE_NARRATION_RELEASE || 'junhao-v2-20260921';
- export const { manifest, videos } = selectNarrationRelease(activeRelease);
- `);
- const filename=path.join(source,'narration.js');
- const original=fs.readFileSync(filename,'utf8');
- const replacement=original.replace("import manifest from './narration-manifest.js';\nimport videos from './narration-video-manifest.js';","import { manifest, videos } from './narration-release.js';");
- assert.ok(replacement.includes("import { manifest, videos } from './narration-release.js';"));
- fs.writeFileSync(filename,replacement);
- fs.writeFileSync(path.join(dir,'activation.json'),JSON.stringify({release:'junhao-v2-20260921',oldMediaCount:80,oldMediaUnchanged:true,clips:39,qualityPassed:true,taskDefinitionsChanged:false},null,2));
- console.log('Activated 39 clips; previous 80 media files preserved.');
|