
    Djۮ                       S r SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKrSSKrSSKrSSKrSSKJr  SSKJr  SSKJrJr  SSKJr  S rSrS	rS
rSrSrSrSr Sr!S r"S r#S r$\" S5      r%\%S-  r&\%S-  r'\%S-  r(\%S-  r)\%S-  r*\%S-  r+\%S-  r,\&\'\(\)\*\+\,4 H  r-\-R]                  SSS9  M     \%S-  r/\%S-  r0\%S-  r1S r2S!r3SSK
r4\4Rj                  " 5       \4Rj                  " 5       \4Rj                  " 5       S".r6SUS# jr7SUS$ jr8S% r9S& r:S' r;S( r<S) r=S* r>S+ r?S, r@S- rAS. rBSUS/ jrCSVS0 jrDSWS1 jrESXS2 jrFSYS3 jrGSZS4 jrH  S[S5 jrI S\S6 jrJ " S7 S85      rK " S9 S:5      rL " S; S<5      rM " S= S>5      rN\N" 5       rO " S? S@\\5      rP " SA SB\5      rQ\RSC:X  a  \S" SD5        \S" SE5        \S" SD5        \S" SF\2 35        \S" SG\% 35        \S" 5         \S" SH5        \S" SI5        \S" SJ5        \S" SK5        \S" SL5        \S" 5         \S" SM5        \S" SN5        \S" SO5        \S" SP5        \S" SQ5        \S" 5         \P" SR\24\Q5      rT\S" SS\2 35         \TR                  5         gg! \V a    \S" ST5        \TR                  5          gf = f)]u^  
数字人口播服务 v2.0 - Duix.Avatar + GPT-SoVITS + RMBG-2.0
RTX 5060 Ti (15.9GB VRAM) + CUDA 13.0

架构:
1. 背景抠图:   RMBG-2.0 (via ComfyUI :8188) → 透明通道 PNG 序列
2. 声音克隆:   GPT-SoVITS API (:9880)         → Few-shot 高质量声音克隆
3. 口型驱动:   Duix.Avatar  (WSL2 :8383)       → 高质量唇形同步
4. 视频合成:   FFmpeg                          → 透明/合成背景输出

API 端点:
  GET  /api/digital-human/v2/list              - 列出所有数字人
  GET  /api/digital-human/v2/status            - 服务状态
  GET  /api/digital-human/v2/tasks             - 任务列表
  GET  /api/digital-human/v2/task/:id          - 任务详情
  POST /api/digital-human/v2/upload            - 上传视频
  POST /api/digital-human/v2/train             - 训练数字人 (RMBG+Duix)
  POST /api/digital-human/v2/generate          - 生成口播视频
  POST /api/digital-human/v2/generate-batch    - 批量生成
  DELETE /api/digital-human/v2/delete/:id      - 删除数字人
  
  GET  /api/digital-human/v2/voices            - 声音库列表
  POST /api/digital-human/v2/voice/train       - Few-shot 声音克隆
  POST /api/digital-human/v2/voice/synthesize  - 合成克隆语音
  POST /api/digital-human/v2/voice/preview     - 试听声音
  DELETE /api/digital-human/v2/voice/delete    - 删除声音
  GET  /api/digital-human/v2/voice/audio/:id   - 获取音频文件
  
  GET  /api/digital-human/v2/videos            - 视频列表
  GET  /api/digital-human/v2/video/:file       - 获取视频文件
  DELETE /api/digital-human/v2/video/delete/:id- 删除视频
    N)Path)datetime)
