Ë
    ÿÍ:jÆ(  ã                   ó  — d dl Zd dlZd dlmZ d dlmZmZm	Z	m
Z
 d dlZd dlmZmZ d dlmZmZmZ d dlmZ  ee«      Z G d„ d«      Z G d	„ d
«      Z	 	 ddej2                  deeef   deeeee   f      dedeeef   f
d„Z G d„ d«      Z y)é    N)ÚPipeline)ÚOptionalÚUnionÚListÚTuple)Ú
load_audioÚSAMPLE_RATE)ÚTranscriptionResultÚAlignedTranscriptionResultÚProgressCallback)Ú
get_loggerc            	       óh   — e Zd ZdZdeeeeef      fd„Zdededeeeef      fd„Z	dede
e   fd	„Zy
)ÚIntervalTreezû
    Simple interval tree for fast overlap queries using sorted array + binary search.

    Uses O(n) space and provides O(log n) query time instead of O(n) linear scan.
    This achieves ~228x speedup for speaker assignment in long-form content.
    Ú	intervalsc                 óÖ  — |s<t        j                  g «      | _        t        j                  g «      | _        g | _        yt        |d„ ¬«      }t        j                  |D �cg c]  }|d   ‘Œ	 c}t         j                  ¬«      | _        t        j                  |D �cg c]  }|d   ‘Œ	 c}t         j                  ¬«      | _        |D �cg c]  }|d   ‘Œ	 c}| _        yc c}w c c}w c c}w )z”
        Initialize the interval tree with diarization segments.

        Args:
            intervals: List of (start, end, speaker) tuples
        Nc                 ó   — | d   S )Nr   © ©Úxs    úe/home/mcse/projects/srt_converter/srt-converter-venv/lib/python3.12/site-packages/whisperx/diarize.pyú<lambda>z'IntervalTree.__init__.<locals>.<lambda>$   s
   € ¸1¸Q¹4€ ó    ©Úkeyr   )Údtypeé   é   )ÚnpÚarrayÚstartsÚendsÚspeakersÚsortedÚfloat64)Úselfr   Úsorted_intervalsÚis       r   Ú__init__zIntervalTree.__init__   s¬   € ñ ÜŸ(™( 2›,ˆDŒKÜŸ™ ›ˆDŒIØ')ˆDŒMØô " )±Ô@ÐÜ—h‘hÐ.>Ö?¨  !£Ò?ÄrÇzÁzÔRˆŒÜ—H‘HÐ,<Ö= q˜a ›dÒ=ÄRÇZÁZÔPˆŒ	Ø'7Ö8 !˜˜1›Ò8ˆ�ùò  @ùÚ=ùÚ8s   Á CÂC!ÃC&ÚstartÚendÚreturnc                 óÞ  — t        | j                  «      dk(  rg S t        j                  | j                  |d¬«      }|dk(  rg S t	        d|«      }| j                  |   |k  | j
                  |   |kD  z  }g }t        j                  |«      d   D ][  }t        | j
                  |   |«      t        | j                  |   |«      z
  }|dkD  sŒ<|j                  | j                  |   |f«       Œ] |S )a)  
        Find all intervals that overlap with [start, end] and compute intersection.

        Args:
            start: Query interval start time
            end: Query interval end time

        Returns:
            List of (speaker, intersection_duration) tuples for overlapping segments
        r   Úleft)Úside)Úlenr    r   ÚsearchsortedÚslicer!   ÚwhereÚminÚmaxÚappendr"   )	r%   r)   r*   Ú	right_idxÚ
