Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

51 рядки
4.5 KiB

  1. from pathlib import Path
  2. import sys,json,subprocess,re,threading,hashlib,shutil
  3. import numpy as np
  4. import soundfile as sf
  5. import httpx
  6. ROOT=Path(__file__).resolve().parents[3]
  7. sys.path.insert(0,str(ROOT/'ai_person/ai_person_api'))
  8. from app.config import get_settings
  9. from app.schemas.open_platform import GenerationCommand
  10. from app.services.open_generation_worker import OpenGenerationWorker
  11. out=ROOT/'unreal_tran/ute2e/reports/bridge-005-moss-refined-20260920'
  12. out.mkdir(parents=True,exist_ok=True)
  13. source=ROOT/'ai_person/outputs/moss-integration-check/step1-refined/natural-42.wav'
  14. audio,sr=sf.read(source)
  15. 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)
  16. starts=re.findall(r'silence_start: ([0-9.]+)',detect.stderr)
  17. ends=re.findall(r'silence_end: ([0-9.]+)',detect.stderr)
  18. pieces=[];last=0;cuts=[]
  19. for start,end in zip(starts,ends):
  20. start,end=float(start),float(end)
  21. a,b=round((start+.16)*sr),round((end-.16)*sr)
  22. if b<=a:continue
  23. pieces.append(audio[last:a]);last=b;cuts.append({'start':start,'end':end,'keptSeconds':.32})
  24. pieces.append(audio[last:])
  25. prepared=np.concatenate(pieces)
  26. wav=out/'TASK-VIRTUAL-005-step-01-Junhao-refined.wav'
  27. sf.write(wav,prepared,sr,subtype='PCM_16')
  28. shutil.copyfile(source,out/'natural-unedited.wav')
  29. settings=get_settings().model_copy(update={'service_base_url':'http://1.14.103.234:8123'})
  30. avatar='a1000000000000000000000000000005'
  31. with httpx.Client(trust_env=False,timeout=30) as client:
  32. response=client.get(str(settings.service_base_url)+f'/api/v1/avatars/{avatar}/assets/manifest.json');response.raise_for_status()
  33. expected=json.loads((ROOT/'ai_person/ai_person_service/builtin_avatars/instructors-v2.json').read_text('utf-8'))['instructors'][0]['bundle_sha256']['manifest.json']
  34. assert hashlib.sha256(response.content).hexdigest()==expected
  35. text='第一步,根据故障现象,确认是哪一个液压泵发生故障。'
  36. command=GenerationCommand(title='005 第一步 Junhao 连贯性修正版',text=text,avatarId='55',voiceId='49',speed=1,width=480,height=640,subtitles=False)
  37. worker=OpenGenerationWorker(None,settings,None,None)
  38. capture=out/'capture.webm';video=out/'TASK-VIRTUAL-005-step-01-Junhao-refined.mp4'
  39. print('pause cleanup:',cuts,'duration:',len(prepared)/sr,flush=True)
  40. worker.capture(command,avatar,wav,None,capture,threading.Event())
  41. 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)
  42. subprocess.run(['ffmpeg','-v','error','-i',str(video),'-f','null','-'],check=True)
  43. probe=json.loads(subprocess.run(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(video)],capture_output=True,text=True,check=True).stdout)
  44. assert abs(float(probe['format']['duration'])-len(prepared)/sr)<.7
  45. shutil.copyfile(out.parent/'bridge-005-moss-junhao-v2-20260919/TASK-VIRTUAL-005-step-01-MOSS-Junhao.mp4',out/'previous.mp4')
  46. (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')
  47. (out/'index.html').write_text('''<!doctype html><html lang="zh-CN"><meta charset="utf-8"><title>005第一步讲解修正版</title><style>body{font:16px/1.8 system-ui;margin:32px auto;max-width:1000px;background:#eef5f3;color:#173e35}main{display:flex;gap:28px}section{background:white;padding:22px;flex:1;border-radius:14px}video{width:100%;aspect-ratio:3/4}audio{width:100%}a{color:#00856c}</style><h1>005 · 第一步讲解复核</h1><p>教员1 v2 · MOSS Junhao。修正版使用连贯句式、压缩长静音、统一16kHz口型输入。仍为独立试片,待人工确认口型和发音。</p><main><section><h2>本次修正版</h2><video controls src="TASK-VIRTUAL-005-step-01-Junhao-refined.mp4"></video><p>第一步,根据故障现象,确认是哪一个液压泵发生故障。</p><audio controls src="TASK-VIRTUAL-005-step-01-Junhao-refined.wav"></audio><p><a href="natural-unedited.wav">试听未处理的新语音</a></p></section><section><h2>上次版本</h2><video controls src="previous.mp4"></video></section></main><script>document.addEventListener('play',e=>{document.querySelectorAll('video,audio').forEach(a=>{if(a!==e.target)a.pause()})},true)</script></html>''','utf-8')
  48. print('completed',video,flush=True)