
    =i<*                        d Z ddlZddlZddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZmZmZ ddlmZ ddlZddlmZ  ee      j+                         j,                  d   d	z  Z	 ej1                  d
d      5 Z ej4                  e      Zddd        eej=                  dd            j?                         Z  ejB                   e"ee ejF                                ejH                  e%      Z&e&jO                   e"ee ejF                                e(ej=                  dd            Z) e(ej=                  dd            Z* e(ej=                  dd            Z+deee   eee,   f   fdZ-de(fdZ.de(dee,   de/fdZ0d#dede1deee   eee,   e(f   fdZ2d$ded e1d!e/de,fd"Z3y# 1 sw Y   xY w# e$ r i ZY *w xY w)%u   文件转写核心逻辑。

职责概览：
- 对输入音频进行分块转写并拼接文本与时间戳。
- 当单块转写失败/空结果时自动降级为更小子块重试。
- 提供可配置的空结果容错模式（fail_on_empty）。
    N)ListOptionalTuple   )	get_model)split_audio_to_chunks!insert_punctuations_into_segmentsmerge_sentences_from_tokens)AudioSegment)Pathzgzzm_config.jsonrzutf-8)encoding	log_levelINFO)level&asr_min_chunk_sec_for_partial_fallback   !asr_partial_result_coverage_ratiog?asr_partial_result_tail_gap_sec   returnc                 @   t        | dd      xs# t        | t              r| j                  d      nd}t        | dd      xs# t        | t              r| j                  d      nd}t        | dd      xs' t        | t              r| j                  d      ndxs g }|||fS )z=Normalize transcribe result to (language, text, time_stamps).languageNtext time_stamps)getattr
isinstancedictget)firstr   text_valts_lists       /app/gzzm/transcribe.py_extract_transcribe_payloadr%   (   s    uj$/oZX]_cMdEIIj4IjnHufd+eZPUW[E\		&0AbdHe]D1zR\]bdhRieii6NoszxzGXw&&    c           
         t        | t              r3| j                  d| j                  d| j                  dd                  }n+t        | dd       }|t        | dd       }|t        | dd      }	 t	        |xs d      S # t
        $ r Y yw xY w)Nendfinishend_time        )r   r   r    r   float	Exception)ts_itemend_vals     r$   _extract_end_secondsr0   0   s    '4 ++eW[[7;;zSV;W%XY'5$/?gx6G?gz37GW^$$ s   0A? ?	B
Bchunk_durationr#   c                     | t         k  ry|syt        d |D        d      }|dk  ry|t        d|       z  }t        d| |z
        }|t        k  xs	 |t        kD  S )NFc              3   2   K   | ]  }t        |        y wNr0   .0tss     r$   	<genexpr>z6_should_fallback_for_partial_result.<locals>.<genexpr>E   s     B'B+B/'   r+   defaultr   TgMbP?)"MIN_CHUNK_SEC_FOR_PARTIAL_FALLBACKmaxPARTIAL_RESULT_COVERAGE_RATIOPARTIAL_RESULT_TAIL_GAP_SEC)r1   r#   max_end_seccoverage_ratiotail_gap_secs        r$   #_should_fallback_for_partial_resultrD   ?   sj    ::B'BCPKa 3un#==NsN[89L99g\Lg=ggr&   
chunk_pathfallback_secondsc                    t               }|t        d      t        j                  |       }t	        |      dz  }	 |j                  | dd      }|rW|d   }t        |      \  }}}	|s|	r?t        ||	      r-t        d |	D        d	      }
t        j                  d
| ||
       n|||	|fS t        j                  d|        t        dt        |            dz  }d}g }g }t        dt	        |      |      D ]1  }||||z    }t        j                   dd      5 }|j#                  |j$                  d       |j$                  }ddd       	 |j                  dd      }|s	 	 t'        j(                  |       |d   }t        |      \  }}}|xs |}|r|j+                  |       |dz  }|D ]_  }t-        |t.              rt1        |j3                  d|j3                  d|j3                  dd                        }t1        |j3                  d|j3                  d|j3                  d|                        }|j3                  d|j3                  dd            }nt5        |dd      }t5        |dd      }|t7        |d      rt5        |d      }|t7        |d      rt5        |d      }t1        |xs d      }t1        |xs |      }t5        |dt5        |dd            }|j+                  t9        ||z   d      t9        ||z   d      |d       b 	 	 t'        j(                  |       4 |dj;                  |      ||fS # t        $ r t        j                  d|        Y w xY w# 1 sw Y   %xY w# t        $ r Y w xY w# t        $ r@ t        j                  d       Y 	 t'        j(                  |       # t        $ r Y w xY ww xY w# t        $ r Y w xY w# 	 t'        j(                         w # t        $ r Y w w xY wxY w) zTranscribe one chunk; if it fails, split it into smaller pieces and retry.

    Returns (language, text, time_stamps, chunk_duration_sec).
    N   模型尚未加载完成g     @@T)audior   return_time_stampsr   c              3   2   K   | ]  }t        |        y wr4   r5   r6   s     r$   r9   z6_run_transcribe_chunk_with_fallback.<locals>.<genexpr>a   s     &R'B';B'?'r:   r+   r;   u]   检测到疑似截断结果，降级为小分片重试: chunk=%s duration=%.3fs max_end=%.3fsu8   切片转写结果为空，降级为小分片重试：%su;   模型对切片转写失败，降级为小分片重试：%sr   i  Fz.wav)deletesuffixwav)formatstartbegin