candidatesÚoverlapsÚresultsÚidxÚintersections	            r   ÚqueryzIntervalTree.query)   sè   € ô ˆt�{‰{Ó˜qÒ ØˆIô —O‘O D§K¡K°¸6ÔBˆ	Ø˜Š>ØˆIô ˜1˜iÓ(ˆ
Ø—K‘K 
Ñ+¨cÑ1°d·i±iÀ
Ñ6KÈeÑ6SÑTˆàˆÜ—8‘8˜HÓ% aÑ(ò 	CˆCÜ˜tŸy™y¨™~¨sÓ3´c¸$¿+¹+ÀcÑ:JÈEÓ6RÑRˆLØ˜aÓØ—‘ §¡¨cÑ 2°LÐAÕBð	Cð ˆr   Útimec                 óà   — t        | j                  «      dk(  ry| j                  | j                  z   dz  }t        j                  t        j
                  ||z
  «      «      }| j                  |   S )zò
        Find the speaker of the nearest segment to a given time point.

        Args:
            time: Time point to find nearest segment for

        Returns:
            Speaker ID of nearest segment, or None if no segments exist
        r   Nr   )r/   r    r!   r   ÚargminÚabsr"   )r%   r=   ÚmidsÚnearest_idxs       r   Úfind_nearestzIntervalTree.find_nearestH   s[   € ô ˆt�{‰{Ó˜qÒ Øð —‘˜dŸi™iÑ'¨1Ñ,ˆÜ—i‘i¤§¡ t¨d¡{Ó 3Ó4ˆØ�}‰}˜[Ñ)Ð)r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   ÚfloatÚstrr(   r<   r   rC   r   r   r   r   r      sc   „ ñð9 $ u¨U°E¸3Ð->Ñ'?Ñ"@ó 9ð&˜5ð  uð °°e¸CÀ¸JÑ6GÑ1Hó ð>* ð *¨8°C©=ô *r   r   c                   óø   — e Zd Z	 	 	 	 ddeeeej                  f      fd„Z	 	 	 	 	 ddeee	j                  f   dee   dee   dee   ded	ed
eeej                   eeeee   f      f   ej                   f   fd„Zy)ÚDiarizationPipelineNÚdevicec                 óâ   — t        |t        «      rt        j                  |«      }|xs d}t        j                  d|› �«       t        j                  |||¬«      j                  |«      | _	        y )Nz(pyannote/speaker-diarization-community-1zLoading diarization model: )ÚtokenÚ	cache_dir)
Ú
isinstancerI   ÚtorchrL   ÚloggerÚinfor   Úfrom_pretrainedÚtoÚmodel)r%   Ú
model_namerN   rL   rO   Úmodel_configs         r   r(   zDiarizationPipeline.__init__\   s^   € ô �fœcÔ"Ü—\‘\ &Ó)ˆFØ!ÒOÐ%OˆÜ�‰Ð1°,°Ð@ÔAÜ×-Ñ-¨lÀ%ÐS\Ô]×`Ñ`ÐagÓhˆ�
r   ÚaudioÚnum_speakersÚmin_speakersÚmax_speakersÚreturn_embeddingsÚprogress_callbackr+   c                 óŠ  ‡‡‡— t        |t        «      rt        |«      }t        j                  |ddd…f   «      t
        dœ}d}‰�dddœŠdgŠdˆˆˆfd„	} | j                  |f|||dœ|�d	|ini ¤Ž}	‰� ‰d
«       |	j                  }
|r|	j                  nd}t        j                  |
j                  d¬«      g d¢¬«      }|d   j                  d„ «      |d<   |d   j                  d„ «      |d<   |rC|�At        |
j                  «       «      D ��ci c]  \  }}|||   j                  «       “Œ }}}||fS |r|dfS |S c c}}w )a±  
        Perform speaker diarization on audio.

        Args:
            audio: Path to audio file or audio array
            num_speakers: Exact number of speakers (if known)
            min_speakers: Minimum number of speakers to detect
            max_speakers: Maximum number of speakers to detect
            return_embeddings: Whether to return speaker embeddings
            progress_callback: Optional callable receiving a float (0-100) with progress percentage

        Returns:
            If return_embeddings is True:
                Tuple of (diarization dataframe, speaker embeddings dictionary)
            Otherwise:
                Just the diarization dataframe
        N)ÚwaveformÚsample_rate)ç        ç      I@)rc   ç     ÀX@)ÚsegmentationÚ
embeddingsrb   c                 ó¤   •— |�M|�J|dkD  rD‰j                  | d«      \  }}|t        ||z  d«      ||z
  z  z   }|‰	d   kD  r|‰	d<    ‰
