本次修正版
第一步,根据故障现象,确认是哪一个液压泵发生故障。
from pathlib import Path import sys,json,subprocess,re,threading,hashlib,shutil import numpy as np import soundfile as sf import httpx ROOT=Path(__file__).resolve().parents[3] sys.path.insert(0,str(ROOT/'ai_person/ai_person_api')) from app.config import get_settings from app.schemas.open_platform import GenerationCommand from app.services.open_generation_worker import OpenGenerationWorker out=ROOT/'unreal_tran/ute2e/reports/bridge-005-moss-refined-20260920' out.mkdir(parents=True,exist_ok=True) source=ROOT/'ai_person/outputs/moss-integration-check/step1-refined/natural-42.wav' audio,sr=sf.read(source) detect=subprocess.run(['ffmpeg','-hide_banner','-i',str(source),'-af','silencedetect=noise=-48dB:d=0.65','-f','null','-'],capture_output=True,text=True,check=True) starts=re.findall(r'silence_start: ([0-9.]+)',detect.stderr) ends=re.findall(r'silence_end: ([0-9.]+)',detect.stderr) pieces=[];last=0;cuts=[] for start,end in zip(starts,ends): start,end=float(start),float(end) a,b=round((start+.16)*sr),round((end-.16)*sr) if b<=a:continue pieces.append(audio[last:a]);last=b;cuts.append({'start':start,'end':end,'keptSeconds':.32}) pieces.append(audio[last:]) prepared=np.concatenate(pieces) wav=out/'TASK-VIRTUAL-005-step-01-Junhao-refined.wav' sf.write(wav,prepared,sr,subtype='PCM_16') shutil.copyfile(source,out/'natural-unedited.wav') settings=get_settings().model_copy(update={'service_base_url':'http://1.14.103.234:8123'}) avatar='a1000000000000000000000000000005' with httpx.Client(trust_env=False,timeout=30) as client: response=client.get(str(settings.service_base_url)+f'/api/v1/avatars/{avatar}/assets/manifest.json');response.raise_for_status() expected=json.loads((ROOT/'ai_person/ai_person_service/builtin_avatars/instructors-v2.json').read_text('utf-8'))['instructors'][0]['bundle_sha256']['manifest.json'] assert hashlib.sha256(response.content).hexdigest()==expected text='第一步,根据故障现象,确认是哪一个液压泵发生故障。' command=GenerationCommand(title='005 第一步 Junhao 连贯性修正版',text=text,avatarId='55',voiceId='49',speed=1,width=480,height=640,subtitles=False) worker=OpenGenerationWorker(None,settings,None,None) capture=out/'capture.webm';video=out/'TASK-VIRTUAL-005-step-01-Junhao-refined.mp4' print('pause cleanup:',cuts,'duration:',len(prepared)/sr,flush=True) worker.capture(command,avatar,wav,None,capture,threading.Event()) subprocess.run(['ffmpeg','-v','error','-y','-i',str(capture),'-r','25','-c:v','libx264','-crf','20','-preset','veryfast','-pix_fmt','yuv420p','-c:a','aac','-b:a','128k','-movflags','+faststart',str(video)],check=True) subprocess.run(['ffmpeg','-v','error','-i',str(video),'-f','null','-'],check=True) probe=json.loads(subprocess.run(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(video)],capture_output=True,text=True,check=True).stdout) assert abs(float(probe['format']['duration'])-len(prepared)/sr)<.7 shutil.copyfile(out.parent/'bridge-005-moss-junhao-v2-20260919/TASK-VIRTUAL-005-step-01-MOSS-Junhao.mp4',out/'previous.mp4') (out/'result.json').write_text(json.dumps({'text':text,'voice':'MOSS Junhao','seed':42,'cuts':cuts,'inputDuration':len(audio)/sr,'preparedDuration':len(prepared)/sr,'lipInputRate':16000,'metadata':probe,'publishedBindingsChanged':False},ensure_ascii=False,indent=2),'utf-8') (out/'index.html').write_text('''
教员1 v2 · MOSS Junhao。修正版使用连贯句式、压缩长静音、统一16kHz口型输入。仍为独立试片,待人工确认口型和发音。
第一步,根据故障现象,确认是哪一个液压泵发生故障。