start_timer(   r)   r*   r   wordr      rP   r(   r   u#   降级子切片仍转写失败：%s)r   RuntimeErrorr   	from_filelen
transcriber%   rD   r>   loggerwarningr-   	exceptionintrangetempfileNamedTemporaryFileexportnameosremoveappendr   r   r,   r    r   hasattrroundjoin)rE   rF   MODELsegr1   resultsr!   r   r"   r#   rA   fallback_msmerged_languagemerged_texts	merged_tssub_start_mssub_segsub_tmpsub_pathsub_results	sub_firstsub_langsub_textsub_ts_list
sub_offsetr8   rP   r(   text_seg
start_attrend_attrs                                  r$   #_run_transcribe_chunk_with_fallbackr}   N   s   
 KE}566

 
 
,CX&Nd""dW["\AJE*Ee*L'Hh76~wO"%&R'&R\_"`KNNw"&#	 $XwFFQS]^ a-./$6K%)O LIaS;7lL;$>?((fENN7<<N6||H F'	**D]a*bKB		(#C $AI.I).T+Hh-9O##H-%.J!b$'!"&&"&&"&&WXBY2Z"[\EubffXrvvjRW?X.Y Z[C!vvfbffVR.@AH!(Wd!;J&r5$7H!)gb,.G%,R%>
'GB
,C#*2z#:!*"34E 1E2C&r672vr3JKH  "5:#5q9 z!115$" ! "2		(#U 8\ BGGL19nLLm  dVXbcd FER    	BHM		(# 	  		(# s   A,L= )L= *M#	N !M07FN O=M M #M-	0	M=<M= O	O!N88	OOO		O	OOPO43P4	P 	=P?P 	 Ptmp_pathchunk_secondsfail_on_emptyc                    t               }|t        d      t        j                         }t        | |      }t        j                  dt        |      |       g }d}d}g }		 |D ]  }
t        |
|dkD  rt        dt        d|dz              nd	
      \  }}}}|xs |}|r|j                  |       |D ]_  }t        |t              rt        |j                  d|j                  d|j                  dd                        }t        |j                  d|j                  d|j                  d|                        }|j                  d|j                  dd            }nt        |dd      }t        |dd      }|t!        |d      rt        |d      }|t!        |d      rt        |d      }t        |xs d      }t        |xs |      }t        |dt        |dd            }|	j                  t#        ||z   d      t#        ||z   d      |d       b ||z  } t        j                         |z
  }|su|rt        dt        |       d| d      t        j%                  dt        |             |dt#        |d      t        |      g g d|D ]  }	 t'        j(                  |        S dj-                  |      }t/        |	|      }t1        |      }|dj-                  |      j3                         t#        |d      t        |      ||d|D ]  }	 t'        j(                  |        S # t*        $ r Y w xY w# t*        $ r Y 6w xY w# |D ]'  }	 t'        j(                  |       # t*        $ r Y %w xY w w xY w)a  Transcribe a file at `tmp_path` using the global model.

    Returns a dict compatible with the previous JSONResponse payload.
    This function will remove any temporary chunk files it created, but
    does not remove `tmp_path` (caller is responsible).
    NrH   u    切分后得到 %d 个 chunk: %sr+   r      r         )rF   rP   rQ   rR   r(   r)   r*   r   rS   r   rT   rU   u5   识别失败: 未从任何切片获得文本 (chunks=z, paths=)u8   本次转写未获得文本，返回空结果: chunks=%d   )r   r   time_secchunksr   time_stamps_tokens )r   rV   timer   rZ   debugrX   r}   r>   minre   r   r   r,   r    r   rf   rg   r[   rc   rd   r-   rh   r	   r
   strip)r~   r   r   ri   start_inferchunk_pathstextsr   start_offsetall_time_stampsrE   
chunk_lang
chunk_textchunk_ts_listr1   r8   rP   r(   rz   r{   r|   
infer_timep	full_textr   merged_time_stampss                             r$   transcribe_audio_filer      s    KE}566))+K'-@K
LL3S5E{SE"HL"$OE%JDgHUXYHYQB0B(C!D_aEAJ
M>
  -:HZ(#b$'!"&&"&&"&&WXBY2Z"[\EubffXrvvjRW?X.Y Z[C!vvfbffVR.@AH!(Wd!;J&r5$7H!)gb,.G%,R%>
'GB
,C#*2z#:!*"34E 1E2C&r672vr3JKH&&"5<#7; |!3Q7$( ! $, N*LA &D YY[;.
"%Z[^_j[kZllt  vA  uB  BC  $D  E  ENNUWZ[fWgh$!*a0k*!&(. A		!  GGEN	>PYZ89KL !HHUO))+j!,+&-"4
 A		!   9  A		!  s\   !H,M L)+A M L8)	L54L58	MMM5M$#M5$	M0	-M5/M0	0M5)r   )T)4__doc__rc   r   loggingr_   typingr   r   r   modelr   utils.audio_utilsr   r	   r
   pydubr   jsonpathlibr   __file__resolveparents_config_pathopen_fload_GZZM_CONFIGr-   strr    upper	LOG_LEVELbasicConfigr   r   	getLogger__name__rZ   setLevelr,   r=   r?   r@   r   r%   r0   boolrD   r]   r}   r    r&   r$   <module>r      s   
    ( (  t t    H~%%'//25GG			3		1R tyy} 
2
   f56<<>	   ''9gllC D			8	$ GLL9 : &+<+;+;<dfh+i%j " %l&6&67Z\_&` a #L$4$45VXY$Z[ '%sDJ0N*O 'U h hT
 hW[ hSMC SM3 SMX]^fgj^kmprvw{r|  D  _D  YE SMlYC Y YD Y\` YY 
2	1 Ls*   $F2 7F%
F2 %F/*F2 2F=<F=