|«       y y y y y )Nr   )rb   rd   g      ð?)Úgetr3   )Ú	step_nameÚstep_artifactÚfileÚtotalÚ	completedÚoffsetr*   ÚpctÚ_STEP_RANGESÚlast_pctr^   s           €€€r   Úhookz*DiarizationPipeline.__call__.<locals>.hook”   sx   ø€ ØÐ$¨Ð)>À5È1Â9Ø".×"2Ñ"2°9¸kÓ"J‘K�F˜CØ ¤3 y°5Ñ'8¸#Ó#>À#ÈÁ,Ñ#OÑO�CØ˜X a™[Ò(Ø&)˜ ™Ù)¨#Õ.ð )ð DMÐ)>Ð$r   )rZ   r[   r\   rr   g      Y@T)Úyield_label)ÚsegmentÚlabelÚspeaker)Úcolumnsrt   c                 ó   — | j                   S ©N)r)   r   s    r   r   z.DiarizationPipeline.__call__.<locals>.<lambda>«   s
   € ÀAÇGÁG€ r   r)   c                 ó   — | j                   S ry   )r*   r   s    r   r   z.DiarizationPipeline.__call__.<locals>.<lambda>¬   s
   € À!Ç%Á%€ r   r*   )NNN)rP   rI   r   rQ   Ú