HTTPServerSimpleHTTPRequestHandler)ThreadingMixInc                     [        U 5      R                  SS5      n[        U5      S:  a&  US   S:X  a  US   R                  5       nSU USS  3nU$ )	u   
将 Windows 路径转换为 WSL2 可访问路径。
Duix.Avatar 运行在 WSL2 (Linux) 中，无法识别 Windows 盘符路径，
必须转换为 /mnt/d/... 格式。

例: D:\AI-Ad-Agent\xxx.wav → /mnt/d/AI-Ad-Agent/xxx.wav
\/      :r   z/mnt/N)strreplacelenlower)win_pathpdrives      0D:\AI-Ad-Agent\portal\digital_human_server_v2.pyto_wsl_pathr   9   sY     	HdC(A
1v{qts{!

E71QR5'"H    zhttp://127.0.0.1:8383zhttp://127.0.0.1:18180zws://127.0.0.1:10095zhttp://127.0.0.1:9880zhttp://127.0.0.1:8188z$D:\AI-Ad-Agent\ffmpeg\bin\ffmpeg.exez%D:\AI-Ad-Agent\ffmpeg\bin\ffprobe.exez7D:\AI-Ad-Agent\digital_human_v2\default\default_ref.wavc           
      P    [         SSSSSS[        U 5      /n[        R                  " USSSS	9nUR                  S
:X  aB  UR
                  R                  5       (       a#  [        UR
                  R                  5       5      $ g! [         a  n[        SU 35         SnAgSnAff = f)u>   使用 ffprobe 获取音频时长（秒），失败返回 None-verror-show_entriesformat=duration-of"default=noprint_wrappers=1:nokey=1T
   capture_outputtexttimeoutr   u#   [音频探测] 获取时长失败: N
FFPROBE_PATHr   
subprocessrun
returncodestdoutstripfloat	Exceptionprint)
audio_pathcmdresultes       r   get_audio_durationr2   S       9$.7
O	
 DtRP!fmm&9&9&;&;,,.//   93A37889   A?B 
B%B  B%c           
      P    [         SSSSSS[        U 5      /n[        R                  " USSSS	9nUR                  S
:X  aB  UR
                  R                  5       (       a#  [        UR
                  R                  5       5      $ g! [         a  n[        SU 35         SnAgSnAff = f)u>   使用 ffprobe 获取视频时长（秒），失败返回 Noner   r   r   r   r   r   Tr   r    r   u#   [视频探测] 获取时长失败: Nr$   )
video_pathr/   r0   r1   s       r   get_video_durationr7   c   r3   r4   c                     SSK n[        U S5       nUR                  S5      n[        U5      S:  a
   SSS5        gUR                  " SUSS 5      u  pEnUS:w  d  US	:w  a
   SSS5        gSnUS:  a  UR                  " S
X7US-    5      u  pUS:X  a  X7S-   US-   U	-    n
UR                  " SU
SS 5      u  pnUR                  " SU
SS 5      u  pn[        U 5      R                  5       R                  nUUS-   U	-   S-   -
  nUS:  a  UU-  nSU4sSSS5        $ OUSU	-   -  nUS:  a  M   SSS5        g! , (       d  f       g= f! [         a  n[        SU 35         SnAgSnAff = f)uJ   验证 WAV 文件头有效性，返回 (valid: bool, duration: float|None)r   Nrb,   )FNz<4sI4s   s   RIFFs   WAVEz<4sI   s   fmt z<HHIz<IHH   T)TNu   [WAV验证] 异常: )
structopenreadr   unpackr   statst_sizer,   r-   )r.   r>   fheaderriffsizewave
fmt_offsetchunk_id
chunk_sizefmt_dataaudio_formatchannelssample_rate	byte_rateblock_alignbits_per_sample	file_size	data_sizedurationr1   s                        r   validate_wav_headerrV   s   s   *d#qVVBZF6{R" $#  &}}Xvcr{CDw$'/" $# Jr/'-}}VVzZ[|=\']$w&%l:a<
3JKH:@--PXY[Z[P\:]7LK>DmmFT\]^_aTb>c;IO $Z 0 5 5 7 ? ?I )Z!^j-H1-L MI 1}#,y#8#X~) $#* a*n,
 r/ / $##0  $QC()s]   E "D7E &D7#E ,B&D7	E D7-D7.E 7
EE E 
E*E%%E*zD:\AI-Ad-Agent\digital_human_v2uploadstrainedoutputstempvoicesmodelsbackgroundsTparentsexist_okzdigital_humans.jsonzvoices.jsonz
tasks.jsoni>"  uT   您好，我是山西正方元数字人{name}，为您提供在线数字人口播！)digital_humansr[   tasksc                     Uc  0 n U R                  5       (       a+  [        U SSS9 n[        R                  " U5      sS S S 5        $  U$ ! , (       d  f       U$ = f! [         a     U$ f = f)Nrutf-8encoding)existsr?   jsonloadr,   )pathdefaultrD   s      r   
_load_jsonrm      sq    ;;==dC'2ayy| 32 
 N	 32 N  Ns-    A A
	A 

AA A 
A*)A*c                     U(       a  [         R                  U5      OSnU(       a  UR                  5          [        X5        U(       a  UR	                  5         gg! U(       a  UR	                  5         f f = f)u`   线程安全 + 原子写入（兼容旧接口，新代码请用 _save_json_nolock + 显式锁）N)_json_locksgetacquire_save_json_nolockrelease)rk   data	lock_namelocks       r   
_save_jsonrw      sP    )2;??9%D$%LLN 4LLN s   A A5c                  "    [        [        0 5      $ N)rm   DIGITAL_HUMANS_FILE r   r   _load_digital_humansr|      s    )2..r   c                 "    [        [        U SS9  g )Nra   ru   )rw   rz   rt   s    r   _save_digital_humansr      s    "D4DEr   c                      SS K n [        [        0 5      nSnUR                  5        H$  u  p4SU;  d  M  U R	                  SS5      US'   SnM&     U(       a  [        U5        U$ )Nr   Fseedr   T)randomrm   VOICES_FILEitemsrandint_save_voices)r   rt   
needs_savevidvoices        r   _load_voicesr      s[    k2&DJjjl
"NN1j9E&MJ # TKr   c                 "    [        [        U SS9  g )Nr[   r~   )rw   r   r   s    r   r   r      s    {DH5r   c                  "    [        [        0 5      $ ry   )rm   
TASKS_FILEr{   r   r   _load_tasksr      s    j"%%r   c                     [         S      [        5       nXU '   [        [        U5        S S S 5        g ! , (       d  f       g = f)Nrb   )ro   r   rr   r   )task_id	task_datarb   s      r   
_save_taskr      s.    	W	"g*e, 
		s	   2
A c           
      v   [         S      [        5       nX;   a$  X    R                  U5        [        [        U5        O([        SU  S[        UR                  5       5       35        S S S 5        SU;   a  US   S;   a  [        R                  " S5        [        5       nX;   at  X0   R                  S5      US   :w  aY  [        SU  SUS    35        [         S      [        5       nX;   a#  X@   R                  U5        [        [        U5        S S S 5        g g g g g ! , (       d  f       N= f! , (       d  f       g = f)	Nrb   u   [UpdateTask] 警告: 任务 u/    不存在于 tasks.json，跳过更新 kwargs=status)	completedfailed333333?u'   [UpdateTask] 状态未生效！重试: z -> )ro   r   updaterr   r   r-   listkeystimesleeprp   )r   kwargsrb   tasks2tasks3s        r   _update_taskr      s    	W	N!!&)j%0 0	9himntnynyn{i|h}~ 
 6fX.2II

3!4!4X!>&BR!R;G9DPXIYHZ[\W%$$O**62%j&9	 &% "S J 
	 &%s   AD3D*
D'*
D8c                     [        U 5      n U R                  S5      nU R                  S5      nU(       dg  U R                  5       (       aR   [        R                  " U R                  SS95      nU(       a&  [        SU R                   S[        U5       S35        g [        US	SS9 n[        R                  " XS
SS9  SSS5        UR                  5       R                  S:  a  [        SU 35        gU R                  5       (       a!   UR                  SS9  U R                  U5        UR                  U 5        g!    N= f! , (       d  f       N= f!    N.= f! [         a  n[        SU  SU 35         SnAgSnAff = f)u   线程安全原子写入（调用方需持有对应锁）

安全措施:
1. 如果数据为空但已有非空备份，阻止写入（防止误清空）
2. 临时文件先验证再替换
z.tmpz	.json.bakre   rf   u(   [安全保护] 拒绝用空数据覆盖 u
   （现有 u    条记录）NwFr   )ensure_asciiindent   u'   [保存JSON失败] 临时文件为空: T
missing_oku   [保存JSON失败] : )r   with_suffixrh   ri   loads	read_textr-   namer   r?   dumprB   rC   unlinkrenamer,   )rk   rt   tmp_pathbak_pathexistingrD   r1   s          r   rr   rr      sU   1Dz##F+##K0 ::dnngn&FGDTYYKzZ]^fZgYhhuvw  (C'2aIIdE!< 3 ==?""Q&;H:FG;;==40H% 	#22 1#D6A3/001sg   A	E AD< E (E?4E 4E 
 E *E <E >E 
EE EE 
F %E;;F c           
      l   [         SSSSSS[        U 5      /n[        R                  " USSS9nUR                  S	:X  a  [
        R                  " UR                  5      nUR                  S
/ 5       H  nUR                  S5      S:X  d  M  UR                  SS5      nUR                  S5      n[        U5      S:X  a  [        US	   5      [        US   5      -  OSnU[        UR                  S0 5      R                  SS	5      5      UR                  SS	5      UR                  SS	5      S.s  $    SS	S	S	S.$ )u1   获取视频信息 (fps, duration, width, height)r   quiet-print_formatri   z-show_streams-show_formatTr!   r"   r   streams
codec_typevideor_frame_ratez25/1r
   r   r   g      9@formatrU   widthheight)fpsrU   r   r   )r%   r   r&   r'   r(   ri   r   r)   rp   splitr   r+   )filepathr/   r0   infostreamfps_str	fps_partsr   s           r   get_video_infor   (  s   wR`befnbo
pC^^C4@FAzz&--(hhy"-Fzz,'72 **^V<#MM#.	CFy>UVCVeIaL)E)A,,??\` %dhhx&<&@&@Q&O P#ZZ3$jj15	  . Qa@@r   c                     [         SS[        U 5      SSSSSS[        U5      /n[        R                  " US	S	S
9nUR                  S:H  $ )u3   从视频提取音频 (16kHz WAV，标准化格式)-y-i-acodec	pcm_s16le-ac1-ar16000Tr   r   )FFMPEG_PATHr   r&   r'   r(   )r6   r.   r/   r0   s       r   extract_audior   ;  sQ     	T4Z;sE7JC
 ^^C4@F!!r   c                 4   [        U5      R                  SSS9  U(       a  SU 3OSn[        SS[        U 5      SSU S	3/nU(       a-  UR	                  S
S5        UR	                  S[        U5      5        [
        R                  " USSS9nUR                  S:H  $ )u   提取视频帧为 PNG 序列Tr^   z-r  r   r   -fimage2	/%06d.pngr   z-r   r   r   )r   mkdirr   r   insertr&   r'   r(   )r6   
frames_dirr   
fps_filterr/   r0   s         r   extract_framesr   E  s    4$7 #3seJT4Zh:,i0C 

1d

1c#h^^C4@F!!r   c                 X   [        [        [        U5      R                  S5      5      5      n[        [        [        U 5      R                  S5      5      5      nUS:X  a  S$ US:X  a  S$ [        SS[        U5      SU  S3S[        U5      SU S3SS	S
SSSSSSS[        U5      /n[        R                  " USSS9nUR                  S:w  a  SSUR                  SS  34$ [        U5      nUR                  5       (       d  SSU 34$ UR                  5       R                  S:X  a  SSU 34$ g)u   
将原始帧 + Alpha 蒙版合成为带透明通道的 WebM 视频
frames_dir: 原始 PNG 帧目录
alpha_dir:  RMBG-2.0 输出的 Alpha 蒙版目录
output_path: 输出 WebM 文件路径
返回: (success: bool, error_msg: str)
*.pngr   Fr   z
-framerater   r   -filter_complexz[0:v][1:v]alphamerge-c:v
libvpx-vp9-pix_fmtyuva420p-b:v0-crf15Tr   u   FFmpeg 合成失败: N,  u   合成后文件未生成: u   合成后文件大小为0: Tok)FuC   Alpha 帧目录为空 (0 个 PNG 文件)，无法合成透明视频)FuC   原始帧目录为空 (0 个 PNG 文件)，无法合成透明视频)r   r   r   globr   r   r&   r'   r(   stderrrh   rB   rC   )	r   	alpha_diroutput_pathr   alpha_countframes_countr/   r0   output_files	            r   frames_to_transparent_videor   S  sR    d4	?//89:KtD,11':;<La\\q\\ 	Tc#hI&c#h9%1JVTKC ^^C4@FA-fmmDS.A-BCCC {#K2;-@@@!!Q&3K=AAAr   c                     SSK Jn  SSKn[        SSSSS[	        U 5      /n[
        R                  " US	S	S
S9n[        R                  " UR                  5      n[        UR                  S0 5      R                  SS5      5      nUS-  n	US-  U-  n
/ n/ n[        U5       GH  nXU
-  -   n[        SS[	        U5      S[	        U 5      SSSSSSSSS/n[
        R                  " US	SS9nUR                  (       a  [        UR                  5      S:  a  Mt  [
        R                  " [        SSSSS S!S"S#[	        U 5      /
S	S	S
S9n[        R                  " UR                  5      nUR                  S$0 /5      S   nUR                  S%S&5      UR                  S'S(5      nnUU-  S-  n[        UR                  5      U:  a  GM   UR                  UR                  SU UR                   S)9R#                  UUS45      nUS*-  US*-  nn[%        US+-  5      [%        US+-  5      nnUUS*-  -
  UUS*-  -
  nnUUUU-   2UUU-   24   nUSS2SS2S4   R'                  [        5      USS2SS2S,4   R'                  [        5      USS2SS2S*4   R'                  [        5      n nnUUS--  :  UU S--  :  -  US.:  -  n!U!R)                  5       U!R*                  -  n"UR-                  U"5        U"S/:  d  GMK  UU!   n#[        U#5      S:  d  GMb  UR/                  U#SS09R'                  [$        5      n$UR-                  U$5        GM     U(       d  g1[)        U5      [        U5      -  n%U%U:  n&U(       a9  UR/                  USS09R'                  [$        5      n'S2U'S   S3 U'S,   S3 U'S*   S3 3n(OS4n([1        S5U%S6 S7U( S8U&(       a  S9OS: 35        U&U%U(4$ ! [2         a  n)[1        S;U) S<35         Sn)A)g1Sn)A)ff = f)=u   
检测视频是否为绿幕背景。
采样若干帧，分析中央区域的绿色占比，并提取主导绿色值。
返回: (is_green_screen: bool, green_ratio: float, green_hex: str)
r   )ImageNr   r   r   ri   r   Tr   r    r   rU   r   r   g?r   -ssr   z-vframesr   r   
image2piper   rgb24z-vcodecrawvideo-   )r!   r#   d   z-select_streamszv:0r   zstream=width,heightr   r   i  r   i  )dtyper   333333?r   g?P   皙?)axis)Fg        0x00FF000x02Xr  u#   [绿幕检测] 平均绿色占比: z.1%u   , 主导绿色: u
   , 判定: u   绿幕   自然背景u   [绿幕检测] 检测失败: u   , 默认为自然背景)PILr   numpyr%   r   r&   r'   ri   r   r)   r+   rp   ranger   r   
frombufferuint8reshapeintastypesumrG   appendmedianr-   r,   )*r6   sample_framesgreen_thresholdr   np	probe_cmdr0   r   rU   
start_timeintervalgreen_ratiosall_green_colorsi	seek_timer/   vinfovdatar   r   hexpected_size	img_arraycxcycrop_wcrop_hx1y1croprd   gb
green_maskgreen_ratiogreen_pixelsmedian_color	avg_greenis_greendominant	green_hexr1   s*                                             r   detect_green_screenr2    s   V& "4/6#S_6		$TSUVzz&--((B/33JBC ^
sNm3}%A"\1IeS^T3z?sD,
Gj#/C  ^^CbIF==C$6$< NNL$RX#4e_#8#j/$K 26D"NE JJu||,EYYy2$/2F::gs+VZZ#-FqAEAIM6==!M1fmmN]&C288T\\^_abde]fgI !VQ!VB S\3q3w<FF&A+%rFaK'7BR6	\2bi<78D 1Qq5k((/a!e1C1CE1JDQRSTUVQVKL^L^_dLe!qA a#g+!a#g+6!b&AJ$..*Z__<K, T!#J/|$s*#%99\9#B#I#I##NL$++L9a &d )%L(99	. yy!1y:AA#FHXa[-hqk#->x{3>OPI"I3Ic?BRS\R]]gt|hp  CQ  hR  S  	TI-- &-aS0GHI%&s,   K$O +O A O B O 
O'O""O'c                 b   [         SS[        U 5      SSU SU 3SSSS	S
SSSSS[        U5      /n[        R                  " USSSS9nUR                  S:w  a  SSUR
                  SS  34$ [        U5      nUR                  5       (       a  UR                  5       R                  S:X  a  SSU 34$ g)ul   
使用 FFmpeg colorkey 滤镜去除绿幕，输出透明通道 WebM。
返回: (success: bool, error: str)
r   r   -vfzcolorkey=0x00FF00:similarity=z:blend=r   r   r   r   r   2Mr   10-auto-alt-refr   Tr   r    r   Fu   FFmpeg 绿幕抠除失败: Nu   绿幕抠除后文件无效: r   
r   r   r&   r'   r(   r   r   rh   rB   rC   )input_videooutput_video
similarityblendr/   r0   r   s          r   chromakey_remove_greenr=    s     	Tc+.zl'%IJfdL	C ^^C4MFA3FMM$34G3HIII|$K;#3#3#5#=#=#B5l^DDDr   c                    U(       a  U(       a  SU SU SU SU S3	nOSn[         SS[        U 5      S[        U5      SUS	S
SSSSSSSSS[        U5      /n[        R                  " USSSS9nUR                  S:w  a  SSUR
                  SS  34$ [        U5      nUR                  5       (       a  UR                  5       R                  S:X  a  SSU 34$ g)ub   
将前景视频（带透明通道）与背景图片合成。
返回: (success: bool, error: str)
z[1:v]scale=r   *:force_original_aspect_ratio=decrease,pad=z9:(ow-iw)/2:(oh-ih)/2[bg];[0:v][bg]overlay=(W-w)/2:(H-h)/2z5[1:v]scale=iw:ih[bg];[0:v][bg]overlay=(W-w)/2:(H-h)/2r   r   r   r   libx264-presetmediumr   18-c:acopy	-shortestr   yuv420pTr   r    r   Fu   FFmpeg 背景合成失败: Nu   背景合成后文件无效: r   r8  )	foreground_videobg_imager:  target_wtarget_hfilter_complexr/   r0   r   s	            r   composite_video_with_bgrM    s    H(1XJ /*AhZ (/0 	 Q 	Tc"#c(m>	9hIL	C ^^C4MFA3FMM$34G3HIII|$K;#3#3#5#=#=#B5l^DDDr   c                    US:X  a*  [         R                  " [        U 5      [        U5      5        gUS:X  a  UR                  S5      n[	        USS S5      [	        USS S5      [	        USS	 S5      pn[
        S
S[        U 5      SSU S3SSSSSS[        U5      /n	[        R                  " U	SSS9n
U
R                  S:H  $ US:X  aa  U(       aZ  [
        S
SSS[        U5      S[        U 5      SSSSSSSSSSS[        U5      /n	[        R                  " U	SSS9n
U
R                  S:H  $ US:X  a*  [         R                  " [        U 5      [        U5      5        gg)u   
将透明背景视频与背景合成
bg_mode: 'transparent' (保持透明), 'color' (纯色), 'image' (背景图), 'natural' (原始背景)
transparentTcolor#r   r   r=   r      r   r   r   z [0:v]format=yuva420p[v];color=0xz7:size=1920x1080[bg];[bg][v]overlay=0:0:format=auto[out]-map[out]r   r@  r   rG  r   image-loopr   z>[1:v]format=yuva420p[fg];[0:v][fg]overlay=0:0:format=auto[out]z1:a?rF  naturalF)	shutilcopy2r   lstripr  r   r&   r'   r(   )r9  	bg_sourcer   bg_modebg_colorrP  rd   r(  r)  r/   r0   s              r   compose_video_with_bgr^    sm   
 -S%s;'78	G	$eAaj"%s51:r':Ca
B<Oa #k".ug5lmGIz9
 DtD  A%%	G		S$I#k"_GVVIz9	
 DtD  A%%	I	S%s;'78r   c                    SU SU SU SU SU SU SU S3n[         SS[        U 5      SS	S[        U5      S
USSSSSSSSSSSSS[        U5      /n	[        SU SU SU 35        [        R                  " U	SSSS9n
U
R
                  S:w  a;  [        U
R                  5      S:  a  U
R                  SS  OU
R                  nS!S"U 34$ [        U5      nUR                  5       (       a  UR                  5       R                  S:X  a  S!S#U 34$ g$)%uW  
【单阶段方案】chromakey 去绿幕 + 背景图合成，一步完成。
避免两阶段处理（WebM 中间编码）导致的 alpha 通道丢失。

参数:
    input_video:   Duix 输出的原始视频（含绿幕背景）
    bg_image_path: 背景图片路径
    output_video:  输出 MP4 路径
    green_hex:     绿幕颜色 (如 '0x68CA50')，从 detect_green_screen 检测得出
    similarity:    chromakey 相似度 (0.01-1.0)，越小越严格
    blend:         边缘混合 (0.0-1.0)，越大边缘越柔和
    target_w/h:    目标分辨率

返回: (success: bool, error: str)
z[0:v]chromakey=r   z[fg];[1:v]scale=r?  zI:(ow-iw)/2:(oh-ih)/2:color=black[bg];[bg][fg]overlay=(W-w)/2:(H-h)/2[out]r   r   rV  r   r   rS  rT  z0:a?r   r@  rA  rB  r   rC  r   rG  rF  u   [绿幕合成] chromakey=, similarity=, blend=Tr   r    r     iNFu   FFmpeg 绿幕合成失败: u   绿幕合成后文件无效: r   r   r   r-   r&   r'   r(   r   r   r   rh   rB   rC   )r9  bg_image_pathr:  r1  r;  r<  rJ  rK  rL  r/   r0   stderr_tailr   s                r   green_screen_compositerf  F  sX   . )Aj\5' :Zq
 +j( $/	0  	Tc+dC.>	9hILC 
%i[j\RWQX
YZ^^C4MFA.1&--.@3.FfmmDE*FMM3K=AAA|$K;#3#3#5#=#=#B5l^DDDr   c                    [         SS[        U 5      SSU SU SU 3SSSS	S
SSSSS[        U5      /n[        SU SU SU 35        [        R                  " USSSS9nUR
                  S:w  a;  [        UR                  5      S:  a  UR                  SS OUR                  nSSU 34$ [        U5      nUR                  5       (       a  UR                  5       R                  S:X  a  SSU 34$ g)ut   
chromakey 去绿幕 → 输出透明通道 WebM（无背景图时使用）。

返回: (success: bool, error: str)
r   r   r4  z
chromakey=r   r   r   r   r   r   r5  r   r6  r7  r   u   [去绿幕→透明] chromakey=r`  ra  Tr   r    r   NFu   FFmpeg 去绿幕失败: u   去绿幕后文件无效: r   rc  )	r9  r:  r1  r;  r<  r/   r0   re  r   s	            r   green_screen_to_transparentri  ~  s    	Tc+I;a
|1UG<JfdL	C 
+I;mJ<xX]W^
_`^^C4MFA.1&--.@3.FfmmDE*FMM0>>>|$K;#3#3#5#=#=#B2<.AAAr   c                   4    \ rS rSrSrS rS rS rS	S jrSr	g)
RMBGMattingEnginei  u*   通过 ComfyUI RMBG-2.0 进行逐帧抠图c                     [         U l        g ry   )COMFYUI_URLcomfyui_urlselfs    r   __init__RMBGMattingEngine.__init__  s
    &r   c                     [         R                  R                  U R                   S35      n[         R                  R	                  USS9 nUR
                  S:H  sSSS5        $ ! , (       d  f       g= f! [         a     gf = f)u$   检查 ComfyUI RMBG-2.0 是否可用z/system_statsr   r#      NF)urllibrequestRequestrn  urlopenr   r,   rp  reqresps      r   check_available!RMBGMattingEngine.check_available  sk    	..((D,<,<+=])KLC''Q'74{{c) 877 		s0   A
A6 A%	A6 %
A3/A6 3A6 6
BBc                 	   SSK n[        US5       nUR                  5       nSSS5        SSKnU R                   S3n[
        R                  R                  U5      nS[        R                  " 5       R                  R                  5       -   n	/ n
U
R                  SU	-   5        U
R                  SU S3R                  5       5        U
R                  S	5        U
R                  S
5        U
R                  W5        U
R                  SU	-   5        U
R                  S5        U
R                  S
5        U
R                  S5        U
R                  SU	-   S-   5        SR                  U
5      n[        R                  R!                  X{SSSU	R#                  5        30S9n[        R                  R%                  USS9 n[&        R(                  " UR                  5       5      nSSS5        WR+                  SU5      nSSUSS.S.SSSS/0S.SSS/S[        R                  " 5       R                  SS  3S S!.S.S".0nU R                   S#3n[        R                  R!                  U[&        R,                  " U5      R                  5       SSS$0S9n[        R                  R%                  US%S9 n[&        R(                  " UR                  5       5      nSSS5        WR+                  S&5      n[/        S'5       GH  n[0        R2                  " S(5        U R                   S)U 3n[        R                  R!                  U5      n[        R                  R%                  US*S9 n[&        R(                  " UR                  5       5      nSSS5        UW;   d  M  UU   R+                  S+0 5      nUR5                  5        H  u  nnUR+                  S,/ 5      nU(       d  M!  US   nU R                   S-US.    S/US0    S1UR+                  S2S35       3n[        R                  R!                  U5      n[        R                  R%                  USS9 n[        US45       nUR7                  UR                  5       5        SSS5        SSS5            g5     g6   g6! , (       d  f       GNo= f! , (       d  f       GN= f! , (       d  f       GN= f! , (       d  f       GNI= f! , (       d  f       Nh= f! , (       d  f       Nq= f)7u=   
使用 ComfyUI API 抠图单帧
返回抠图后的 RGBA PNG
r   Nr9   z/upload/images   ----FormBoundary   --z8Content-Disposition: form-data; name="image"; filename=""s   Content-Type: image/pngr   s+   Content-Disposition: form-data; name="type"s   input   
POSTContent-Typezmultipart/form-data; boundary=rt   methodheaders   rt  r   prompt	LoadImagerU  )rU  upload)
class_typeinputsBRIA_RMBG_Zhor   z
Image Save2rmbg_r<   PNG)imagesfilename_prefix	file_type)r   r  3z/promptapplication/jsonr   	prompt_idx   r   z	/history/   rY   r  z/view?filename=filenamez&type=typez&subfolder=	subfolderr   wbTF)base64r?   r@   iorn  osrk   basenameuuiduuid4hexencoder  joinrv  rw  rx  decodery  ri   r   rp   dumpsr	  r   r   r   write)rp  
input_pathr   r  rD   img_datar  
upload_urlr  boundary
body_partsbodyr{  r|  upload_resultuploaded_nameworkflow
prompt_urlprompt_resultr  _history_urlhistoryrY   node_idnode_outputr  img_infoimg_urls                                r   _rmbg_single_frame$RMBGMattingEngine._rmbg_single_frame  s   
 	 *d#qvvxH $ 	(()7
77##J/&)9)9)@)@)BB
%(*+TU]T^^_`ggij45#(#%(*+HI#(#%(*U23||J'nn$$&#'EhooFWEX%YZ % 
 ^^##C#4 JJtyy{3M 5 &))&(; "-(5I
 #2&a1
 #/#&(-24::<3C3CBQ3G2H+I%*
, (()1
nn$$H%,,.#%78	 % 
 ^^##C#4 JJtyy{3M 5 "%%k2	 sAJJqM!--.i	{CK..((5C''Q'74**TYY[1 8G#!),00B?,3MMO(G[(__Xr:Fv#)!9%)%5%5$6ohzFZE[[abjkqbrass~  @H  @L  @L  MX  Z\  @]  ^  #_$nn44W=#^^33C3D!%k4!8A !		 4 "9 E  $ -< ' & q $#8 54F 54 87 "9!8 EDsS   R
7%R(%R.%S S# S	0S#

R
R+.
R= 
S	
S S##
S1Nc           
         [        U5      R                  SSS9  [        U5      R                  S[        R                  " 5       R
                  SS  3-  nUR                  SSS9  [        SU 35        [        U[        U5      5      (       d  g[        UR                  S5       Vs/ s H  oUPM     sn5      n[        U5      n[        S	U S
35        Sn[        U5       H  u  p[        U5      U	S-   S S3-  n U R                  [        U
5      [        U5      5      (       aC  US-  nU(       a5  U	S-  S:X  a*  [        U	S-   U-  S-  5      n[        X<SU	S-    SU S3S9  M  M  M  M     ["        R$                  " [        U5      SS9  [        SU SU S35        U$ s  snf ! [          a  n[        SU	S-    SU 35         SnAM  SnAff = f)u]   
从视频逐帧提取 Alpha 蒙版
返回: alpha_dir 中的 PNG 序列，同时返回帧数
Tr^   raw_frames_Nr<   u   [RMBG] 提取视频帧: r   r   u   [RMBG] 共 u    帧，开始逐帧抠图...r   06d.pngr   r   u   抠图进度: r
   u    帧progressmessageu   [RMBG] 帧 u    抠图失败: ignore_errorsu   [RMBG] 抠图完成: u
    帧成功)r   r   parentr  r  r  r-   r   r   sortedr   r   	enumerater  r  r   r,   rX  rmtree)rp  r6   r   r   r   rD   frame_filestotal_framessuccess_countr  
frame_fileout_pathr  r1   s                 r   extract_alpha_from_video*RMBGMattingEngine.extract_alpha_from_video  s   
 	YdT: )_++DJJL<L<LRa<P;Q.RR
5(56j#j/:: )AB)AAa)ABC;'L>)EFG &{3MAIAaC9D)99H=**3z?CMJJ!Q&M1r6Q;#&A'='C#D$W.<QqSE<.PT,UW $/w K 4 	c*oT:%m_Al^:NO1 C  =AaC5s;<<=s   &F:A&F!!
G
+GG
)rn  ry   )
__name__
__module____qualname____firstlineno____doc__rq  r}  r  r  __static_attributes__r{   r   r   rk  rk    s    4'`D(r   rk  c                       \ rS rSrSr\R                  " 5       rS rS r	\
SS j5       rS r   SS jr  SS	 jrS
 rS rS rSS jrSS jrS rS rSrg)GPTSoVITSEnginei=  u   GPT-SoVITS v2 API 封装

并发安全: 类级别互斥锁，确保同一时间只有一个声音合成请求在执行。
GPT-SoVITS 是单模型服务，并发请求会导致音色混淆和发音错误。
c                 (    [         U l        S U l        g ry   )GPT_SOVITS_URLbase_urlcurrent_voice_idro  s    r   rq  GPTSoVITSEngine.__init__E  s    & $r   c                      [         R                  R                  U R                   S35      n[         R                  R	                  USS9 n SSS5        g! , (       d  f       g= f! [
         a     gf = f)u   检查 GPT-SoVITS 是否可用z/docsr   rt  NTF)rv  rw  rx  r  ry  r,   rz  s      r   r}  GPTSoVITSEngine.check_availableI  s_    	..((DMM?%)@AC''Q'74 877 		s0   A
A( AA( 
A%!A( %A( (
A54A5c                    U (       d  U $ SSK nUR                  SSU 5      n UR                  SSU 5      n UR                  SSU 5      n UR                  S	S
U 5      n U (       a  U S   S;  a  U S-  n US::  a  UR                  SSU 5      n O-US:  a  UR                  SSU 5      n OUR                  SSU 5      n UR                  SSU 5      n U R                  5       n U $ )u~  
v4.0 中文文本智能预处理：韵律自然版

处理内容:
1. 修正中文标点符号（半角→全角，确保 TTS 引擎正确解析停顿）
2. 仅在末尾无标点时补充句号
3. 根据 speed 参数调整句间停顿间隔
4. 处理英文/数字与中文混排（添加空格分隔）
5. v4.0: 保留标点原有节奏，不插入额外换行打断语调连贯性
r   Nz((?<=[^\x00-\x7F])\.(?=\s|$|[^\x00-\x7F])u   。z'(?<=[^\x00-\x7F]),(?=\s|$|[^\x00-\x7F])u   ，z'(?<=[^\x00-\x7F])!(?=\s|$|[^\x00-\x7F])u   ！z((?<=[^\x00-\x7F])\?(?=\s|$|[^\x00-\x7F])u   ？u0   。！？.!?…~～、，,;；:：)）"」』·
皙?u   ([。！？])\s*z\1\ng?z\1z\n{3,}z

)resubr*   )r"   speedr  s      r   _preprocess_chinese_text(GPTSoVITSEngine._preprocess_chinese_textR  s     K vvA5$Ovv@%Nvv@%NvvA5$O DH$WWEMD C<66-w=Dc\66-ud;D 66-w=D vvi.zz|r   c                     U R                    S[        R                  R                  U5       3n[        R                  R                  U5      n[        R                  R                  USS9 n[        R                  " UR                  5       5      nSSS5        U R                    S[        R                  R                  U5       3n[        R                  R                  U5      n[        R                  R                  USS9 n[        R                  " UR                  5       5      nSSS5        g! , (       d  f       N= f! , (       d  f       g= f! [         a  n[        SU 35         SnAgSnAff = f)	u   切换 GPT-SoVITS 模型z/set_gpt_weights?weights_path=r  rt  Nz!/set_sovits_weights?weights_path=Tu!   [GPT-SoVITS] 切换模型失败: F)r  rv  parsequoterw  rx  ry  ri   r   r@   r,   r-   )rp  gpt_weights_pathsovits_weights_pathurlr{  r|  r0   r1   s           r   	set_modelGPTSoVITSEngine.set_model~  s   	]]O#A&,,BTBTUeBfAghC..((-C''R'8DDIIK0 9 ]]O#DV\\EWEWXkElDmnC..((-C''R'8DDIIK0 9  98 98  	5aS9:	sO   A+E -%D3A3E %E*E 3
E=E 
EE E 
E7E22E7Nc                   ^  SSK m US;   a  U R                  XS9nT R                    U R                  R	                  SS9nU(       d  [        S5      e U(       a_  [        U5      R                  5       (       aA  U R                  USS	9nU(       a  [        U5      R                  5       (       d  [        U5      nO@[        n[        U5      R                  5       (       a  Un[        S
U 35        O[        S5      eU(       dL  U(       aE   [        S5        U R                  U5      nU(       a  Un[        SUSS  S35        O[        S5         U R                  U5        U(       a  U(       a  U R                  Xg5        U	S:X  a  Su  nnnOU	S:X  a  Su  nnnOSu  nnn0 SU_SU_SU_SU_SU_SU_SS_S S_S!S"_S#S$_S%U_S&U_S'U_S(U
c  S)OU
_S*S+_S,S-_nS.R                  U 4S/ jUR!                  5        5       5      nU R"                   S0U 3nT R$                  R'                  U5      n[)        U5      n[+        [-        US1-  S25      S35      nS4nSn[/        U5       H7  n T R$                  R1                  UUS9 nUR3                  5       nSSS5          O   U(       aK  [C        US=5       nURE                  W5        SSS5        [        U5      U R                  RG                  5         $ WU R                  RG                  5         $ ! [
         a  n[        SU S35         SnAGNSnAff = f! , (       d  f       N= f! T R4                  R6                  [8        R:                  [<        4 aQ  nUnUUS$-
  :  a?  US$-   S5-  n[        S6US$-    S7U S8U S9U S:3	S;S<9  [>        R@                  " U5         SnAGMZ  UeSnAff = f! , (       d  f       GN= f! U R                  RG                  5         f = f)>u  
合成语音（v6.0 音色克隆稳定性版 — 修复 v4.0 致命参数错误）
text: 待合成文本
ref_audio_path: 参考音频路径或 None（使用默认音色）
ref_text: 参考音频对应文本（关键参数，准确填写极大提升音色贴合度）
          为空时自动调用 ASR 转录参考音频
language: zh/en/ja/ko/yue/auto
speed: 语速倍率
gpt_weights/sovits_weights: 可选，切换到指定声音模型
output_path: 保存路径，None 则返回音频字节
quality: "expressive"(默认情感优先)/"balanced"(均衡)/"precise"(发音优先)

v7.0 全面修复（情感恢复 + 抑扬顿挫）:
- text_split_method: cut5→cut1：恢复逐句切分，每句独立语调（cut5合并句子导致情感被平均化）
- temperature: 0.72→0.78（expressive）：适度提高波动，增加抑扬顿挫
- fragment_interval: 0.3→0.2s：缩短句间停顿，更自然的说话节奏
- 中文预处理：保持标点节奏标记，确保每句语调独立
r   N)zhyueauto)r  r   rt  uY   GPT-SoVITS 模型正忙（有其他用户的语音合成在进行中），请稍后重试qualitymodeu-   [GPT-SoVITS] 使用系统默认参考音频: uH   无法合成语音：未提供参考音频且系统默认音频不存在u:   [GPT-SoVITS] ref_text 为空，自动转录参考音频...u!   [GPT-SoVITS] 自动转录成功: 2   ...uX   [GPT-SoVITS] 自动转录失败，使用空参考文本（音色贴合度可能降低）u!   [GPT-SoVITS] 自动转录异常: u   ，继续使用空参考文本
expressive)g(\?r   g333333?precise)g?   gffffff?)g(\?r=   r  r"   	text_langref_audio_pathprompt_langprompt_textspeed_factor
media_typewavstreaming_modetext_split_methodcut1
batch_sizer   top_ktop_ptemperaturer   r  repetition_penaltyg?fragment_intervalg?&c              3   z   >#    U  H0  u  pU S TR                   R                  [        U5      5       3v   M2     g7f)=N)r  r  r   ).0kvrv  s      r   	<genexpr>-GPTSoVITSEngine.synthesize.<locals>.<genexpr>  s4     #cTbDAqc6<<+=+=c!f+E*F$GTbs   8;z/tts?r  r  X  r   r   u'   [GPT-SoVITS] HTTP 请求失败 (尝试 r
   ): , u   s 后重试...Tflushr  )$urllib.parser  r  _model_lockrq   r,   r   rh   _prepare_ref_audior   DEFAULT_REF_AUDIOr-   _transcribe_audio_preload_ref_audior  r  r   r  rw  rx  r   minmaxr	  ry  r@   r   URLErrorsocketr#   ConnectionErrorr   r   r?   r  rs   )!rp  r"   r  ref_textlanguager  gpt_weightssovits_weightsr   r  r   _model_lock_acquiredprocessed_refdefault_ref	auto_textr1   _temp_top_k_top_pparamsquery_stringr  r{  text_lenr#   max_retries
last_errorattemptr|  
audio_datawaitrD   rv  s!                                   @r   
synthesizeGPTSoVITSEngine.synthesize  sO   * 	 ,,000CD  $//777D#wxxj	'$~"6"="="?"? $ 7 7Y 7 W$D,?,F,F,H,H$'$7M 0$++--$/MI+WX#$noo 	aVX $ 6 6} EI #, A(3B-PSTU xz
 ##M2 ~{; ,&(6%vvI%(6%vv )7%vvX !- x	
 x  e !! $V a   u dl %d  $S!F& 88#cTZT`T`Tb#ccL]]O57C..((-C4yH#h!mS137G KJ -)//W/E%)YY[
 F	 . +t,GGJ' -;' $$& $$&a ! a=aS@^_``az FE --v~~O )!"Jq0 '!q0 GPQ	{RST_S``cdecffhimhnn|}  FJ  K

4(((() -, $$&s   B4O 7L ?O  L DO L<9L+
L<O )N?;O (O 
L(L#O #L((O +
L9	5L<</N<+AN7.O 5N77N<<O ?
O	O O-c                 h   S[        [        R                  " 5       5       3nUc	  [        U-  n[        U5      nUR	                  SSS9  US-  n[
        SS[        U5      SS	S
SSS[        U5      /n[        R                  " USS9  U R                  [        U5      5      n	US-  n
U	S:  a  U	S-  nU	S-  n[        XS-
  5      n[        SX-
  U-
  5      nUS:  a  Sn[        SU	S-
  5      n[
        SS[        U5      S[        U5      S[        U5      SS	S
SSS[        U
5      /n[        R                  " USS9  U R                  [        U
5      5      n[        SU	S SUS S35        O+SSKnUR                  " [        U5      [        U
5      5        U	nSn U R                  [        U
5      5      nU(       a  UOSnU(       a  [        S USS!  S"35        O[        S#5         UUU[        U
5      [        U5      USSS&["        R$                  " 5       R'                  5       UU	[        [        R                  " 5       S'-  5      S(-  S).nXF S*3-  nXF S+3-  nUR)                  5       (       a!  [        U5      US,'   [        U5      US-'   S.US/'   UU4$ ! [          a  n[        S$U S%35         SnANSnAff = f)0u7  
v5.0 增强版声音克隆:
- 保存完整参考音频 (reference_full.wav)
- 自动提取最佳 3-10s 片段 (reference.wav) 用于 Zero-shot 推理
- ASR 转录最佳片段（而非完整音频，速度更快更准确）

Zero-shot 模式：使用 GPT-SoVITS 预训练模型 + 参考音频进行实时克隆
vc_NTr^   zreference_full.wavr   r   r   r   r   r   r   r   r!   reference.wavr   皙?r         @g      "@r         ?      ?r   -tu   [GPT-SoVITS] 声音训练: 从 .1fu   s 截取最佳 u   s 片段r   r   u   [GPT-SoVITS] 声音训练 ASR: <   r  uB   [GPT-SoVITS] 声音训练 ASR 返回空，将使用空参考文本u   [GPT-SoVITS] ASR 失败: u   ，使用空参考文本	zero_shot  r   )idr   r&  	ref_audioref_audio_fullr%  r'  r(  r  
created_atrU   full_durationr   z	_gpt.ckptz_sovits.pthr'  r(  few_shotr  )r  r   
VOICES_DIRr   r   r   r   r&   r'   _get_audio_durationr!  r   r-   rX  rY  r  r,   r   now	isoformatrh   )rp  r.   
voice_namer&  
output_dirr   voice_idref_full_pathr/   rK  r  marginmid
clip_startclip_durtrim_cmdref_durationrX  r%  
asr_resultr1   
voice_info	gpt_modelsovits_models                           r   train_voiceGPTSoVITSEngine.train_voice   s    TYY[)*+#h.J*%
5 #%99tS_{E3w

 	s4000]1CD $o52"T)F!#CV3Y/J3 :V CDH!| 
sMC$78 T4]);s:c(m;sE7N#	H NN8D933C4GHL3M#3FoVbcfUggopq LL]+S-@A(L 	K//N0CDJ%/zRH7"cJKZ\  ^,!-0 "",,.224$*		d*+j8

" :Y!77	!j$<<(+IJ}%+.|+<J'(!+Jv##9  	K-aS0HIJJ	Ks   >J J 
J1J,,J1c                     U R                  U5      nU(       a  U$ [        S5        U R                  U5      nU(       a  [        SUSS  S35        U$ [        S5        g)u\  
v5.0 增强版多级音频转录:

Fallback 链:
1. Duix ASR WebSocket (主方案，GPU加速)
2. faster-whisper 本地转录 (fallback，可靠)
3. 所有方案失败后返回空字符串

参考音频转录的准确性直接影响 GPT-SoVITS 音色克隆质量。
prompt_text 越准确，GPT-SoVITS 越能精确提取说话人的音色特征。
u=   [ASR] Duix ASR 失败，尝试 faster-whisper 本地转录...u   [ASR-Whisper] 转录成功: NrD  r  u`   [ASR] 所有转录方案均失败，将以空参考文本继续（音色贴合度可能降低）r   )_try_duix_asrr-   _try_whisper_asr)rp  r.   	full_texts      r   r  !GPTSoVITSEngine._transcribe_audio  sf     &&z2	 	MN))*5	03B0@DEpqr   c           	        ^ SSK n[        S5       GH  n / mSnU4S jn UR                  5       nUR                  [        SS9  SnUR                  [        R                  " S	S
S.5      5        [        US5       nSn UR                  U5      n	U	(       d  OUR                  U	5        M,  SSS5        UR                  [        R                  " SS05      5        [        R                  " 5       n
[        R                  " 5       U
-
  S:  aH   UR                  S5        UR                  5       nU" Xk5        [        R                  " 5       U
-
  S:  a  MH  U(       a   UR                  5         O SR                  T5      R!                  5       nU(       a  [#        SUS-    SUSS  S35        Us  $ US:X  a$  [#        S5        [        R$                  " S5        GM  GM     g! , (       d  f       GN;= f!    M  = f!    N= f! U(       a   WR                  5         f !    f = ff = f! [&         aL  nUS:X  a,  [#        SU S35        [        R$                  " S5         SnAGMP  [#        SU 35         SnAGMe  SnAff = f)u    使用 Duix ASR WebSocket 转录r   Nr   Fc                    >  [         R                  " U5      nUR                  SUR                  SS5      5      nU(       a  TR                  U5        g g !    g = f)Nr"   r0   r   )ri   r   rp   r  )wsr  rt   r"   result_texts       r   
on_message1GPTSoVITSEngine._try_duix_asr.<locals>.on_message  sQ    #zz'2#xx20FG'..t4  s   AA Ar   rt  Tstartr  )r  langr9   i    r  endr<   g      ?r   u!   [ASR-Duix] 转录成功 (attempt r   r  rD  r  u6   [ASR-Duix] 第 1 次转录无结果，1秒后重试...u   [ASR-Duix] 第 1 次失败 (u   )，1秒后重试...u   [ASR-Duix] 第 2 次也失败: )	websocketr	  	WebSocketconnectDUIX_ASR_URLsendri   r  r?   r@   send_binaryr   
settimeoutrecvcloser  r*   r-   r   r,   )rp  r.   ro  r5  ws_connectedrj  rh  rD   rK   chunktimeout_startmsgrd  r1   ri  s                 @r   rb  GPTSoVITSEngine._try_duix_asr  s.   QxG;A $!",,.BJJ|RJ8#'LGGDJJ'FGHj$/1%)
"$%FF:$6E#( %NN51	 # 0 GGDJJ78$(IIKM))+59"MM#."$'')C&r/	 ))+59 $!HHJ $ GGK0668	=gai[IVYWYNK[[^_`$$a<RTJJqM  g  | S 0/ "!
! 	 $!HHJ! 	 $  Aa<8;OPQJJqMM;A3?@@As   
H%AH ;0G+A H )G15H H%G9+AH%/'H%
G.	)H 1G63H 9G=;H% H"	HH"H	H""H%%
I;/+I6!I66I;c                     SSK Jn  SnSn SSKnUR                  R	                  5       (       a*  Sn[        SUR                  R                  S5       S35        U" US	US
9nUR                  USSS9u  pxSR                  S U 5       5      n	U	(       a  U	$  g!    NB= f! [         a    [        S5         g[         a  n
[        SU
 35         Sn
A
gSn
A
ff = f)u@   使用 faster-whisper 本地转录（GPU加速，CPU fallback）r   )WhisperModelsmallint8Nfloat16u   [ASR-Whisper] 使用 GPU ()r  )devicecompute_typer  r  )r&  	beam_size c              3   T   #    U  H  oR                   R                  5       v   M      g 7fry   )r"   r*   )r  segs     r   r  3GPTSoVITSEngine._try_whisper_asr.<locals>.<genexpr>  s      FXc!1!1Xs   &(u&   [ASR-Whisper] faster-whisper 未安装u   [ASR-Whisper] 转录失败: r   )faster_whisperr~  torchcudais_availabler-   get_device_name
transcriber  ImportErrorr,   )rp  r.   r~  
model_sizer  r  modelsegmentsr   rd  r1   s              r   rc   GPTSoVITSEngine._try_whisper_asr  s    	63 !J!L::**,,#,L6uzz7Q7QRS7T6UUVWX !FVE"--j4ST-UNH FX FFI      	<:;   	60455	6s5   
B AB ;B BB C6	C?CCc                 n   Ub  US::  a  U R                  U5      n[        U5      R                  n[        US-  5      n[        U5      R	                  5       (       af   U R                  U5      n[        U5      R                  5       R                  nSUs=::  a  S::  a  O  OUS:  a  U$ [        U5      R                  5         US:  a  U$ US-  n[        S	X(S-  -
  5      n	U	S:  a  Sn
[        US-
  S
5      nODUS-  nXS-  -
  n
XS-  -   nX:  a  Un
[        X-   X(-
  5      nXU-
  :  a  X(-
  n[        XU	-
  5      n
X-
  S:  a  U
S	-   nX-
  S:  a  Sn
[        US-
  S	5      nSU
 SU S3n[        SS[        U5      SUSSSSSSU/n[        R                  " USSS9nUR                  S:H  =(       aF    [        U5      R	                  5       =(       a&    [        U5      R                  5       R                  S:  nU(       a  U R                  U5      nSUs=::  a  S::  a  O  O[        SUS SUS S35        U$ US:  a  [        SUS S 35        US-  n[        SSUS![        [        SUS"-
  5      5      S#S$SSSSSSUS%-   /n[        R                  " USSS9nUR                  S:X  a  SSKnUR#                  US%-   U5        U$ [        S&UR$                  (       a  UR$                  S'S OS( 35         [        U5      R                  5         [        US)-  5      n[        SS[        U5      S![        U
5      S#S*SSSSSSU/n[        R                  " USSS9nUR                  S:X  ax  [        U5      R	                  5       (       aZ  [        U5      R                  5       R                  S:  a3  U R                  U5      nSUs=::  a  S::  a  O  O[        S+US S,35        U$ [        S-5        U$ ! [         a0     [        U5      R                  5          GNN! [         a      GN\f = ff = f! [         a     GN*f = f).u  
v5.0 预处理参考音频以最大化音色保留:

v5.0 核心变更:
- GPT-SoVITS API 严格要求 3-10 秒，上限从 12s 收紧至 10s
- 从音频中间段截取（而非从头截取），中间段通常语音内容最丰富
- 始终使用 quality 模式：仅格式转换+防爆音，保留原始声纹特征

处理策略:
1. 找到音频的"黄金中间段"（避开开头结尾的杂音/呼气和收尾衰减）
2. 仅做格式转换 (16kHz/mono/16bit) + 极轻度防爆音 (-3%)
3. 输出严格保证在 3-10 秒范围内
Nr   zref_processed.wavr   r   rF  r   r>  g      #@g      $@g      @r  zatrim=r   z!,asetpts=PTS-STARTPTS,volume=0.97r   r   z-afr   r   r   r   r   r   Tr   u"   [GPT-SoVITS] 音频预处理(v5): rC  u   s → u   s (中间段)u   [GPT-SoVITS] 预处理后 u   s > 10s，二次截取至 9sr   r?  rB  9z.tmp.wavu6   [GPT-SoVITS] 预处理失败，回退到简单截取: i8unknownzref_trimmed.wavr6  u!   [GPT-SoVITS] 简单截取成功: su@   [GPT-SoVITS] 音频预处理全部失败，回退到原始音频)rN  r   r  r   rh   rB   rC   r   r,   r   r!  r   r&   r'   r(   r-   rX  mover   )rp  r  rU   r  r  processed_pathdurrG   rU  
target_dur
trim_starttrim_endrV  filter_chainr/   r0   successprocessed_dur
retrim_cmdsub2_shutilfallback_pathfallback_cmd	fb_resultfb_durs                            r   r  "GPTSoVITSEngine._prepare_ref_audio  s    x1}//?Hn%,,V&99: &&((
..~>N+002::>r>dTk))^$++- a<!! Dh!34
>J8c>40H Q,CA~-J!^+H"#
z68IJV++#, J)>?
  2%!C'H 3&J8d?C0H !AhZ 8% & tS%8<{E3w	
 DtD$$) >d>.B.I.I.K >',,.66= 	  44^DMM'R':8C.}]`Naanop%%#2=2EEabc#a't^3s1cCi014{E3w"Z/	
 "~~jDQ??a',LL*!<nM)) 	F_e_l_lv}}UYUZG[r{F|}~	 '') F%667tS%83z?D${E3w	
 NN<4P	1$m)<)C)C)E)E$}J]JbJbJdJlJlosJs--m<FF b 9&QGH$$PQC  (//1  ^  		sC   AO) )O) 0P& )
P#4P
PP#PP#&
P43P4c                      U R                  U5      n[        U5      R                  5       R                  nX$s=:*  =(       a    U:*  Os  =(       a    US:  $ ! [         a     gf = f)u0   验证音频是否有效且时长在规定范围rF  F)rN  r   rB   rC   r,   )rp  r.   min_durationmax_durationr  rG   s         r   _validate_audioGPTSoVITSEngine._validate_audio|  s\    	**:6C
#((*22D66,6F4$;F 		s   AA 
A#"A#c                 $    [         SSSSSU/n[        R                  " USSS9nUR                  S:X  aK  [        R
                  " UR                  5      n[        UR                  S	0 5      R                  S
S5      5      $  g! [         a     gf = f)u   获取音频时长r   r   r   ri   r   Tr   r   r   rU   )
r%   r&   r'   r(   ri   r   r)   r+   rp   r,   )rp  r.   r/   r0   r   s        r   rN  #GPTSoVITSEngine._get_audio_duration  s    	wYcdC^^C4HF  A%zz&--0TXXh377
AFGG &
   		s   A=B 
BBc                 f   U(       a  [        U5      R                  5       (       d  [        SU S35        g [        R                  R                  S[        U5      05      nU R                   SU 3n[        R                  R                  U5      n[        R                  R                  USS9 n[        R                  " UR                  5       5      nUR                  S5      S	:X  a
   S
S
S
5        g S
S
S
5        g! , (       d  f       g= f! [         a  n[        SU 35         S
nAgS
nAff = f)uN   在 GPT-SoVITS 中预加载参考音频（提升后续合成速度和质量）u6   [GPT-SoVITS] 跳过预加载：参考音频不存在 (r  Frefer_audio_pathz/set_refer_audio?r   rt  r  r  NTu*   [GPT-SoVITS] 预加载参考音频失败: )r   rh   r-   rv  r  	urlencoder   r  rw  rx  ry  ri   r   r@   rp   r,   )rp  r  r0  r  r{  r|  r0   r1   s           r   r  "GPTSoVITSEngine._preload_ref_audio  s
   T.%9%@%@%B%BJ>JZZ[\]		D\\++-?^AT,UVF]]O#4VH=C..((-C''R'8DDIIK0::i(I5 985 9  98   	D>qcBCC	Ds<   A8D /;C=*D 4D =
DD D 
D0D++D0)r  r  )rA  )	Nr   r  rA  NNNr  N)r  NN)Nr  )r   r   )r  r  r  r  r  	threadingLockr  rq  r}  staticmethodr  r  r8  r_  r  rb  rc  r  r  rN  r  r  r{   r   r   r  r  =  s    
 .."K% ) )V( KO?C@DL'\ <@-1]$~4BH@}~
r   r  c                   L    \ rS rSrSrS rS rSS jrSS jrSS jr	SS	 jr
S
rg)DuixAvatarEnginei  u   Duix.Avatar WSL2 服务封装c                 0    [         U l        [        U l        g ry   )DUIX_VIDEO_URL	video_urlDUIX_TTS_URLtts_urlro  s    r   rq  DuixAvatarEngine.__init__  s    '#r   c                 ^    [         R                  R                  U R                   S35      n[         R                  R	                  USS9 nUR
                  S:  sSSS5        $ ! , (       d  f       g= f! [         R                  R                   a  nUR                  S:  s SnA$ SnAf[         aw     SSK
nUR                  " UR                  UR                  5      nUR                  S5        UR                  S5      nUR                  5         US:H  s $ ! [         a      gf = ff = f)	u1   检查 Duix.Avatar 视频合成服务是否可用r
   r   rt  rb  Nr   )z	127.0.0.1i   F)rv  rw  rx  r  ry  r   r   	HTTPErrorcoder,   r#  AF_INETSOCK_STREAMru  
connect_exrw  )rp  r{  r|  her#  sockr0   s          r   r}   DuixAvatarEngine.check_available  s    	..((DNN+;1)=>C''Q'74{{S( 877||%% 	!77S=  
	}}V^^V5G5GH")<=

{" 
	sa   A
A6 A%	A6 %
A3/A6 3A6 6D,B("D,(D,6A!DD,
D($D,'D((D,c                    [         R                  R                  U5      n[        US5       nUR	                  5       nSSS5        [
        R                  " SS[        S5      R                  W5      R                  5        3US.5      R                  5       n[        R                  R                  U R                   S3USS	S
0S9n[        R                  R                  USS9 n[
        R                   " UR	                  5       5      n	SSS5        W	R#                  S5      U	R#                  SS5      4$ ! , (       d  f       N= f! , (       d  f       NB= f)ua   
Duix TTS 预处理（声音特征提取）
返回: (asr_format_audio_url, reference_audio_text)
r9   N.wavzdata:audio/wav;base64,r  )r   reference_audiorm  z/v1/preprocess_and_tranr  r  r  r  r  rt  asr_format_audio_urlreference_audio_textr   )r  rk   r  r?   r@   ri   r  
__import__	b64encoder  r  rv  rw  rx  r  ry  r   rp   )
rp  r.   r&  r  rD   r6  request_bodyr{  r|  r0   s
             r   preprocess_voice!DuixAvatarEngine.preprocess_voice  s%   
 77##J/ *d#qJ $ zz!7
88L8V8VWa8b8i8i8k7lm#
  68	 	 nn$$||n34#%78	 % 
 ^^##C#5ZZ		,F 6 zz016::>TVX3YYY' $#  65s   D3#%E3
E
ENc                    Uc  [        [        R                  " 5       5      n[        U5      n[        U5      n[        R
                  " UUUSSSS.5      R                  5       nSn	[        US-   5       GH3  n
 [        R                  R                  U R                   S3USSS0S	9n[        R                  R                  US
S9 n[        R                  " UR                  5       5      nSSS5        WR                  S5      nUR                  SS5      nUS:X  a-  U
S:  a  [!        SU
 SU 35        O[!        SU SU 35        Us  $ SU;   d  SU;   d  US:X  a8  X:  a3  [!        SU SU
S-    SU S35        ["        R$                  " U5        Un	GM  ['        SU=(       d    S 35      e   ['        S U S!U	 35      e! , (       d  f       N= f! [        R(                  R*                   aZ  nX:  aB  [!        SU SU
S-    SU SU 35        ["        R$                  " U5        [        U5      n	 SnAGM  ['        SU 35      eSnAff = f)"uR  
提交视频合成任务（带自动重试，Duix 单任务限制）
audio_path: 口播音频 (GPT-SoVITS 合成的克隆语音)
video_path: 数字人底视频 (抠图后的透明背景视频)
max_retries: 最大重试次数（默认30次，每次间隔10秒，最长等待5分钟）
retry_delay: 重试间隔（秒）
返回: task_code
Nr   r   )	audio_urlr  r  chaofenwatermark_switchpnz/easy/submitr  r  r  r  r  rt  r  r{  r   '  u   [Duix] 提交成功 (重试u   次后): audio=u   [Duix] 提交任务: audio=z, video=   忙碌   繁忙i'  u   [Duix] 服务忙碌中，u   秒后重试 (r
   )...u   Duix 任务提交失败: u   未知错误u   [Duix] 网络错误，r  u   Duix 连接失败: u   Duix 任务提交失败（u   次重试后仍忙碌）: )r   r  r  r   ri   r  r  r	  rv  rw  rx  r  ry  r   r@   rp   r-   r   r   r,   r   r"  )rp  r.   r6   	task_coder3  retry_delay	wsl_audio	wsl_videor  r4  r5  r{  r|  r0   r  r{  r1   s                    r   submit_video_task"DuixAvatarEngine.submit_video_task  sc    DJJL)I  
+	
+	zz"" !#
  68 	 
[1_-G&;nn,,~~&l3%!+-?@	 -  ^^++C+<!ZZ		4F = zz&)jj+5={ ;G9OT]S^_` ;I;hykZ[$$ s?h#o, 9+nU\]^U^T__`al`mmqrs

;/%(
   ";C<Q>;R STT? .R 4[MA[\f[ghiiA =<0 <<(( ;(2;-~gVWi[XYZeYffijkilmnJJ{+!$QJ"5aS 9::;sF   8AG%F<*AGAGG<
G
	GI+A I 2I  Ic                    [         R                   " 5       n[         R                   " 5       U-
  U:  Ga   [        R                  R                  U R                   SU 35      n[        R                  R                  USS9 n[        R                  " UR                  5       5      nSSS5        WR                  S5      nUS:X  a  UR                  S0 5      nUR                  S5      n	U	S	:X  ad  UR                  S
S5      n
U
R                  S5      (       a  U
R                  S5      nSU 3nSU 3nUnOU
nU
nU
nSUUUUR                  SS5      S.$ U	S:X  a  SUR                  SS5      S.$ UR                  SS5      nSUS.$  [         R                  " S5        [         R                   " 5       U-
  U:  a  GM  SSS.$ ! , (       d  f       GN = f! [         a  n[        SU 35         SnANiSnAff = f)uh   
查询视频合成任务状态
返回: {'status': 'completed'/'processing'/'failed', 'video_url': ...}
z/easy/query?code=r   rt  Nr  r  rt   r   r   r0   r   r
   z/code/data/temp/z"\\wsl$\duix-avatar\code\data\temp\r   video_durationr   )r   r  wsl_pathr   rU   r   r   r{  u   生成失败)r   r   r  
processing)r   r  u   [Duix] 查询任务失败: r#   u   任务超时)r   rv  rw  rx  r  ry  ri   r   r@   rp   
startswithrZ  r,   r-   r   )rp  r  r#   r  r{  r|  r0   status_codert   task_statusresult_pathr  wsl_full_pathwin_full_pathr  r  r1   s                    r   query_video_task!DuixAvatarEngine.query_video_task#  s   
 YY[
iikJ&0)9nn,,~~&&7	{C ^^++C+<!ZZ		4F = %jj0%'!::fb1D"&((8"4K"a'&*hhx&< '11#66'2'9'9#'>H.>xj,IM.WX`Wa,bM(5I,7M,7M(3I&1)2(5(5(,1A1(E   %)*2TXXe^=\]]#'88J#:*6HMM; (D JJqMY iikJ&0\ $n==S =<H  93A37889s=   AG  %F>%B+G G ,G >
GG 
G2G--G2c                    [        [        R                  " 5       5      nU(       a  [        USSS9   U R	                  [        U5      [        U5      U5        U(       a  [        USS	S9  [        R                  " 5       nS
n[        R                  " 5       U-
  U:  a  U R                  USS9n	U	S   S:X  a$  U(       a  [        USSS9  U	R                  SS5      $ U	S   S:X  a  [        SU	R                  S5       35      eU	S   S:X  aH  U(       a*  S	[        U	R                  SS5      S-  5      -   n
[        X:S9  [        R                  " S5        O[        R                  " S5        [        R                  " 5       U-
  U:  a  M  [        SUS-   S35      e! [
         a(  n[        U5      nSU;   d  SU;   a  [        S5      ee SnAff = f)uq   
生成视频（阻塞直到完成，自动重试忙碌状态）
返回 Duix 生成的视频 URL 或本地路径
uF   提交 Duix.Avatar 视频生成任务（如忙碌将自动重试）...r   )r  r  r  r  uM   Duix 服务临时不可用，请稍后重试。队列会自动重新调度。Nu$   Duix.Avatar 唇形同步生成中...r  i  rt  r   r   u   Duix.Avatar 视频生成完成r   r  r   r   u   Duix 生成失败: r   r  r  r   r   )r  r  u   Duix.Avatar 生成超时（rD  u	   分钟）)r   r  r  r   r  r,   r   r  rp   r  r   )rp  r.   r6   r   r  r1   	error_strrl  r#   r   r  s              r   generate_video_blocking(DuixAvatarEngine.generate_video_blockingZ  s   
 

%	*r}  A	""3z?C
OYO *P[]^ 		iikE!G+**9b*AFh;. 2R]_`zz+r22!X-"5fjj6I5J KLL!\1!C

:q(AC(G$HHH <

1

1 iikE!G+" 5grk])LMM=  	AI9$I(="oqq	s   %F 
G #GG)r  r  )r  )NrD  r   )r  ry   )r  r  r  r  r  rq  r}  r  r  r  r  r  r{   r   r   r  r    s)    '$*Z:CjJ5>n*Nr   r  c                       \ rS rSrSr\R                  " 5       r\" 5       r	S r
S r\S 5       r\S 5       r\S 5       rS r0 rS	r\R                  " 5       rS
rS rSS jr    SS jr    SS jrS rS rS rSrg)DigitalHumanEngineV2i  u-  数字人口播引擎 v2.0（内部串行队列架构）

并发安全设计:
- GPT-SoVITS 模型锁: 确保同一时间只有一个语音合成/训练在进行
- 生成队列: 串行化 Duix.Avatar 视频合成请求
- 任务取消: 支持前端取消正在执行的任务，自动清理临时文件
c                    [        5       U l        [        5       U l        [	        5       U l        [        R                  " 5       U l        SU l	        [        R                  " U R                  SS9U l        U R                  R                  5         g )NTtargetdaemon)rk  rmbgr  
gpt_sovitsr  duixqueueQueue_generate_queue_worker_runningr  Thread_process_queue_worker_threadrl  ro  s    r   rq  DigitalHumanEngineV2.__init__  sf    %'	)+$&	  %{{}#'..d6I6IRVW!!#r   c                     SU l         g)u   优雅关闭工作线程FN)r  ro  s    r   shutdownDigitalHumanEngineV2.shutdown  s
    $r   c                 V    U R                   R                  U5        [        SU 35        g)u   取消指定任务u   [任务取消] N)_cancelled_tasksaddr-   clsr   s     r   cancel_task DigitalHumanEngineV2.cancel_task  s'     	  )y)*r   c                     XR                   ;   $ )u   检查任务是否已被取消)r  r	  s     r   is_cancelled!DigitalHumanEngineV2.is_cancelled  s     ....r   c                 :    U R                   R                  U5        g)u   清理取消标记N)r  discardr	  s     r   clear_cancelled$DigitalHumanEngineV2.clear_cancelled  s     	$$W-r   c           	      Z   U R                   (       a   U R                  R                  SS9u  pU R                  U5      (       a8  [        USSS9  U R                  U5        U R                  R                  5         M{   U R                  R                  5       nUS:  a  [        USSSU S	3S
9  O[        USSSS
9  U R                  " U40 UD6  U R                  R                  5         U R                  U5        U R                   (       a  M  gg! [        R                   a     GM$  f = f! [         aG  n[        R                  " 5       n[        SU SU 35        [        USS[        U5       3S9   SnANSnAff = f! U R                  R                  5         U R                  U5        f = f)uH   后台工作线程：依次处理生成队列中的任务，支持取消r   rt  	cancelled   任务已取消r   r  r   r  u.   排队完成，开始生成（队列中还有 u    个任务等待）...r   r  r     开始生成口播视频...u   [队列生成失败] task=r   r      生成失败: N)r  r  rp   r  Emptyr  r   r  	task_doneqsize_do_generater,   	traceback
format_excr-   r   )rp  r   r0  	remainingr1   	error_msgs         r   r   #DigitalHumanEngineV2._process_queue  s   """&"6"6":":1":"E
   ))W[BST$$W-$$..0. 00668	q= *XYbXccy(z| !(EG !!'4V4 $$..0$$W-? """ ;; *  ?%002	27)2i[IJWX&4SVH$=?? $$..0$$W-s<   D =AD) D&%D&)
E:3=E50E= 5E::E= =-F*r   r   c                 X   [         R                   " 5       n[        R                     U[        R                  -
  [        R                  :  a"  [        [        R                  5      sSSS5        $  SSS5        U R                  R                  5       U R                  R                  5       U R                  R                  5       S.n[        R                     U[        l        [         R                   " 5       [        l        SSS5        U$ ! , (       d  f       N= f! , (       d  f       U$ = f)u_   获取所有服务状态（带 10 秒缓存，防止状态检查阻塞 Guardian 健康检测）N)rmbg_availablegpt_sovits_availableduix_available)r   r  _status_cache_lock_status_cache_timeSTATUS_CACHE_TTLdict_status_cacher  r}  r  r  )rp  rO  
new_statuss      r   
get_statusDigitalHumanEngineV2.get_status  s    iik!44)<<<?S?d?dd0>>? 54d 5
 #ii779$(OO$C$C$E"ii779


 "441; .6:iik 3 5  54 54 s   >D	*D	
D
D)Nc                     US:X  a  Sn[        USSSS9  [        R                  U5      (       a!  [        USSS	9  [        R                  U5        g
[	        U5      n[
        U-  nUR                  SSS9  [        [        U5      5      nUS   n	US   n
[        USSUS    SUS    SU	S SU
S S3	S9  [        USSS9  [        [        U5      5      u  pnUS:X  a  U(       a  [        USSUS S3S9  SnOEUS:X  a  U(       d  [        USS US S!3S9  O'US:X  a  [        USS"US S#3S9  O[        USS$US S%3S9  [        US&S'S9  [	        S(5      nUR                  SSS9  X S)3-  nUR                  R                  5       nUS):X  a*  [        R                  " [        U5      [        U5      5        Ob[        S*S+[        U5      S,S-S.S/[        U5      /	n[        R                   " USSS09nUR"                  S1:w  a  [%        S2UR&                  S3S
  35      e[        US4S5U S63S9  [        US7S8S9  S9nS9nU S:3n[(        R+                  US;9nUS<-  nS=nUS>-  n[        US?S@S9   [-        [        U5      [        U5      5        UR/                  5       (       aI  UR1                  5       R2                  SA:  a+  [5        SBU SCUR1                  5       R2                   SD35        OS
n[5        SE5         S9nU(       a@   U R6                  R9                  [        U5      5      n[5        SHU(       a  US
S4 OSI SJ35        U R6                  R;                  5       (       a   [        USLSMS9  U R6                  R=                  UU(       a  [        U5      OS
USNSO[        U5      SP9  UR/                  5       (       a+  UR1                  5       R2                  SA:  a  Sn[        USQSRS9  U(       d{  U R>                  R;                  5       (       a\   [        USUSVS9  U RA                  U[        U5      5        UR/                  5       (       a   UR1                  5       R2                  SA:  a  SnU(       d&  [        USQSXS9  U RC                  U[        U5      5        UR/                  5       (       a  UR1                  5       R2                  SA:  a  [        U5      n SYU 3n[D        U-  nUR                  SSS9  USZ-  n[        R                  " U[        U5      5        [        U5      n [G        5       nUUSNUUS
S
S\U=(       d    S9[H        RJ                  " 5       RM                  5       U R6                  RO                  U5      S]US^.n U UU'   [Q        U5        [        US_S`U 3S9  O[        US_ScS9  [        USdSeS9  [S        5       n"0 SfU_SgU_ShU_Si[        U5      _Sj[        U5      _SkS9_SlU_SmU_SnU_So[        U5      _SpU=(       d    S9_Sq[H        RJ                  " 5       RM                  5       _SUS   _SUS   _SU	_SU
_Sr[U        U5      _SsSt0EU"U'   [W        U"5        US:X  a  SuOSvn#U(       a  SwOScn$[        USxSySzU# S{U$ 3US|9  [5        S}U SCU S~U S[U        U5       35        g
! [$         a  nS
n[5        SFU SG35         S
nAGNS
nAff = f! [$         a  n[5        SKU 35         S
nAGNS
nAff = f! [$         a  n[5        SSU ST35         S
nAGN+S
nAff = f! [$         a  n[5        SWU 35         S
nAGNS
nAff = f! [$         a  n[5        S[U 35         S
nAGN;S
nAff = f! [$         a$  n![5        SaU! 35        [        US_SbS9   S
n!A!GNS
n!A!ff = f! [$         aD  n[X        RZ                  " 5       n%[5        SU% 35        [        USS[        U5       3S	9   S
nAg
S
nAff = f)u\  
训练数字人 - 快速模式

bg_mode:
  'auto'        - 自动检测绿幕（默认）
  'transparent' - 强制透明模式（绿幕抠图）
  'natural'     - 强制自然背景（不抠图）

声音处理：
  有声音 - 提取音频并自动注册到声音库（Zero-shot 参考音频）
  无声音 - 跳过音频步骤，仅保存视频
r  rW  r  r  u   开始训练数字人...r  r  r  r  NTr^   r   rU   r   u   视频信息: r   xr   r  rC  zfps, r  r     u!   正在检测视频背景类型...   u,   ⚠️ 检测到绿幕背景（绿色占比 z.0%u*   ），已自动切换为透明背景模式rO  u/   ℹ️ 未检测到明显绿幕（绿色占比 u   ），将尝试 AI 抠图u)   ✅ 检测为自然背景（绿色占比 u   ），跳过抠图u)   ✅ 检测到绿幕背景（绿色占比 u   ），将生成透明通道r  u"   准备 Duix.Avatar 训练数据...D:\heygem_data\face2face\videos.mp4r   r   r   r@  rD  aacr   r   u   视频格式转换失败: rh  r  u   视频已准备 (u   背景模式)7   u   生成默认训练语音...r   u   （默认音色）)r   zdefault_voice.wavFztrain_ref_audio.wav8   u-   提取训练视频原声作为参考音色...rF  u    [训练] 已提取视频原声:  ( bytes)u3   [训练] 原声提取失败，回退到默认音色u   [训练] 提取原声异常: u   ，回退到默认音色u   [训练] 原声转录: u   (空)r  u   [训练] 转录异常: rD  u/   GPT-SoVITS 生成默认语音（高质量）...r  rA  )r"   r  r%  r&  r  r   A   u   GPT-SoVITS 语音合成完成u"   [默认语音] GPT-SoVITS 失败: u   ，尝试 Duix TTS...>   u   Duix TTS 生成默认语音...u#   [默认语音] Duix TTS 也失败: u.   TTS 均不可用，生成静音占位音频...r;  r=  u,   [默认语音] 复制到声音目录失败: default_ttsdigital_human_train)rG  r   r&  rH  r%  r'  r(  r  ownerrJ  rU   source
digital_idK   u,   默认语音已生成并注册到声音库: u   [声音库注册失败] u6   默认语音生成完成（注册失败，可忽略）u   默认语音生成失败Z   u   保存数字人配置...rG  r   r\  r6   original_videotransparent_videor.   rS  r1  	train_dirr?  rJ  	has_audior   readyu!   透明背景（生成时抠图）r  u   默认语音已注册r   r   u    数字人训练完成！背景: z | )r   r  r  rA  u   [训练完成] 数字人 z) bg=z audio=u   [训练失败] r   u   训练失败: ).r   r  r  r  r   	TRAIN_DIRr   r   r   r2  suffixr   rX  rY  r   r&   r'   r(   r,   r   DEFAULT_TRAINING_TEXTr   r   rh   rB   rC   r-   r  r  r}  r8  r  _generate_default_tts_generate_silence_ttsrM  r   r   rO  rP  rN  r   r|   boolr   r  r   )&rp  r   r6   rA  r   r\  r?  rF  
video_infor   rU   r/  r+  r1  heygem_video_dirduix_video_pathextr/   r0   r.   voice_id_from_trainvoice_name_displaytraining_texttrain_audio_pathtts_oktrain_ref_audior1   train_ref_text	voice_dirvoice_ref_pathcopy_errr[   r\  vera   bg_labelaudio_labelr"  s&                                         r   trainDigitalHumanEngineV2.train  s   X	V& #Kef $0099W[BST$44W=j)J!J.IOOD4O8 (J8JU#C!*-H2"0G1D0EQzRZG[F\\^_bcf^gglmuvylzz{ |~ 27Z[/B3z?/S,H9)#Wr&RS^_bRc  dN  %OP'M)(Wr&UVabeUff  %ABI%Wr&OP[\_O``r$su Wr&OP[\_O``{$|~ 27[\#$FG""4$"? /<t1DDO##))+Cf}S_c/.BC #D$Jy&%_9MO#DtL$$)#&@tuAU@V$WXX29J7)S`7ab
 27TUJ"$$(6);!< 288d8CM  )+>>F
 (*??O27fg	Sc*os?/CD"))++0D0D0F0N0NQU0U<_<MRP_PdPdPfPnPnOoovwx&*OOQ  N9%)__%F%Fs?G[%\N3>N3B4G_f3ggjkl
 ..00Y 2?pqOO..*?Ns?';TX!/!%!$'(8$9 /  (..005E5J5J5L5T5TW[5[!%$WrCbc
 dii7799E 2?_`..}cBR>ST'..005E5J5J5L5T5TW[5[!%
 Wr;kl**=#>N:OP&&((-=-B-B-D-L-Lt-S !12
U,/
|*<' *-@ @IOOD4O@%.%@NLLS-@A!$^!4Jy)^F1 2$(%/$1'+*. -!&"&.lln&>&>&@$(OO$G$G
$S"7&0"J 3=F./ ( 2*VWiVj(km Wr;UV 27QR13N*j** 7* c/2	*
 !#j/* $R* j* /* Y* S^* "* hlln668* G,* *X.* s*  H!*" T*-#*$ '%*N:&( !0 ?F>V:\jH6I1OiKs"B8*CP[} ]#-/ -j\D6wiwW[\fWgVhijo  S"&5aS8PQRRS ! 93A37889& ! Y>qcAVWXXY ! E?sCDDE$ ! UH
STTU0 ! y4RD9: 2?wxxyP  	V!,,.IOI;/0^CPQF8;TU	Vs(  A^ H^ !A;Z ^ Z *
^ 5?Z> 4^ B[$ &^ ?A\ A+^ A\1 A;] D^ 
Z;Z60^ 6Z;;^ >
[![^ [!!^ $
\.\=^ \^ 
\.\)#^ )\..^ 1
];]	^ ]^ 
^!^ :^  ^^ 
_:__c                     U R                   R                  5       S-   nUS:  a  [        USSSU SUS-
   S3S9  O[        USSSS9  U R                   R                  UUUUUUUUU	U
US	.
45        g
)u\  
加入生成队列（所有请求串行处理，根治 Duix "忙碌中"）
前端通过 task_id 轮询进度即可

output_size: 'horizontal' (横版 1280×720) / 'vertical' (竖版 720×1280)
lip_sync_mode: 'quality' (GPT-SoVITS高品质) / 'sync' (Duix完美唇形同步)
quality: 'balanced' / 'expressive' / 'precise' (v3.0 语音质量模式)
r   queuedr   u   排队中（第 u    位），前方 u    个任务等待...r  u'   已加入队列，即将开始生成...)
rA  r"   rS  r\  r]  rd  r  output_sizelip_sync_moder  N)r  r  r   put)rp  r   rA  r"   rS  r\  r]  rd  r  rd  re  r  	queue_poss                r   generateDigitalHumanEngineV2.generate  s     ((..014	q=A"29+=NyYZ{m[n oq A IK 	  '$  *&*,
 " 	r   c                 $   U	S:X  a  Su  pOSu  p [        USSSS9  [        R                  U5      (       a!  [        USS	S
9  [        R                  U5        g[	        5       nX.;  a  [        SU S35      eX   n[        U-  nUR                  SSS9  [        USSS9  US-  nU(       Ga  U R                  R                  5       (       Ga  [        5       nUU;   a  [        USSS9  UU   nU R                  R                  US   UR                  SS5      US9nU R                  R                  UUUR                  SS5      UR                  SS5      UUR                  S5      UR                  S5      [        U5      UUR                  S5      S 9
  GO6UR                  S!5      (       a  [        US!   5      R!                  5       (       a{  [        USS"S9  US!   nU R                  R                  UUS9nSn U R                  R#                  U5      nU(       a  UnU R                  R                  UUUSU[        U5      US#9  GO[        S$U S%35      eU R                  R                  5       (       a  UR                  S!5      (       a  [        US!   5      R!                  5       (       a  [        US&S'S9  U R                  R                  US!   US9nSn [%        S(5        U R                  R#                  U5      nU(       a  Un[%        S)['        U5       S*35        O[%        S+5         U R                  R                  UUUSU[        U5      US#9  Ow[        S.5      eUR                  S!5      (       aK  [        US!   5      R!                  5       (       a*  [        US&S/S9  U R)                  X?S!   [        U5      5        O[        S05      eUR!                  5       (       d  [        S15      e[+        [        U5      5      u  nnU(       d  [%        S2U 35        [-        [        U5      5      nU(       Ga  [%        S3US4 S5U 35        [/        S6['        U5      S7-  5      nUUS8-  :  Ga  [%        S9US4 S:US4 S;['        U5       S<35        [        US=S>S9   [1        U[        U5      S?US@9nU(       Ga
  U[        5       ;   a  [        5       U   nU R                  R                  US   UR                  SS5      S?S9nU R                  R2                  R5                  SASB9   U R                  R                  UUUR                  SS5      UR                  SS5      UUR                  S5      UR                  S5      [        U5      S?UR                  S5      S 9
  U R                  R2                  R7                  5         O6U R                  R                  US[9        5       ;   a  WOS[        U5      S?USC9  [-        [        U5      5      n[%        SDUS4 SE35        UUS8-  :  a  [%        SFUS4 SGUS8-  S4 SH35        O O[%        SK5        [-        [        U5      5      n[        USLSMS9  U R:                  R                  5       (       d  [        SN5      eUR                  SOS5      nU(       a  [        U5      R!                  5       (       d  [        SPU 35      e[        USQSRS9  U R:                  R=                  [        U5      UUSS9n [        USTSUS9  USV-  n!U R?                  SW5      (       a  [@        RB                  RE                  U 5      n"[@        RB                  RG                  U"SXSB9 n#[I        [        U!5      SY5       n$U$RK                  U#RM                  5       5        SSS5        SSS5        OU R?                  SZ5      (       a  [        U!5      RO                  S[SZ5      n%S\U%S]S  3n&S^U  S_U& S`3n'[P        RR                  " U'SSSSa9n(U(RT                  S:w  a0  [%        SbU(RV                   35        [        ScU(RV                   35      eOM[        U 5      R!                  5       (       a!  [X        RZ                  " U [        U!5      5        O[        SdU  35      e[        USeSfS9  [        USgShS9  [\        U SiU Sj3-  n)USk:X  Ga  UR                  SlSm5      n*U(       Ga8  [        U5      R!                  5       (       Ga  [        USnSoU* SpU SqU Sr3S9  [_        [        U!5      U[        U)5      U*SsStXSu9u  n+n,U+(       d  [%        SvU, Sw35        USx-  n-[a        [        U!5      [        U-5      U*SsStSy9u  n.n/U.(       ab  [c        [        U-5      U[        U)5      XSz9u  n0n1U0(       d8  [%        S{U1 S|35        [X        RZ                  " [        U!5      [        U)5      5        GO[%        S}U/ S|35        [X        RZ                  " [        U!5      [        U)5      5        GO{[        USnS~U* S3S9  [\        U SiU S3-  n2[a        [        U!5      [        U25      U*SsStSy9u  n+n,U+(       a  U2n)GO0[%        SU, S35        [X        RZ                  " [        U!5      [        U)5      5        O[        USnSU SqU Sr3S9  US-  n3[d        SS[        U!5      SSU SU SU SU S3	SSSSSSSSS[        U35      /n4[P        RR                  " U4SSSS9n5U5RT                  S:X  a?  U3R!                  5       (       a*  [X        RZ                  " [        U35      [        U)5      5        OE[%        SU5RV                  SS  S35        [X        RZ                  " [        U!5      [        U)5      5        U)R!                  5       (       d  [        SU) 35      e[        USSS9  U)Rf                  Ri                  5       S:H  n6[k        [        U)5      5      n7U7(       aF  U(       a?  [m        U7U-
  5      n8U8S:  a+  [%        SU7S4 SUS4 SU8S4 SE35        U8S:  a  [%        S5        Sn
U
S:X  Gaj  [%        S5        US:X  a  US-  n9[d        SS[        U!5      SSSSS[        U95      /
n:[P        RR                  " U:SSS9  U9R!                  5       (       ai  [\        U SiU S3-  n;[-        [        U95      5      n<[d        SS[        U)5      S[        U95      SSSSSSSSSU<(       a  [        U<5      OS[        U;5      /n'GOU)n;GOUSk:X  a  U(       a  [        U5      R!                  5       (       d  US-  n9[d        SS[        U!5      SSSSS[        U95      /
n:[P        RR                  " U:SSS9  U9R!                  5       (       a  [\        U SiU SU)Rf                   3-  n;[-        [        U95      5      n<U6(       a  SOSn=[d        SS[        U)5      S[        U95      SSSU=SSSSSU<(       a  [        U<5      OS[        U;5      /n'OU)n;OU)n;S[9        5       ;   a~  [P        RR                  " W'SSS9n(U(RT                  S:X  a(  U;R!                  5       (       a  U)Ro                  SS9  U;n)GOd[%        S[q        U(S5      (       a  U(RV                  SS OS 35        GO4[%        S5        GO'U6(       a6  [\        U SiU S3-  n;[d        SS[        U)5      S[        U5      SSSSSSSS/n'O5[\        U SiU S3-  n;[d        SS[        U)5      S[        U5      SSSSSSSS/n'U(       a;  US:  a5  U'Rs                  S5        U'Rs                  US 5        [%        SUS S35        OU'Rs                  S5        [%        S5        U'Rs                  [        U;5      5        [P        RR                  " U'SSS9n(U(RT                  S:X  a'  U;R!                  5       (       a  U)Ro                  SS9  U;n)O[%        S5        U6(       a2  [d        SS[        U)5      S[        U5      SSSSSSSSS[        U;5      /n>O1[d        SS[        U)5      S[        U5      SSSSSSSSS[        U;5      /n>[P        RR                  " U>SSS9n?U?RT                  S:X  a'  U;R!                  5       (       a  U)Ro                  SS9  U;n)O.[%        S[q        U?S5      (       a  U?RV                  SS OS 35        U)R!                  5       (       d  [        SU) 35      eU)Ru                  5       Rv                  n@U@S:X  a  [        SU) 35      e[X        Rx                  " [        U5      SS9  U)Rz                  nASUA 3nB[        USSSUB[        U)5      UBUAW@S9	  [%        SU SU) SU@ S35        g! [
         a     GNlf = f! [
         a  n[%        S,U S-35         SnAGNSnAff = f! U R                  R2                  R7                  5         f = f! [
         a  n[%        SIU SJ35         SnAGNSnAff = f! , (       d  f       G
Nt= f! , (       d  f       G	N= f! [
         aD  n[|        R~                  " 5       nC[%        SUC 35        [        USS[        U5       3S
9   SnAgSnAff = f)u  
实际执行生成口播视频（由队列工作线程调用）
1. GPT-SoVITS 合成克隆语音
2. Duix.Avatar 驱动数字人
3. FFmpeg 合成背景

output_size: 'horizontal' (1280×720) / 'vertical' (720×1280)
lip_sync_mode: 'quality' (GPT-SoVITS 高品质音频, 默认) / 'sync' (Duix 内嵌音频, 完美唇形同步)
quality: 'balanced' / 'expressive' / 'precise' (v3.0 语音合成质量模式)
vertical)     )rm  rl  r  r  r  r  r  r  r  Nu
   数字人 u
    不存在Tr^   r   u    GPT-SoVITS 合成克隆语音...r  ztts_output.wavr;   rH  rU   r   r  r%  r   r&  r  r'  r(  r   
r"   r  r%  r&  r  r'  r(  r   r  r   r.   u<   声音库条目丢失，使用数字人音频回退合成...)r"   r  r%  r&  r  r   r     声音 u    不存在且无回退音频r   u6   使用 GPT-SoVITS 合成语音（数字人声音）...u6   [生成] 自动转录数字人音频获取 ref_text...u    [生成] ref_text 转录成功 (u    字)uH   [生成] 转录无结果，使用空 ref_text（音色质量会下降）u   [生成] 转录异常: u   ，使用空 ref_textu*   未选择声音且数字人无参考音频u9   GPT-SoVITS 不可用，回退到 Duix TTS 合成语音...u&   GPT-SoVITS 不可用且无参考音频u*   语音合成失败：音频文件未生成u>   [WAV验证] 警告：WAV 文件头可能损坏，尝试继续 u+   [音频] GPT-SoVITS 合成完成，时长: rC  z	s, speed=g       @r?  r   u   [音频警告] 时长异常: u   s, 预期≥u	   s, 文本u   字 - 启动重试r3  u0   音频时长异常，正在重试语音合成...r  )r"   r   r  r  r  rt  )r"   r  r   r  r  u   [重试] 音频时长: r  u#   [音频警告] 重试后仍偏短: zs < u   s - 继续处理u#   [重试] 语音合成重试失败: u   ，使用原始音频继续u?   [音频] 无法获取音频时长，使用 ffprobe 兜底探测r  u*   语音合成完成，开始口型驱动...uH   Duix.Avatar 视频合成服务不可用，请确认 WSL2 服务已启动r6   u   数字人底视频不存在:     u(   提交 Duix.Avatar 唇形同步任务...)r.   r6   r   R   u&   Duix 视频生成完成，下载中...zduix_output.mp4httpr  r  r
   r	   z/mnt/d/r   zwsl -d duix-avatar -- cp "z" "r  )shellr!   r"   u   [WSL复制失败] u#   无法从 WSL2 复制 Duix 输出: u    无法获取 Duix 输出视频: U   u,   Duix 视频已下载，开始背景合成...X   u   合成最终视频...r  r5  rO  r1  r  rC  u   绿幕合成 (u   ) + 背景图 →    ×r  Q?r  )r1  r;  r<  rJ  rK  u    [绿幕合成] 单阶段失败: u   ，尝试降级方案...ztransparent_fallback.webm)r1  r;  r<  )rJ  rK  u   [降级合成] 也失败: u   ，输出原始视频u   [降级 chromakey] 也失败: u   chromakey 去绿幕 (u   ) → 透明 WebM....webmu   [去绿幕] 失败: u   ，降级为直接输出u   自然背景模式，缩放至 zscaled_natural.mp4r   r   r4  zscale=r   r?  z:(ow-iw)/2:(oh-ih)/2r   r@  rA  fastr   rC  r   rG  z-anr   r    u   [自然缩放] 失败: ru  u   ，使用原始输出u    背景合成后文件不存在: ^   u   合并高品质音频...rA  u   [时长警告] 视频=u	   s 音频=u	   s 差异=g      @u]   [时长警告] 严重不匹配！可能导致唇形漂移，强制使用 lip_sync_mode='sync'syncu<   [唇形同步] 使用 Duix 内嵌音频，保证口型协调rW  zduix_audio.aacz-vnrD  r6  z-b:a128kr   z
_final.mp4rE  rS  z0:vz1:arB  rF  zduix_audio.opuslibopus96k_finalr/   r   u9   [音频合并] sync模式失败，使用无音频版本: r   uJ   [唇形同步] green_screen_composite 已内嵌音频，无需额外合并z_final.webmr@  z.2fu+   [音频合并] 品质模式，音频时长=u   s，以音频为准截断输出uC   [音频合并] 品质模式，音频时长未知，使用 -shortestuD   [音频合并] 品质模式失败，尝试使用 -shortest 兜底...u>   [音频合并] 两次尝试均失败，使用无音频版本: u    最终视频文件生成失败: u   最终视频文件大小为0: r  /api/digital-human/v2/video/r   r   u   视频生成完成！)r   r  r  r  r6   
output_urlr  rS   u   [生成完成] 任务 u   ，视频: r9  r:  u   [生成失败] r   r  )@r   r  r  r  r|   r,   TEMP_DIRr   r  r}  r   r  rp   r8  r   r   rh   r  r-   r   _synthesize_with_duixrV   r2   r!  r+  r  rq   rs   dirr  r  r  rv  rw  rx  ry  r?   r  r@   r   r&   r'   r(   r   rX  rY  
OUTPUT_DIRrf  ri  rM  r   rJ  r   r7   absr   hasattrr  rB   rC   r  r   r  r   )Drp  r   rA  r"   rS  r\  r]  rd  r  rd  re  r  rJ  rK  ra   dhtask_diraudio_outputr[   r   rH  dh_audior%  auto_refr1   	wav_validwav_durationaudio_durationexpected_minretry_paramsretry_e
base_videoduix_video_url
raw_outputr{  r|  rD   win_destwsl_destr/   r0   final_outputr1  r   errrE  ok_fberr_fbok2err2transparent_outputtemp_scaled	cmd_scaleresult_scaleis_webmr  dur_diffaudio_from_duixcmd_extractfinal_with_audioduix_audio_durcodeccmd_fallbackresult2rS   video_filenamevideo_full_urlr"  sD                                                                       r   r  !DigitalHumanEngineV2._do_generate  s    *$!*Hh!*Hz	VKhi $0099W[BST$44W=13N/*ZL
 CDD+B')HNN4$N7 27YZ#&66LDOO;;==%v% 2?ab"8,E $ B Bk*EIIj!,D7 !C !TIOO..!'0!&:r!:!&:t!<#$)IIm$<',yy1A'B$'$5 '"YYv. /  VVL))d2l3C.D.K.K.M.M 2?}~!,/H $ B B8RY B ZI!H#'??#D#DY#O#'/H OO..!'0!)!%#$'$5 ' /  $ghZ7S$TUU002266,''DL1A,B,I,I,K,K 2?wx $ B B2lCSZa B bI!H	R VX#'??#D#DY#O#'/H!$DS]OSX"YZ!$ln OO..!'0!)!%#$'$5 ' /  $$PQQ 66,''DL1A,B,I,I,K,K 2?z{..t5Es<GXY#$LMM&&(( LMM ':#l:K&L#I|VWcVdef/L0ABNCNSVCWW`af`ghi"3D	C8!L3$669.9M\ZfgjYkktuxy}u~t  @R  S  T 2?qrj'+3|CT^got'u#LN(B$0N8$<E(,(J(J5Q\K]_d_h_hisuv_w  H(J  )II OO77???LF $ : :)-i-2YYz2-F-2YYz4-H*/UYY}=U3899=M3N03L0A9).6): !; !" !% ; ; C C E OO66%){VYV[G[)ae,/,=yX] 7  *<C<M)N 7s7K1MN)L3,>>!$GWZG[[_`lor`rsv_w  xH  #I  J= 7D WY!3C4E!F27cd 99,,.. jkk b1JT*%5%<%<%>%>"?
| LMM 27ab!YY>>|,% ? N 27_`!$55J((00nn,,^<^^++C+=c*ot4		, 5 >=  **3//z?224=$Xab\N32>2B#hZqQ#4SWX$$).v}}o>?#&I&--$YZZ * n%,,..^S_="B>BR STT27ef 27NO%7)1ZL(EEL-'FF;
;	 T-%8%?%?%A%A 2*8CUV^U__abjakkn(oq4JL8I"+D!)>GB
  @E]^_,47R,R)(C
OS1B-C&/D)Nv !(? #$5 6s<GX)1)FIC $' %(B4&H]&^ _ &S_c,>O P!$B6(J_"`a"LLZ#l:KL !2*?	{J^(_a)3	:,e6T)T&9J-?)@"+DJGB '9 4SE9QRSS_c,6GH Wr=\]e\ffhiqhrru;vw&)==#j/VH:Qxj8bckbllmnvmw  xL  MIy&&$	5$	  *~~iSWade**a/K4F4F4H4HLL[!13|3DE3L4G4G4M3NNcdeLLZ#l2CD  &&(("B<. QRR 27QR"))//1W<G 0L0ABN.~>?c>2>#2FiP^_bOcclmuvylzz{|}#~ }(.& TVi'&.1A&AO#Tc*ovuffO,	#K NN;t$O&--//+57)1ZLPZ8[+[();C<P)Q' #l"3 #o"6"FFE"E65 #n"5[ 01 ,8(-}mI\IcIcIeIe&.1B&BO#Tc*ovy&%O,	#K NN;t$O&--//+57)1ZLPVWcWjWjVk8l+l();C<P)Q-4	%' #l"3 #o"6"FdU"E65 #n"5[ 01 ,8( (4$CE>'^^C4PF((A-2B2I2I2K2K$++t+<'7 Yqxy  BJ  rK  rKZ`ZgZghlilZm  QS  ZT  U  Vfh '1wiqK4X'X$#Tc,/c,/	vuC (2wiqJ4W'W$#Tc,/c,/vuC "ns&:JJt$JJ.!57GWZG[[z{|JJ{+_a

3/01#DtL$$).>.E.E.G.G ''4'8#3L`b' #l"3T3|;L"FFI"E65+ 01( ( #l"3T3|;L"FFE"E65+ 01( )nn\$UYZG))Q.3C3J3J3L3L$++t+<'7 ^w~  @G  IQ  xR  xR_f_m_mnror_s  XZ  _[  \  ]  &&(("B<. QRR$))+33IA~"@ OPP MM#h-t<)..N;N;KLNs 7"0#&|#4#1!/"+- *7);|nBykY`abC % 8 % R 7s:OPQQRl !% ; ; C C E % j CG9LghiijB 54 >=N  	V!,,.IOI;/0^CPQF8;TU	Vs1  AAH& FAH& 7$AE: B9AH& AAF AH& AF (E(AH& B	AG A8AF2 BAG 2D-AH& AH5 AHAHgAH& E:
AFFAH& FAFFAH& F
AF/FAF*F$AH& F*AF/F/AH& F2&AGGAG G
AG?G%AG:G4AH& G:AG?G?AH& H
AH	HAHH
AH#HAH& H&
AI4H0:AI/I/AI4c                 ,   SSK n[        R                  " USSSS.5      R                  5       nUR                  R                  [         S3USSS	0S
9nUR                  R                  USS9 n[        R                  " UR                  5       5      nSSS5        WR                  S0 5      R                  SS5      nU(       a  UR                  S5      (       a  UO	[         U 3n	UR                  R                  U	5      n
UR                  R                  U
SS9 n[        US5       nUR                  UR                  5       5        SSS5        SSS5        g[        S5      e! , (       d  f       N= f! , (       d  f       N3= f! , (       d  f       g= f)uC   使用 Duix TTS 生成默认训练语音（不需要参考音频）r   Nr   r  r"   r  r  rm  
/v1/invoker  r  r  r  rD  rt  rt   r  rr  r  u   Duix TTS 未返回音频 URL)urllib.requestri   r  r  rw  rx  r  ry  r   r@   rp   r  r?   r  r,   )rp  r"   r   rv  r  r{  r|  rt   r  full_urlreq2resp2rD   s                r   rL  *DigitalHumanEngineV2._generate_default_tts	  s]   zz$&$&	
 
 68 	 nn$$nJ'#%78	 % 
 ^^##C#4::diik*D 5 HHVR(,,["=	$-$8$8$@$@yW`VaFbH>>))(3D''b'9U+t,GGEJJL) - :9 :;; 54 -, :9s0   -%E#F' E4F#
E14
F	>F
Fc                     [        [        U5      S-  S5      n[        SSSSSS[        U5      S	S
[        U5      /n[        R
                  " USS9  g)u<   用 FFmpeg 生成最长3秒的占位音频（回退方案）r   r   r   r   lavfir   zanullsrc=r=24000:cl=monorB  r   r   Tr<  N)r   r   r   r   r&   r'   )rp  r"   r   est_durationr/   s        r   rM  *DigitalHumanEngineV2._generate_silence_tts	  sU     3t9q=!,'4#;#l#{
 	s40r   c                     U R                   R                  U5      nU R                  R                  U5      u  pV[        R
                  " UUUSS.5      R                  5       n[        R                  R                  [         S3USSS0S9n[        R                  R                  USS	9 n	[        R                  " U	R                  5       5      n
S
S
S
5        W
R                  S0 5      R                  SS5      nU(       a  [        R                  R                  UR                  S5      (       a  UO	[         U 35      n[        R                  R                  USS	9 n	[!        US5       nUR#                  U	R                  5       5        S
S
S
5        S
S
S
5        g
g
! , (       d  f       N= f! , (       d  f       N)= f! , (       d  f       g
= f! [$         a  n['        SU 35        e S
nAff = f)u#   使用 Duix 内置 TTS 合成语音r  r  r  r  r  r  r  r  rt  Nrt   r  r   rr  rD  r  u   [Duix TTS] 合成失败: )r  r  r  r  ri   r  r  rv  rw  rx  r  ry  r   r@   rp   r  r?   r  r,   r-   )rp  r"   rH  r   r*  asr_urlr%  r  r{  r|  rt   r  rD   r1   s                 r   r  *DigitalHumanEngineV2._synthesize_with_duix	  s   	 OO>>yIM $		 : := IG::(/(0	' 
 vx  ..((.
+!');<	 ) C ''S'9Tzz$))+. : ,00bAInn,,):N:Nv:V:VY_k^lmvlw\xy^^++C+<k40A		, 1 =< 	 :9 10 =<  	-aS12	sg   B(G *%FBG F>* F-
F>G 
F*&G -
F;	7F>>
GG G 
G-G((G-)r  r  r  r  r  r  )rW  N)NrO  #00FF00NrA  
horizontalr  r  )r  r  r  r  r  r  r  _gptsovits_locksetr  rq  r  classmethodr  r  r  r   r,  r)  r(  r*  r.  r`  rh  r  rL  rM  r  r  r{   r   r   r  r    s      nn&Ou	$% + +
 / / . .!.F M")"eVN <@JNDM% D @DNRHQ)MV^<>1 r   r  c                       \ rS rSrSrSrg)ThreadingHTTPServeri
  Tr{   N)r  r  r  r  daemon_threadsr  r{   r   r   r  r  
  s    Nr   r  c                       \ rS rSrS rS rS rS rS rS r	S(S jr
S	 rS
 rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS r S r!S  r"S! r#S" r$S# r%S$ r&S% r'S&r(g'))DigitalHumanV2Handleri
  c                     g ry   r{   )rp  r   argss      r   log_message!DigitalHumanV2Handler.log_message 
  s    r   c                 B   [         R                  " USS9R                  S5      nU R                  U5        U R	                  SS5        U R	                  S[        U5      5        U R	                  SS5        U R                  5         U R                  R                  U5        g )	NF)r   re   r  zapplication/json; charset=utf-8Content-LengthAccess-Control-Allow-Origin*)	ri   r  r  send_responsesend_headerr   end_headerswfiler  )rp  r   rt   r  s       r   _json_response$DigitalHumanV2Handler._json_response#
  s    zz$U3::7C6")JK)3t956<

r   c                 p    U R                  SS5        U R                  SS5        U R                  SS5        g )Nr  r  zAccess-Control-Allow-MethodszGET, POST, DELETE, OPTIONSzAccess-Control-Allow-Headersz5Content-Type, Authorization, X-Auth-User, X-Auth-Role)r  ro  s    r   _send_cors_headers(DigitalHumanV2Handler._send_cors_headers,
  s7    6<79UV79pqr   c                 L    U R                   R                  SS5      =(       d    S$ )u:   从 X-Auth-User 头获取当前用户（由代理注入）zX-Auth-Userr   N)r  rp   ro  s    r   _get_auth_user$DigitalHumanV2Handler._get_auth_user1
  s    ||r2:d:r   c                 ^    U R                  5       nU(       d  U R                  SSS05        gU$ )u"   要求认证，未认证返回 401  r   u-   未登录或登录已过期，请重新登录N)r  r  )rp  usernames     r   _require_auth#DigitalHumanV2Handler._require_auth5
  s1    &&(g/^%_`r   c                 n   [        U R                  R                  SS5      5      nUS::  a  U R                  SSS05        gSnSnX1:  a>  U R                  R                  X-
  5      nU(       d  OX$-  nU[        U5      -  nX1:  a  M>  U(       d  U R                  SSS05        gS	 H-  n UR                  U5      n[        R                  " U5      nUs  $    g! [        [        R                  4 aB    US
:X  a8  USS R                  S5      n[        SU 3SS9  U R                  SSS05           g M  f = f)uD  安全读取 JSON 请求体（v4.0: 多编码兼容）

自动处理：
1. 部分读取问题（rfile.read 可能返回少于 Content-Length 的字节）
2. UTF-8/GBK/Latin-1 编码回退
3. 空 body 错误

Returns:
    dict: 解析后的 JSON 对象
Raises:
    Exception: 读取失败时已自动发送 JSON 错误响应
r  r     r   u'   请求体为空，请检查请求格式Nr   u   请求体为空)re   gbklatin-1r  r  r  u2   [Server] JSON body 解析失败 (前30字节hex): Tr  u.   请求体编码异常，请使用 UTF-8 编码)r  r  rp   r  rfiler@   r   r  ri   r   UnicodeDecodeErrorJSONDecodeErrorr  r-   )	rp  content_lengthraw_body
bytes_readry  rg   r"   r  hex_previews	            r   _read_json_body%DigitalHumanV2Handler._read_json_body=
  sJ    T\\--.>BCQg/X%YZ 
)JJOON$?@EH#e*$J ) g/@%AB 4Hx0zz$'	 4  '(<(<= y("*3B-"3"3C"8KN{m\dhi''g7g-his   *(CAD43D4c                     U R                  5       nU(       d  U R                  SSS05        gUR                  SS5      nU(       a  XC:w  a  U R                  SSSU 305        gU(       d  g	g	)
u7   检查数据所有权，不属于当前用户返回 403r  r   u	   未登录Fr?  r     u   无权访问此T)r  r  rp   )rp  rt   resource_typer  r?  s        r   _check_ownership&DigitalHumanV2Handler._check_ownershipn
  sk    &&(g{%;<"%U&g/P%QRr   c                 f    U R                  S5        U R                  5         U R                  5         g )Nru  )r  r  r  ro  s    r   
do_OPTIONS DigitalHumanV2Handler.do_OPTIONS}
  s(    3!r   c                 4   U R                   R                  S5      S   nUS:X  a  U R                  5         g US:X  a  U R                  5         g US:X  a  U R	                  5         g UR                  S5      (       a&  UR                  S5      S   nU R                  U5        g UR                  S	5      (       a&  UR                  S5      S   nU R                  U5        g US
:X  a  U R                  5         g UR                  S5      (       a  U R                  U5        g US:X  a  U R                  5         g UR                  S5      (       a  SU;  a  U R                  U5        g UR                  S5      (       a  U R                  U5        g U R                  S5        g )N?r   z/api/digital-human/v2/listz/api/digital-human/v2/statusz/api/digital-human/v2/tasksz/api/digital-human/v2/task/r
   r  /api/digital-human/v2/preview/z/api/digital-human/v2/voices"/api/digital-human/v2/voice/audio/z/api/digital-human/v2/videosr  delete/api/digital-human/v2/bg/  )rk   r   _list_digital_humans_get_status_list_tasksr  	_get_task_preview_digital_human_list_voices_serve_voice_audio_list_videos_serve_video_serve_bg_image
send_error)rp  rk   r   rA  s       r   do_GETDigitalHumanV2Handler.do_GET
  sN   yys#A&//%%'3322__:;;jjob)GNN7#__=>>C,J''
333__ABB##D)33__;<<XQUEUd#__899  &OOC r   c                    U R                   R                  S5      S   nUS:X  a  U R                  5         g US:X  a  U R                  5         g US:X  a  U R	                  5         g US:X  a  U R                  5         g US:X  a  U R                  5         g US:X  a  U R                  5         g US	:X  a  U R                  5         g US
:X  a  U R                  5         g US:X  a  U R                  5         g US:X  a  U R                  5         g UR                  S5      (       a&  UR                  S5      S   nU R                  U5        g US:X  a  U R                  5         g U R                  S5        g )Nr  r   z/api/digital-human/v2/uploadz/api/digital-human/v2/trainz/api/digital-human/v2/generatez$/api/digital-human/v2/generate-batchz/api/digital-human/v2/bg/uploadz"/api/digital-human/v2/voice/uploadz!/api/digital-human/v2/voice/trainz&/api/digital-human/v2/voice/synthesizez#/api/digital-human/v2/voice/previewz"/api/digital-human/v2/voice/reseedz/api/digital-human/v2/cancel/r
   r  r  )rk   r   _handle_upload_handle_train_handle_generate_handle_generate_batch_handle_bg_upload_handle_voice_upload_handle_voice_train_handle_voice_synthesize_handle_voice_preview_handle_voice_reseedr  _handle_cancel_taskr  )rp  rk   r   s      r   do_POSTDigitalHumanV2Handler.do_POST
  s<   yys#A&11!22 55!!#;;'')66""$99%%'88$$&==))+::&&(99%%'__<==jjob)G$$W-::&&(OOC r   c                 B   U R                   R                  S5      S   nUR                  S5      (       a&  UR                  S5      S   nU R                  U5        g UR                  S5      (       a&  UR                  S5      S   nU R	                  U5        g UR                  S5      (       a&  UR                  S5      S   nU R                  U5        g UR                  S5      (       a&  UR                  S5      S   nU R                  U5        g U R                  S	5        g )
Nr  r   z/api/digital-human/v2/delete/r
   r  z#/api/digital-human/v2/voice/delete/z#/api/digital-human/v2/video/delete/z"/api/digital-human/v2/task/delete/r  )rk   r   r  _delete_digital_human_delete_voice_delete_video_delete_taskr  )rp  rk   rA  rS  video_idr   s         r   	do_DELETEDigitalHumanV2Handler.do_DELETE
  s    yys#A&??:;;C,J&&z2__BCCzz#r*Hx(__BCCzz#r*Hx(__ABBjjob)Gg&OOC r   c                     [         R                  5       nUR                  SUS   (       a  SOSUS   (       a  SOSUS   (       a  SOSS	.S
.5        U R                  SU5        g )Nz2.0r'  u	   运行中u	   未运行r&  r%  u   可用u	   不可用)duix_avatarr  r  )versionservicesru  )enginer.  r   r  )rp  r   s     r   r  !DigitalHumanV2Handler._get_status
  sc    ""$.45E.F{K-34J-KkQ\$*+;$<+
 	 	C(r   c                    [        5       nX;  a  U R                  SSS05        gX!   nU R                  US5      (       d  gUR                  S5      =(       d    UR                  SS5      nU(       a  [	        U5      R                  5       (       dG  [	        S	5      U S
3-  nUR                  5       (       a  [        U5      nOU R                  SSS05        g[	        U5      nUR                  5       R                  nUR                  R                  5       nUS:X  a  SOSnU R                  S5        U R                  SU5        U R                  SU5        U R                  SS5        U R                  5         U R                  5         [        [        U5      S5       n	[         R"                  " XR$                  5        SSS5        g! , (       d  f       g= f)u   预览数字人原始视频r  r      数字人不存在N	   数字人rD  r6   r   r4  r5  u   视频文件不存在rx  
video/webm	video/mp4ru  r  r  Accept-Rangesbytesr9   )r|   r  r  rp   r   rh   r   rB   rC   rJ  r   r  r  r  r  r?   rX  copyfileobjr  )
rp  rA  dhsr  r6   heygem_pathrS   rR  content_typerD   s
             r   r   ,DigitalHumanV2Handler._preview_digital_human
  s   "$ g/C%DE_$$R55VV,-Ib1I
j!1!8!8!:!:AB
|SWEXXK!!## -
##C'3J)KL*%
OO%--	%%''*g~|;36)95'2!#j/4(Aq**- )((s   !G
Gc                 h   U R                  5       n[        5       n/ nUR                  5        H  u  pEUR                  SS5      nU(       a  U(       a  Xa:w  a  M,  UR	                  US   US   UR                  SS5      UR                  SS5      UR                  SS	5      UR                  S
S5      UR                  SS5      UR                  SS5      UR                  SS5      UR                  SS5      USU 3S.5        M     UR                  S SS9  U R                  SU[        U5      S.5        g )Nr?  r   rG  r   r\  rW  rJ  r   rH  rU   r   r   r   rG  TrS  r  )rG  r   r\  rJ  r   rU   r   r   rG  rS  r?  preview_urlc                     U S   $ NrJ  r{   r1  s    r   <lambda><DigitalHumanV2Handler._list_digital_humans.<locals>.<lambda>      !L/r   keyreverseru  )ra   count)r  r|   r   rp   r  sortr  r   )rp  r  r-  r0   dh_idr  r?  s          r   r  *DigitalHumanV2Handler._list_digital_humans  s   &&("$IEFF7B'EEe&7MMh6
66)Y7 ff\26&&73FF:q1+&&1-VVK6FF:r2!?wG  %& 	14@CFS[!QRr   c                    U R                  5       n[        5       n/ n0 nUR                  5        GH4  u  pVUR                  SS5      nU(       a  U(       a  Xq:w  a  M-  UR                  S5      nUR	                  UUUR                  SS5      UR                  SS5      UR                  SS5      UR                  S	S
5      UR                  SS5      UR                  SS5      UR                  SS5      UR                  SS5      UUR                  SS5      UR                  SS5      UR                  SS5      S.5        U(       d  GM  X;  a  / XH'   XH   R	                  U5        GM7     UR                  S SS9  U R                  SUS S [        U5      [        UR                  5       5      S S  V	V
s0 s H  u  pX_M	     sn
n	S.5        g s  sn
n	f )Nr?  r   batch_idbatch_indexr   batch_totalr  rh  r   r  r  r  r"   rA  rJ  r  r6   )r   rA  rB  rC  r  r   r  r  r"   rA  r?  rJ  r  r6   c                     U S   $ r4  r{   r5  s    r   r6  3DigitalHumanV2Handler._list_tasks.<locals>.<lambda>;  r8  r   Tr9  ru  r   r   )rb   r<  batches)	r  r   r   rp   r  r=  r  r   r   )rp  r  rb   r0   rF  tidtaskr?  rA  r  r  s              r   r  !DigitalHumanV2Handler._list_tasks  s   &&(ICHHWb)EEe&7xx
+HMM$#xxq9#xxq94((8Y7 HHZ388Ir2,"hh|R8"hh|R8!XXk26"hh|R8   x*(*G%!((-3 '4 	14@CDS\[)-gmmo)>s)CD)C)CD"
 	 Es   2Gc                     [        5       nX;  a  U R                  SSS05        g X!   nU R                  US5      (       d  g U R                  SU5        g )Nr  r      任务不存在   任务ru  )r   r  r  )rp  r   rb   rH  s       r   r  DigitalHumanV2Handler._get_taskB  sU    g/@%AB~$$T844C&r   c                 <   U R                  5       n[        5       n/ nUR                  5        H=  u  pEUR                  SS5      nU(       a  U(       a  Xa:w  a  M,  UR	                  U5        M?     UR                  S SS9  U R                  SU[        U5      S.5        g )Nr?  r   c                 &    U R                  SS5      $ )NrJ  r   )rp   r5  s    r   r6  4DigitalHumanV2Handler._list_voices.<locals>.<lambda>V  s    !%%b"9r   Tr9  ru  )r[   r<  )r  r   r   rp   r  r=  r  r   )rp  r  r[   r0   rS  r   r?  s          r   r  "DigitalHumanV2Handler._list_voicesL  s    &&(%||~OHIIgr*EEe&7MM%   . 	94HCFS[!IJr   c                    U R                  5       n/ n[        5       n[        5       nSnUR                  5        Hz  u  pgUR	                  SS5      nU(       a  U(       a  X:w  a  M,  SnUR	                  SS5      n	U	(       d  MI  UR                  [        [        U	5      R                  5       5      5        M|     [        [        R                  S5      S SS9 GHq  n
U
R                  R                  5       S	;  a  M$   U
R                  5       nUR                  S
-  n[        U
R                  5       5      nU(       a  U(       a  X;  a  Mr  U(       d  M{  SnSnUR                  5        H_  u  pgUR	                  SS5      n	U	(       d  M   [        [        U	5      R                  5       5      U:X  d  MH  UnUR	                  SS5      SS n  O   UR!                  U
R"                  SU
R"                   3SU
R"                   S3UR                  [%        US5      [&        R(                  " UR*                  5      R-                  5       UUS.5        GMt     U R1                  SU[3        U5      S.5        g! [.         a     GM  f = f)u6   列出当前用户生成的视频，关联任务信息Fr?  r   Tr6   z*.*c                 6    U R                  5       R                  $ ry   )rB   st_mtimer5  s    r   r6  4DigitalHumanV2Handler._list_videos.<locals>.<lambda>k  s    affh>O>Or   r9  )r5  rx  i   Nr"   r   r  z?download=1r   )r  r  download_urlrG   size_mbrJ  r   r"   ru  )videosr<  )r  r   r  r   rp   r  r   r   resolver  r  r   rJ  r   rB   rC   r  r   roundr   fromtimestamprT  rP  r,   r  r   )rp  r  rX  rb   user_task_videosuser_has_any_tasksrG  rH  r?  vprD   rB   file_size_mbvideo_path_strrelated_task_idrelated_texts                   r   r  "DigitalHumanV2Handler._list_videosY  s   &&( 5"ICHHWb)EEe&7!%,+Br $$Sb)9)9);%<= ' 
.4OY]^Axx~~'88!vvx#||{;!$QYY[!1 )n.T /  #'!!&IC,3Brc$r("2"2"45G*-'+xx';CR'@ "/  !9!&&B&B166(+$V LL$\15"*"8"8"G"Q"Q"S.(	 	3 _J 	CFS[!IJ  s,   ;AI+I+0I+$I+-BI++
I:9I:c           	      J	   UR                  S5      S   nSU;   a  UR                  S5      S   OSn0 nU(       a8  UR                  S5       H#  nSU;   d  M  UR                  SS5      u  pgXtU'   M%     UR                  S5      S:H  nUR                  S	S5      n	S
U	;   d  U	(       d  U R                  S5        g[        U	-  n
U
R                  5       (       d  U R                  S5        gU R                  5       nU(       a  [        5       n[        U
R                  5       5      nSnUR                  5        Hw  u  nnUR                  SS5      nU(       d  M!  [        [        U5      R                  5       5      U:X  d  MI  UR                  SS5      U:X  d  UR                  S5      (       d  Sn  O   U(       d  U R                  SSS05        gU
R                  5       R                  nU
R                  R!                  5       nUS:X  a  SOSnU R"                  R                  SS5      nU(       Ga  SSKnUR'                  SU5      nU(       Gae  [)        UR+                  S5      5      nUR+                  S5      nU(       a  [)        U5      S-
  OUS-
  n[-        UUS-
  5      nUU-
  S-   nU R/                  S5        U R1                  SU5        U R1                  SU5        U R1                  SSU S U S!U 35        U R1                  S"S#5        U(       a&  SS$KJn  U" U	5      nU R1                  S%S&U S'U 35        U R7                  5         U R9                  5         [;        [        U
5      S(5       nUR=                  U5        U R>                  RA                  URC                  U5      5        SSS5        gU R/                  S)5        U R1                  SU5        U R1                  SU5        U R1                  S"S#5        U(       a&  SS$KJn  U" U	5      nU R1                  S%S&U S'U 35        U R7                  5         U R9                  5         [;        [        U
5      S(5       n[D        RF                  " UU R>                  5        SSS5        g! , (       d  f       g= f! , (       d  f       g= f)*u  提供视频文件（预览/下载），支持 Range 请求（用于浏览器 seek）

认证策略：当 proxy 豁免了 /video/ 路径的认证时，后台 X-Auth-User 为空。
此时跳过所有权检查 — 文件级访问由 proxy 层的列表过滤和随机 task_id 保护。
r  r   r   r   r  r  downloadr   r  ..r  Nr  Fr6   r?  Tr  r   u   无权访问此视频rx  r(  r)  Rangezbytes=(\d+)-(\d*)r      r  r  zContent-Rangezbytes r   r
   r*  r+  )r  zContent-Dispositionzattachment; filename="z"; filename*=UTF-8''r9   ru  )$r   rp   r   r  r  rh   r  r   r   rY  r   r   r  rB   rC   rJ  r   r  r  matchr  groupr   r  r  r  r  r  r  r?   seekr  r  r@   rX  r,  )rp  rk   raw_path	query_strquery_paramspairr  r  is_downloadr  r6   r  rb   r`  ownedrG  rH  tvprS   rR  r/  range_headerr  ri  range_startrange_end_str	range_endr  r  safe_filenamerD   s                                  r   r  "DigitalHumanV2Handler._serve_video  s    ::c?1%*-+DJJsOA&2	!,$;::c1-DA&'O -
 #&&z2c9##$BBG88OOC (*
  ""OOC  &&(ME !3!3!56NE"[[]	Thh|R033tCy0023~Exx,8@Q@Q $ + ##C'3J)KLOO%--	%%''*g~|; ||''4HH1<@E!%++a.1 %A6CC.2UV		9q=9	!*[!81!<""3'  >  !1>B  F;-qSTU^T_2`a  ':2$)(OM$$%:%;M?J`an`o#pr'')  "#j/40AFF;'JJ$$QVVN%;< 1  	36)95'2*!(OM23M?BXYfXghj!#j/4(Aq$**- )(% 10  )(s   *<R"R
R
R"c                    UR                  SS5      R                  S5      S   nSU;   d  U(       d  U R                  S5        g[        U-  nUR	                  5       (       d  U R                  S5        gUR                  5       R                  nUR                  R                  5       nS	S
S
SSS.nUR                  US	5      nU R                  S5        U R                  SU5        U R                  SU5        U R                  SS5        U R                  5         U R                  5         [        [        U5      S5       n[         R"                  " XR$                  5        SSS5        g! , (       d  f       g= f)u   提供背景图片文件r  r   r  r   rf  r  Nr  z	image/pngz
image/jpegz
image/webpz	image/bmpr  z.jpgz.jpegz.webpz.bmpru  r  r  zCache-Controlzmax-age=86400r9   )r   r   r  BG_DIRrh   rB   rC   rJ  r   rp   r  r  r  r  r?   r   rX  r,  r  )	rp  rk   r  bg_pathrS   rR  content_type_mapr/  rD   s	            r   r  %DigitalHumanV2Handler._serve_bg_image  s5   << ;R@FFsKAN88OOC 8#~~OOC LLN**	nn""$ !!
 (++C=36)95/:!#g,%q**- &%%s   >!E((
E6c                 @   UR                  SS5      R                  S5      S   nSU;   a  U R                  S5        g UR                  S5      nU(       a  US   OUnU R                  5       nU(       aS  [	        5       nXF;   aD  Xd   nUR                  S5      (       a*  UR                  S5      U:w  a  U R                  S	S
S05        g [        U-  nUR                  5       (       dV  [        U5      S:X  aG  [        U-  S-  n	U	R                  5       (       a  U	nO#[        U-  S-  n
U
R                  5       (       a  U
nUR                  5       (       d  U R                  S5        g UR                  5       R                  nU R                  S5        U R                  SS5        U R                  SU5        U R                  5         U R                  5         [!        [#        U5      S5       n[$        R&                  " XR(                  5        S S S 5        g ! , (       d  f       g = f)Nr  r   r  r   rf  r  r
   r?  r  r   u   无权访问此声音r   preview.wavr=  r  ru  r  z	audio/wavr  r9   )r   r   r  r  r   rp   r  rM  rh   r   rB   rC   r  r  r  r  r?   r   rX  r,  r  )rp  rk   voice_audio_idpartsrS  r  r[   r   r.   preview_pathref_pathrS   rD   s                r   r  (DigitalHumanV2Handler._serve_voice_audio  s   &JBOUUVYZ[\]>!OOC  $$S)$58. &&(!^F!(99W%%%))G*<*H''g7N-OP  .0
   ""s5zQ%0=@L""$$)
 &0?B??$$!)J  ""OOC OO%--	35)95!#j/4(Aq**- )((s   %!H
Hc                 `   U R                   R                  SS5      nSU;  a  U R                  SSS05        g UR                  S5      S   R	                  5       n[        U R                   R                  S	S
5      5      nU R                  R                  U5      nUR                  SU-   5      nU GHU  nSU;  a  M  UR                  S5      nUS:X  a  M%  US U R                  SSS9nXgS-   S  n	U	R                  S5      (       a  U	S S n	S
S Kn
U
R                  SU5      nU(       a  UR                  S5      OSnS[        [        R                  " 5       5       3n[        R                   R#                  U5      S   R%                  5       =(       d    SnU U 3n[&        U-  n[)        [+        U5      S5       nUR-                  U	5        S S S 5        U R                  SSU[+        U5      [+        U5      U[/        U	5      S.5          g    U R                  SSS05        g ! , (       d  f       N[= f)Nr  r   multipart/form-datar  r      需要 multipart/form-data	boundary=r   r  r   r  	   filename=   

r  re   ignoreerrorsr   r  filename="(.+?)"z	video.mp4upload_r5  r  ru  T)r  	upload_idr6   rk   r  rG      未找到文件)r  rp   r  r   r  r  r  r@   findr  endswithr  searchrj  r   r  rk   splitextr   
UPLOAD_DIRr?   r   r  r   )rp  r/  r  r  r  r  part
header_endheaders_raw	file_datar  filename_matchr  r  rR  	save_name	save_pathrD   s                     r   r
  $DigitalHumanV2Handler._handle_uploadF  s	   ||''; 4g/K%LM%%k215<<>T\\--.>BCzz~.

58+,D4';/JR{
+22782LK\]+I!!'**%crN	YY':KHN2@~++A.kH!#diik"2!34I''""8,Q/557A6C$+cU+I"Y.Ic)nd+q	" , &!)nI$I&  C F 	C'+<!=> ,+s   8H
H-	c                    U R                   R                  SS5      nSU;  a  U R                  SSS05        gUR                  S5      S	   R	                  5       n[        U R                   R                  S
S5      5      nU R                  R                  U5      nUR                  SU-   5      nU GHq  nSU;  a  M  UR                  S5      nUS:X  a  M%  USU R                  SSS9nXgS-   S n	U	R                  S5      (       a  U	SS n	SSKn
U
R                  SU5      nU(       a  UR                  S	5      OSnS[        [        R                  " 5       5       S[        R                   " 5       R"                  SS  3n[$        R&                  R)                  U5      S	   R+                  5       nUS;  a  SnU U 3n[,        U-  n[/        [1        U5      S5       nUR3                  U	5        SSS5        U R                  SSU[1        U5      S U 3U[5        U	5      S!.5          g   U R                  SSS"05        g! , (       d  f       NU= f)#u:   上传背景图片（用于绿幕/透明数字人合成）r  r   r  r  r   r  Nr  r   r  r   r  r  r  r  re   r  r  r   r  r  r  zbg.pngbg_r  rR  rz  r  r  ru  Tr  )r  bg_idr|  bg_urlr  rG   u   未找到背景图片文件)r  rp   r  r   r  r  r  r@   r  r  r  r  r  rj  r   r  r  r  r  rk   r  r   r{  r?   r   r  r   )rp  r/  r  r  r  r  r  r  r  r  r  r  r  r  rR  r  r  rD   s                     r   r  'DigitalHumanV2Handler._handle_bg_uploadv  s.   ||''; 4g/K%LM%%k215<<>T\\--.>BCzz~.

58+,D4';/JR{
+22782LK\]+I!!'**%crN	YY':KHN2@~++A.hH#diik*+1TZZ\-=-=bq-A,BCE''""8,Q/557CDD '#I*Ic)nd+q	" , y>5i[A$I&  G J 	C'+H!IJ ,+s   H;;
I		c           
         U R                  5       nU(       d  g U R                  5       nUc  g UR                  S5      nUR                  SS5      nUR                  SS5      nU(       d  U R                  SSS05        g [	        U5      R                  5       (       d  U R                  SSS	U 305        g [        [        R                  " 5       5      S S
 nS[        [        R                  " 5       5       3nUSSSSUUU[        R                  " 5       R                  5       S.	n[        Xh5        U R                  SSUUSS.5        [        R                   " ["        R$                  XcXtU4SU0SS9R'                  5         g )Nr6   r   u   未命名数字人r\  rW  r  r   u   缺少 video_path   视频文件不存在: r<   dh_r`  pendingr   u   等待训练...)	r   r  r   r  r  rA  r   r?  rJ  ru  Tstarted)r  r   rA  r   r?  )r  r  r   r  )r  r  rp   r  r   rh   r   r  r  r  r   r   rO  rP  r   r  r  r#  r`  rl  )	rp  r  r  r6   r   r\  r   rA  r   s	            r   r  #DigitalHumanV2Handler._handle_train  sm   %%'##%<XXl+
xx 45((9i0g/B%CDJ&&((g1H/U%VWdjjl#BQ'3tyy{+,-
 ($",,.224

	 	7&C$	"
 	 	<<zAX&		

 %'r   c                 @   U R                  5       nU(       d  g U R                  5       nUc  g UR                  S5      nUR                  S5      nUR                  S5      nUR                  SS5      nUR                  S5      n[        UR                  SS5      5      nUR                  S	S
5      n	UR                  SS5      n
UR                  SS5      nU(       a  U(       d  U R	                  SSS05        g [        5       nUR                  U0 5      nU(       a  U R                  US5      (       d  g UR                  SUR                  SS5      5      nU(       a;  [        5       nUR                  U0 5      nU(       a  U R                  US5      (       d  g [        [        R                  " 5       5      S S nUSSSSUU[        R                  " 5       R                  5       S.n[        UU5        U R	                  SSUSS.5        [        R!                  UX4X^XgXX5        g )NrA  r"   rS  r]  r  rd  r  rA  rd  r  re  r  r  r  r   u   缺少 digital_id 或 textr'  r\  rW     声音r<   rh  r  r   u   等待生成...)r   r  r   r  r  rA  r?  rJ  ru  Trc  )r  r   r   )r  r  rp   r+   r  r|   r  r   r   r  r  r   rO  rP  r   r#  rh  )rp  r  r  rA  r"   rS  r]  rd  r  rd  re  r  r-  r  r\  r[   r   r   r   s                      r   r  &DigitalHumanV2Handler._handle_generate  s   %%'##%<XXl+
xx88J'88J	21dhhw,-hh}l;)<((9l3g/K%LM #$WWZ$d++B<<((9bffY	&BC !^FJJx,ET225(CCdjjl#BQ'($",,.224	
	 	7I&CTgQY!Z[ 	8h_d  tA  	Kr   c                    U R                  5       nU(       d  gU R                  5       nUc  gUR                  S5      nUR                  S/ 5      nUR                  S5      nUR                  SS5      nUR                  S/ 5      n[        UR                  SS	5      5      nUR                  S
S5      n	UR                  SS5      n
UR                  SS5      nU(       a  U(       d  U R	                  SSS05        g[        5       nUR                  U0 5      nU(       a  U R                  US5      (       d  gUR                  SUR                  SS5      5      nU(       a;  [        5       nUR                  U0 5      nU(       a  U R                  US5      (       d  gU(       a  U(       d  U R	                  SSS05        g[        [        R                  " 5       5      SS nUSS n/ n[        U5       H|  u  nnU SU 3nUUSSSSUS-    S[        U5       S3UUSS  UUS-   [        U5      [        R                  " 5       R                  5       S!.n[!        UU5        UR#                  U5        M~     U R	                  S"S#UU[        U5      S$[        U5       S%3S&.5        [        [%        UU5      5       H;  u  nu  nnU[        U5      :  a  UU   OSn[&        R)                  UUUUXUXX5        M=     g)'u9   批量生成：一次提交多条文案（串行执行）NrA  textsrS  r]  r  	bg_imagesr  rA  rd  r  re  r  r  r  r   u   缺少 digital_id 或 textsr'  r\  rW  r  r<   r   r  rh  r  r   u   等待批量生成 (r   r
   r  r   )r   rA  r  r   r  r  rA  r"   r?  rB  rC  rJ  ru  Trc  u1    个任务已加入生成队列，将依次处理)r  rA  task_idsr<  r   r  )r  r  rp   r+   r  r|   r  r   r   r  r  r  r   r   rO  rP  r   r  zipr#  rh  )rp  r  r  rA  r  rS  r]  r  r  rd  re  r  r-  r  r\  r[   r   rA  batch_textsr  r  r"   r   r   idxitem_bgs                             r   r  ,DigitalHumanV2Handler._handle_generate_batch  s   %%'##%<XXl+
"%88J'88J	2HH["-	dhhw,-hh}l;)<((9l3g/L%MN #$WWZ$d++B<<((9bffY	&BC !^FJJx,ET225(CCg/L%MNtzz|$Ra(CRj -GAt!
!A3'G"$"#1!A#aK8H7IN(Tc
! 1u";/&lln668I w	*OOG$# .& 	C  ]h-(YZ"
 	 %.c(K.H$I C'4(+c)n(<in$GOOGZx"gu=c %Jr   c                    U R                   R                  SS5      nSU;  a  U R                  SSS05        gUR                  S5      S	   R	                  5       n[        U R                   R                  S
S5      5      nU R                  R                  U5      nUR                  SU-   5      nU GH  nSU;  a  M  UR                  S5      nUS:X  a  M%  USU R                  SSS9nXgS-   S n	U	R                  S5      (       a  U	SS n	SSKn
U
R                  SU5      nU(       a  UR                  S	5      OSnS[        [        R                  " 5       5       3n[        U-  nUR!                  SSS9  ["        R$                  R'                  U5      S	   R)                  5       =(       d    SnSU 3nUU-  n[+        [-        U5      S5       nUR/                  U	5        SSS5        US-  n[0        SS [-        U5      S!S"S#S$S%S&[-        U5      /n[2        R4                  " USS'9  U R                  S(SU[-        U5      [-        U5      U[7        U	5      S).5          g   U R                  SSS*05        g! , (       d  f       N= f)+u   上传音频文件到声音库r  r   r  r  r   r  Nr  r   r  r   r  r  r  r  re   r  r  r   r  r  r  z	audio.wav
vc_upload_Tr^   r  rawr  r=  r   r   r   r   r   r   r   r   r<  ru  )r  rS  r.   rl  r  rG   r  )r  rp   r  r   r  r  r  r@   r  r  r  r  r  rj  r   rM  r   r  rk   r  r   r?   r   r  r   r&   r'   r   )rp  r/  r  r  r  r  r  r  r  r  r  r  r  r  
upload_dirrR  r  r  rD   r  r/   s                        r   r  *DigitalHumanV2Handler._handle_voice_uploadd  se   ||''; 4g/K%LM%%k215<<>T\\--.>BCzz~.

58+,D4';/JR{
+22782LK\]+I!!'**%crN	YY':KHN2@~++A.kH$S%5$67I#i/JTD9''""8,Q/557A6CcUI"Y.Ic)nd+q	" , "O3HT4Y;sE7HC
 NN3t4%!(m	N$I&  [ ^ 	C'+<!=>- ,+s   I00
I>	c                 P  ^^^^^^^^ U R                  5       mT(       d  gU R                  R                  SS5      nSU;   Gao  UR                  S5      S   R	                  5       n[        U R                  R                  SS5      5      nU R                  R                  U5      nUR                  S	U-   5      nSnS
mSmU H  nSU;  a  M  UR                  S5      nUS:X  a  M$  USU R                  SSS9n	XxS-   S n
U
R                  S5      (       a  U
SS n
SSKnUR                  SU	5      nU(       d  Mx  UR                  S5      nSU	;   a  U
nM  US;   a  U
R                  SSS9mM  US:X  d  M  U
R                  SSS9mM     U(       d  U R                  SSS05        gS[        [        R                  " 5       5       3m[         T-  mTR#                  SSS9  TS-  m[%        ['        T5      S 5       nUR)                  U5        SSS5        ['        [*        R,                  " 5       5      SS! mTS"S#SS$TT[.        R0                  " 5       R3                  5       S%.n[5        TU5        U R                  S&STTS'S(.5        UUUUUUU4S) jn[6        R8                  " USS*9R;                  5         gU R=                  5       nUc  gUR                  S+5      mUR                  S,5      =(       d    UR                  S-S
5      mUR                  SS5      mT(       a  [?        T5      RA                  5       (       d  U R                  SSS.05        gS[        [        R                  " 5       5       3m[         T-  m['        [*        R,                  " 5       5      SS! m[5        TTS"S#SS$TT[.        R0                  " 5       R3                  5       S%.5        U R                  S&STTS'S(.5        UUUUUUU4S/ jn[6        R8                  " USS*9R;                  5         g! , (       d  f       GN= f)0u   声音克隆训练Nr  r   r  r  r   r  r   r  u   未命名声音r  s   Content-Dispositionr  r  re   r  r  r   r  r  zname="(.+?)"z	filename=)rQ  r   r&  r  r   u   未找到音频文件r;  Tr^   zraw_upload.wavr  r<   voice_trainr  u   声音克隆训练中...)r   r  r   r  r  rS  r?  rJ  ru  r  )r  rS  r   r   c            	      ,  >  [         R                  R                  [        T5      T
T[        T5      TS9u  p[	        5       nT	US'   TUS'   XT	'   [        U5        [        TSSSS9  g ! [         a!  n[        TSS	[        U5       3S
9   S nAg S nAff = fN)rR  r   rG  r?  r   r   u   声音克隆完成！r  r   u   声音克隆失败: r  r#  r  r_  r   r   r   r   r,   )v_idr\  r[   r1   r&  raw_audio_pathr   r  rZ  rS  rQ  s       r   do_train;DigitalHumanV2Handler._handle_voice_train.<locals>.do_train  s    d'-'8'8'D'DN+Z#&y>7 (E ($D
 *^F'/Jt$*2Jw''18$ ( s(?A  d EYZ]^_Z`YaCbcds   A$A( (
B2BBr  r.   r   rQ  u   音频文件不存在c            	        >  [         R                  R                  TT
T[        T5      TS9u  pT	US'   TUS'   [	        5       nXT	'   [        U5        [        TSSSS9  g ! [         a!  n[        TSS	[        U5       3S
9   S nAg S nAff = fr  r  )r  r\  r[   r1   r.   r&  r   r  rZ  rS  rQ  s       r   do_train_json@DigitalHumanV2Handler._handle_voice_train.<locals>.do_train_json)  s    d'-'8'8'D'D"J#&y>7 (E ($D (0Jt$*2Jw')^F'18$ ( s(?A  d EYZ]^_Z`YaCbcds   AA 
B
)BB
)!r  r  rp   r   r  r  r  r@   r  r  r  r  r  rj  r  r   rM  r   r?   r   r  r  r  r   rO  rP  r   r  r  rl  r  r   rh   )rp  r/  r  r  r  r  r6  r  r  r  
field_datar  
name_match
field_namerD   r   r  r  r.   r&  r  r   r  rZ  rS  rQ  s                     @@@@@@@@r   r  )DigitalHumanV2Handler._handle_voice_train  s   %%'||''; L0#))+6q9@@BH !1!12BA!FGN::??>2DJJux/0EJ*JH)5!YY{3
#";J/66wx6P!Q,-0
&&w//!+CRJYYD
!'--a0
+-!+J#99!+!2!278!2!LJ:-)000JH3 6 ##C'3J)KL S-./H"X-IOOD4O8&)99Nc.)40A
# 1 $**,'+G"%&5$!&lln668	I w	*$"#	& d d$ HT:@@B '')D|,/J&)VTXXlDU-VJxx
D1HT*%5%<%<%>%>##C'3J)KLS-./H"X-I$**,'+Gw"%&5$!&lln668	! 	 $"#	& d d  M$?EEGA 10s   9P
P%c                    U R                  5       nUc  gUR                  S5      nUR                  SS5      n[        UR                  SS5      5      nUR                  SS5      nU(       a  U(       d  U R                  S	S
S05        g[	        5       nX&;  a  U R                  SS
S05        gXb   nU R                  US5      (       d  gUR                  SS5      nU(       a  [        U5      R                  5       (       d  U R                  SS
S05        g[        R                  R                  5       (       d  U R                  SS
S05        g S[        R                  " 5       R                  SS  3n	[        U-  n
U
R                  SSS9  X S3-  n[        R                  R!                  XR                  SS5      5      n[        R                  R#                  UUUR                  SS5      UR                  SS5      UUR                  S5      UR                  S5      [%        U5      US9	  U R                  S SS!U S"U	 S3[%        U5      S#.5        g! [&         a*  nU R                  S$S
S%[%        U5       305         SnAgSnAff = f)&u   使用克隆声音合成语音NrS  r"   r   r  rA  r  r  r  r   u   缺少 voice_id 或 textr     声音不存在r  rH  3   参考音频文件不存在，请重新训练声音i  u   GPT-SoVITS 服务不可用synth_r<   Tr^   r  rU   r   r%  r&  r  r'  r(  )	r"   r  r%  r&  r  r'  r(  r   r  ru  r  r
   )r  r  rk   rb  u   合成失败: )r  rp   r+   r  r   r  r   rh   r#  r  r}  r  r  r  rM  r   r  r8  r   r,   )rp  r  rS  r"   r  r  r[   r   ref_audio_src	output_idrR  r   rH  r1   s                 r   r  .DigitalHumanV2Handler._handle_voice_synthesize;  sr   ##%<88J'xx#dhhw,-((9l3tg/I%JK!g/@%AB $$UH55 		+r2D$7$>$>$@$@g/d%ef  0022g/K%LM	K !1!1"1!5 67I#h.JTD9$D'99K))<<yyQ79I (((:r2:t4!IIm4$yy)9:, ) 
 A(1YKW[\K(& 
  	KgAx/H%IJJ	Ks   DI 
J JJc                    U R                  5       nU(       d  gU R                  5       nUc  gUR                  S5      nUR                  SS5      nUR                  SS5      nU(       d  U R                  SSS	05        g[	        5       nX6;  a  U R                  S
SS05        gXc   nU R                  US5      (       d  gUR                  SS5      nU(       a  [        U5      R                  5       (       d  U R                  S
SS05        g[        R                  R                  5       (       d  U R                  SSSU S3SS.5        g [        U-  n	U	R                  SSS9  U	S-  n
[        R                  R                  XR                  SS5      5      n[        R                  R                  UUUR                  SS5      UR                  SS5      SUR                  S5      UR                  S5      [        U
5      UUR                  S 5      S!9
  U R                  SSSU S"3[        U
5      S#.5        g! [          a*  nU R                  S$SS%[        U5       305         SnAgSnAff = f)&u$   声音试听（合成示例文本）NrS  r"   u6   你好，这是我的克隆声音，效果怎么样？r  r  r  r      缺少 voice_idr  r  r  rH  r   r  ru  Tr  z/reference.wavu/   GPT-SoVITS 不可用，返回原始参考音频)r  r  noter^   r  rU   r   r%  r&  r  rA  r'  r(  r   rn  z/preview.wav)r  r  r  rb  u   试听失败: )r  r  rp   r  r   r  r   rh   r#  r  r}  rM  r   r  r8  r   r,   )rp  r  r  rS  r"   r  r[   r   r  preview_dirr  rH  r1   s                r   r  +DigitalHumanV2Handler._handle_voice_previewz  se   %%'##%<88J'xx XY((9l3g/@%AB!g/@%AB $$UH55 		+r2D$7$>$>$@$@g/d%ef  0022A(>ZI& 
 	K$x/KdT:&6L ))<<yyQ79I (((:r2:t4!IIm4$yy)9:-YYv& )  A(<X #L 1& 
  	KgAx/H%IJJ	Ks   	C,H6 6
I*  I%%I*c           
         U R                  5       nU(       d  gU R                  5       nUc  gUR                  S5      nU(       d  U R                  SSS05        g[	        5       nX4;  a  U R                  SSS05        gXC   nU R                  US5      (       d  gS	SKnUR                  S
S5      nXuS'   [        U5        U R                  SSUSUS    SU S3S.5        g)uW   v7.3: 重新随机生成声音的固定种子（让用户获得不同的音色变体）NrS  r  r   r  r  r  r  r   r   r   r   ru  Tro  r   u    的种子已更新为 u!   ，下次试听将使用新音色)r  r   r  )	r  r  rp   r  r   r  r   r   r   )rp  r  r  rS  r[   r   r   new_seeds           r   r  *DigitalHumanV2Handler._handle_voice_reseed  s    %%'##%<88J'g/@%AB!g/@%AB $$UH55 	>>!Z0 fVC v/FxjPqr"
 	r   c                    [        5       nX;  a  U R                  SSS05        g X!   nU R                  US5      (       d  g UR                  U5        [	        U5        [        UR                  SS5      5      nUR                  5       (       a  [        R                  " [        U5      SS9  U R                  S	SUS
.5        g )Nr  r   r&  r'  rF  r   Tr  ru  r  deleted)r|   r  r  popr   r   rp   rh   rX  r  r   )rp  rA  r-  r  rF  s        r   r  +DigitalHumanV2Handler._delete_digital_human  s    "$ g/C%DE_$$R55
S! R01	MM#i.=CTj!IJr   c                 ^   [        5       nX;  a  U R                  SSS05        g X!   nU R                  US5      (       d  g UR                  U5        [	        U5        [
        U-  nUR                  5       (       a  [        R                  " [        U5      SS9  U R                  SSUS.5        g )	Nr  r   r  r  Tr  ru  r  )
r   r  r  r  r   rM  rh   rX  r  r   )rp  rS  r[   r   rZ  s        r   r  #DigitalHumanV2Handler._delete_voice  s    !g/@%AB $$UH55

8V )	MM#i.=CTh!GHr   c                    U R                  5       n[        U-  nUR                  5       (       d  U R                  SSSU 305        g[	        UR                  5       5      n[        5       n/ n/ nUR                  5        H  u  pU	R                  SS5      n
U
(       d  M   [	        [        U
5      R                  5       5      U:X  d  MH  U	R                  SS5      nU(       a  U(       a  X:w  a  UR                  U5        M  UR                  U5        M     U(       a9  U(       d2  SR                  [        U5      5      nU R                  S	SS
U 305        gU(       d"  U(       d  US:w  a  U R                  S	SS05        gU H   nXX   n	U R                  U	S5      (       a  M     g   UR                  5         U H  nX;   d  M
  XX	 M     U(       a  [        [         USS9  U R                  SSUUS.5        g)u0   删除生成的视频文件及关联任务记录r  r   r  Nr6   r   r?  r  r  u"   无权删除：视频属于用户 adminu3   仅管理员可删除无关联任务的视频文件u   视频rb   r~   ru  T)r  r  related_tasks)r  r  rh   r  r   rY  r   r   rp   r   r  r  r  r  r   rw   r   )rp  r  r  r6   r`  rb   r  other_owner_tasksrG  rH  rr  r?  ownerss                r   r  #DigitalHumanV2Handler._delete_video  s   &&(  (*
  ""g1H
/S%TU Z//12IC((<,Css49,,./>A"-%*;%,,U3!((- ' ]YYs#456Fg1STZS[/\%]^ %67"##C'3h)ij !C:D((x88 ! 	 !C|J ! z5G<C*"
 	r   c                    [         R                  U5        [        5       nX;  a  U R                  SSS05        gX!   nU R	                  US5      (       d  gSnUR                  SS5      nU(       a:  [        U5      R                  5       (       a   [        U5      R                  5         S	n[        U-  nUR                  5       (       a-   [        R                  " [        U5      S	S9  [        SU 35        X!	 [        [         USS9  U R                  SS	UUS.5        g! [         a  n[        S
U SU 35         SnANSnAff = f! [         a  n[        SU 35         SnANmSnAff = f)u9   删除任务记录（可选：同时删除关联视频）r  r   rK  NrL  Fr6   r   Tu   [删除视频] 失败: r   r  u    [清理] 临时目录已删除: u#   [清理] 临时目录删除失败: rb   r~   ru  )r  r  video_also_deleted)r  r  r   r  r  rp   r   rh   r   r,   r-   r  rX  r  r   rw   r   )rp  r   rb   rH  deleted_videor6   r1   temp_dirs           r   r  "DigitalHumanV2Handler._delete_taskJ  sc    	((1g/@%AB~$$T844 XXlB/
$z*1133CZ '') $
 g%??Ac(m4@8
CD N:u8C"/"
 	  C/
|2aSABBC  A;A3?@@As0   D 
,E 
E(D>>E
E(E##E(c                 .   [        5       nX;  a  U R                  SSS05        gX!   nU R                  US5      (       d  gUR                  S5      S;  a(  U R                  SSS	UR                  S5       S
305        g[        R                  U5        [        USSS9  [        U-  nUR                  5       (       a   [        R                  " [        U5      SS9  U R                  SSSUS.5        g! [         a  n[        SU 35         SnAN4SnAff = f)u<   取消任务（POST /api/digital-human/v2/cancel/:task_id）r  r   rK  NrL  r   )r  rc  r  r  u   任务状态 u    不支持取消r  u   用户取消任务r  Tr  u#   [取消] 临时目录清理失败: ru  r  )r  r  r   )r   r  r  rp   r  r  r   r  rh   rX  r  r   r,   r-   )rp  r   rb   rH  r  r1   s         r   r  )DigitalHumanV2Handler._handle_cancel_taskt  s   g/@%AB~$$T844 88H%HHgtxx?Q>RRb/c%de 	((1W[:NO g%??Ac(m4@ 	CT>O\c!de  A;A3?@@As   =C2 2
D<DDr{   N)u   资源))r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r
  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r{   r   r   r  r  
  s    r
;/b
!8!>!(
)".HS4%N'K7Kr].~.B0.h.?`1Kf1f5KnOcb;?zXHt=K~FKP"LK(I(8t(Tfr   r  __main__z<============================================================u<   数字人口播服务 v2.0 (Duix.Avatar + GPT-SoVITS + RMBG)u
     端口: u     工作目录: u!     服务依赖 (需单独启动):z.    Duix.Avatar:  http://127.0.0.1:8383 (WSL2)z/    Duix TTS:     http://127.0.0.1:18180 (WSL2)z'    GPT-SoVITS:   http://127.0.0.1:9880z'    ComfyUI RMBG: http://127.0.0.1:8188u     API 端点:u7       POST /api/digital-human/v2/upload    - 上传视频u:       POST /api/digital-human/v2/train     - 训练数字人u=       POST /api/digital-human/v2/generate  - 生成口播视频u9       POST /api/digital-human/v2/voice/train - 声音克隆z0.0.0.0u0   [启动] 数字人 v2.0 服务运行在 0.0.0.0:u   
[停止] 服务已停止ry   )   )r   g      ?)g333333?r>  )NN)rP  r  )r  rw  r  rm  rl  )r  rw  r  )Xr  r  sysri   r  r   rX  r>   rH   r&   r  r  r  r  rv  urllib.errorr#  pathlibr   r   http.serverr   r   socketserverr   r   r  r  rr  r  rm  r   r%   r  r2   r7   rV   WORK_DIRr  rI  r  r  rM  
MODELS_DIRr{  dr   rz   r   r   PORTrK  
_threadingr  ro   rm   rw   r|   r   r   r   r   r   r   rr   r   r   r   r   r2  r=  rM  r^  rf  ri  rk  r  r  r  r#  r  r  r  r-   serverserve_foreverKeyboardInterruptr  r{   r   r   <module>r     s@  B 
 
                < ' ,,*++;<N   B 789$
9$	9$
6!8#
8#
=(
iXz:v	VAGGD4G( 
W !66 .-
 o 
  oo'oo__		/F6&-:,&1TA&""(X\&|4"H)X IM365r OSBY Y|c	 c	P_N _NHI I\ 
	.* rf4 rfn+ z	(O	
HI	(O	Jtf
	XJ
'(	G	
-.	:<	;=	35	35	G	/	CE	FH	IK	EG	G )T!24IJF	<TF
CD1 2  *+s   I% %JJ