選択できるのは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)