from_numpyr	   rV   Úspeaker_diarizationÚspeaker_embeddingsÚpdÚ	DataFrameÚ
itertracksÚapplyÚ	enumerateÚlabelsÚtolist)r%   rY   rZ   r[   r\   r]   r^   Ú
audio_datarr   ÚoutputÚdiarizationrf   Ú
diarize_dfÚsrv   r}   rp   rq   s         `         @@r   Ú__call__zDiarizationPipeline.__call__i   s‚  ú€ ô4 �eœSÔ!Ü˜uÓ%ˆEä×(Ñ(¨¨t²Q¨w©Ó8Ü&ñ
ˆ
ð
 ˆØÐ(ð
 !,Ø*ñˆLð �uˆH÷/ð �—‘Øð
à%Ø%Ø%ñ	
ð
 "&Ð!1�˜‰~°rñ
ˆð Ð(Ù˜eÔ$à×0Ñ0ˆÙ2C�V×.Ò.Èˆ
ä—\‘\ +×"8Ñ"8ÀTÐ"8Ó"JÒTsÔtˆ
Ø(¨Ñ3×9Ñ9Ñ:KÓLˆ
�7ÑØ& yÑ1×7Ñ7¹ÓHˆ
�5Ñá Ð!7ÜT]Ð^i×^pÑ^pÓ^rÓTs×!tÁjÀaÈ '¨:°a©=×+?Ñ+?Ó+AÑ"AÐ!tÐÑ!tØÐ1Ð1Ð1ñ Ø˜tÐ#Ð#àÐùó "us   ÄD?)NNÚcpuN)NNNFN)rD   rE   rF   r   r   rI   rQ   rL   r(   r   ÚndarrayÚintÚboolr   Útupler~   r   ÚdictÚlistrH   rŠ   r   r   r   rK   rK   [   sÞ   „ ð ØØ5:Øñið ˜˜s E§L¡LÐ0Ñ1Ñ2ó	ið  '+Ø&*Ø&*Ø"'Ø.2ñMà�S˜"Ÿ*™*�_Ñ%ðMð ˜s‘mðMð ˜s‘mð	Mð
 ˜s‘mðMð  ðMð ,ðMð 
ˆu�R—\‘\ 8¨D°°d¸5±kÐ1AÑ,BÑ#CÐCÑDÀbÇlÁlÐRÑ	SôMr   rK   rˆ   Útranscript_resultr}   Úfill_nearestr+   c                 ó†  — |j                  dg «      }|r| �t        | «      dk(  r|S | j                  «       D ��cg c]  \  }}|d   |d   |d   f‘Œ }}}t        |«      }|D �]N  }	|	j                  dd«      }
|	j                  dd«      }|j	                  |
|«      }|rGi }|D ]  \  }}|j                  |d«      |z   ||<   Œ t        |j                  «       d„ ¬«      d   |	d<   n"|r |
|z   d	z  }|j                  |«      }|r||	d<   d
|	v sŒª|	d
   D ]�  }d|vrŒ|d   }|j                  d|«      }|j	                  ||«      }|rGi }|D ]  \  }}|j                  |d«      |z   ||<   Œ t        |j                  «       d„ ¬«      d   |d<   Œz|sŒ}||z   d	z  }|j                  |«      }|sŒ™||d<   ŒŸ �ŒQ |�||d<   |S c c}}w )aŽ  
    Assign speakers to words and segments in the transcript.

    Uses an interval tree for O(log n) overlap queries instead of O(n) linear scan,
    achieving ~228x speedup for long-form content (3+ hour podcasts).

    Args:
        diarize_df: Diarization dataframe from DiarizationPipeline
        transcript_result: Transcription result to augment with speaker labels
        speaker_embeddings: Optional dictionary mapping speaker IDs to embedding vectors
        fill_nearest: If True, assign speakers even when there's no direct time overlap

    Returns:
        Updated transcript_result with speaker assignments and optionally embeddings
    Úsegmentsr   r)   r*   rv   rb   c                 ó   — | d   S ©Nr   r   r   s    r   r   z&assign_word_speakers.<locals>.<lambda>å   s   € ÈaÐPQÉd€ r   r   r   Úwordsc                 ó   — | d   S r—   r   r   s    r   r   z&assign_word_speakers.<locals>.<lambda>ü   s   € ÐWXÐYZÑW[€ r   r}   )rh   r/   Úiterrowsr   r<   r4   ÚitemsrC   )rˆ   r’   r}   r“   Útranscript_segmentsÚ_Úrowr   ÚtreeÚsegÚ	seg_startÚseg_endr8   Úspeaker_intersectionsrv   r;   Úseg_midÚnearest_speakerÚwordÚ
word_startÚword_endÚword_overlapsÚword_mids                          r   Úassign_word_speakersr«   ¹   sF  € ð* ,×/Ñ/°
¸BÓ?ÐÙ *Ð"4¼¸J»È1Ò8LØ Ð ð
 !×)Ñ)Ó+÷áˆAˆsð 
ˆW‰�s˜5‘z 3 y¡>Ò2ð€Iñ ô ˜	Ó"€Dà"ó (:ˆØ—G‘G˜G SÓ)ˆ	Ø—'‘'˜% Ó%ˆð —:‘:˜i¨Ó1ˆáà68Ð!Ø)1ò hÑ%�˜Ø1F×1JÑ1JÈ7ÐTWÓ1XÐ[gÑ1gÐ% gÒ.ðhä Ð!6×!<Ñ!<Ó!>ÁNÔSÐTUÑVˆC�	ŠNÙà  7Ñ*¨aÑ/ˆGØ"×/Ñ/°Ó8ˆOÙØ!0��I‘ð �cŠ>Ø˜G™ò :�Ø $Ñ&Øà! '™]�
ØŸ8™8 E¨:Ó6�à $§
¡
¨:°xÓ @�á Ø,.Ð)Ø1>ò pÑ-˜ Ø9N×9RÑ9RÐSZÐ\_Ó9`ÐcoÑ9oÐ-¨gÒ6ðpä&)Ð*?×*EÑ*EÓ*GÉ^Ô&\Ð]^Ñ&_�D˜’OÚ!Ø *¨XÑ 5¸Ñ:�HØ&*×&7Ñ&7¸Ó&A�OÚ&Ø*9˜˜Yšò%:ð-(:ðV Ð%Ø2DÐÐ.Ñ/àÐùóis   ºF=c                   ó(   — e Zd Zddededee   fd„Zy)ÚSegmentNr)   r*   rv   c                 ó.   — || _         || _        || _        y ry   )r)   r*   rv   )r%   r)   r*   rv   s       r   r(   zSegment.__init__  s   € ØˆŒ
ØˆŒØˆ�r   ry   )rD   rE   rF   r�   r   rI   r(   r   r   r   r­   r­   
  s    „ ñ˜Sð  cð °8¸C±=ô r   r­   )NF)!Únumpyr   Úpandasr~   Úpyannote.audior   Útypingr   r   r   r   rQ   Úwhisperx.audior   r	   Úwhisperx.schemar
   r   r   Úwhisperx.log_utilsr   rD   rR   r   rK   r   r�   rI   r‘   rH   rŽ   r«   r­   r   r   r   ú<module>r¶      sÃ   ðÛ Û Ý #ß /Ó /Û ç 2ß ]Ñ ]Ý )á	�HÓ	€÷J*ñ J*÷Z[ñ [ðB <@Øñ	NØ—‘ðNàÐ7Ð9LÐLÑMðNð !  c¨4°©;Ð&6Ñ!7Ñ8ðNð ð	Nð
 Ð%Ð':Ð:Ñ;óN÷bò r   