§
    øóÉjÆ(  ã                   ó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j        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 dS )é    N)ÚPipeline)ÚOptionalÚUnionÚListÚTuple)Ú
load_audioÚSAMPLE_RATE)ÚTranscriptionResultÚAlignedTranscriptionResultÚProgressCallback)Ú
get_loggerc            	       óˆ   — 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d
S )Ú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 | _        dS t          |d„ ¬¦  «        }t          j        d„ |D ¦   «         t           j        ¬¦  «        | _        t          j        d„ |D ¦   «         t           j        ¬¦  «        | _        d„ |D ¦   «         | _        dS )z”
        Initialize the interval tree with diarization segments.

        Args:
            intervals: List of (start, end, speaker) tuples
        Nc                 ó   — | d         S )Nr   © ©Úxs    ús/home/lighthouse/badge-speaker-benchmark/.benchmark-venv-community/lib/python3.11/site-packages/whisperx/diarize.pyú<lambda>z'IntervalTree.__init__.<locals>.<lambda>$   s
   € ¸1¸Q¼4€ ó    ©Úkeyc                 ó   — g | ]
}|d          ‘ŒS )r   r   ©Ú.0Úis     r   ú
<listcomp>z)IntervalTree.__init__.<locals>.<listcomp>%   s   € Ð?Ð?Ð?¨  !¤Ð?Ð?Ð?r   )Údtypec                 ó   — g | ]
}|d          ‘ŒS )é   r   r   s     r   r   z)IntervalTree.__init__.<locals>.<listcomp>&   s   € Ð=Ð=Ð= q˜a œdÐ=Ð=Ð=r   c                 ó   — g | ]
}|d          ‘ŒS )é   r   r   s     r   r   z)IntervalTree.__init__.<locals>.<listcomp>'   s   € Ð8Ð8Ð8 !˜˜1œÐ8Ð8Ð8r   )ÚnpÚarrayÚstartsÚendsÚspeakersÚsortedÚfloat64)Úselfr   Úsorted_intervalss      r   Ú__init__zIntervalTree.__init__   s·   € ð ð 	Ýœ( 2™,œ,ˆDŒKÝœ ™œˆDŒIØ')ˆDŒMØˆFõ " )°°Ð@Ñ@Ô@ÐÝ”hÐ?Ð?Ð.>Ð?Ñ?Ô?ÅrÄzÐRÑRÔRˆŒÝ”HÐ=Ð=Ð,<Ð=Ñ=Ô=ÅRÄZÐPÑPÔPˆŒ	Ø8Ð8Ð'7Ð8Ñ8Ô8ˆŒˆˆr   ÚstartÚendÚreturnc                 óä  — t          | j        ¦  «        dk    rg S t          j        | j        |d¬¦  «        }|dk    rg S t	          d|¦  «        }| j        |         |k     | j        |         |k    z  }g }t          j        |¦  «        d         D ]a}t          | j        |         |¦  «        t          | j        |         |¦  «        z
  }|dk    r"| 	                    | j
        |         |f¦  «         Œb|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/   r0   Ú	right_idxÚ
candidatesÚoverlapsÚresultsÚidxÚintersections	            r   ÚqueryzIntervalTree.query)   sø   € õ ˆtŒ{ÑÔ˜qÒ Ð ØˆIõ ”O D¤K°¸6ÐBÑBÔBˆ	Ø˜Š>ˆ>ØˆIõ ˜1˜iÑ(Ô(ˆ
Ø”K 
Ô+¨cÒ1°d´iÀ
Ô6KÈeÒ6SÑTˆàˆÝ”8˜HÑ%Ô% aÔ(ð 	Cð 	CˆCÝ˜tœy¨œ~¨sÑ3Ô3µc¸$¼+ÀcÔ:JÈEÑ6RÔ6RÑRˆLØ˜aÒÐØ—’ ¤¨cÔ 2°LÐAÑBÔBÐBøØˆr   Útimec                 óÆ   — t          | j        ¦  «        dk    rdS | 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$   )r5   r'   r(   r%   ÚargminÚabsr)   )r,   rC   ÚmidsÚnearest_idxs       r   Úfind_nearestzIntervalTree.find_nearestH   s]   € õ ˆtŒ{ÑÔ˜qÒ Ð Ø�4ð ”˜dœiÑ'¨1Ñ,ˆÝ”i¥¤ t¨d¡{Ñ 3Ô 3Ñ4Ô4ˆØŒ}˜[Ô)Ð)r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   ÚfloatÚstrr.   rB   r   rI   r   r   r   r   r      s§   € € € € € ðð ð9 $ u¨U°E¸3Ð->Ô'?Ô"@ð 9ð 9ð 9ð 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dS )ÚDiarizationPipelineNÚcpuÚdevicec                 óø   — t          |t          ¦  «        rt          j        |¦  «        }|pd}t                               d|› �¦  «         t          j        |||¬¦  «                             |¦  «        | _	        d S )Nz(pyannote/speaker-diarization-community-1zLoading diarization model: )ÚtokenÚ	cache_dir)
Ú
isinstancerO   ÚtorchrS   ÚloggerÚinfor   Úfrom_pretrainedÚtoÚmodel)r,   Ú
model_namerU   rS   rV   Úmodel_configs         r   r.   zDiarizationPipeline.__init__\   sx   € õ �f�cÑ"Ô"ð 	*Ý”\ &Ñ)Ô)ˆFØ!ÐOÐ%OˆÝ�ŠÐ@°,Ð@Ð@ÑAÔAÐAÝÔ-¨lÀ%ÐS\Ð]Ñ]Ô]×`Ò`ÐagÑhÔhˆŒ
ˆ
ˆ
r   FÚaudioÚnum_speakersÚmin_speakersÚmax_speakersÚreturn_embeddingsÚprogress_callbackr1   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
        |
                     d¬¦  «        g d¢¬¦  «        }|d                              d„ ¦  «        |d<   |d                              d„ ¦  «        |d<   |r3‰�1ˆfd„t          |
                     ¦   «         ¦  «        D ¦   «         }||fS |r|dfS |S )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@)rj   ç     ÀX@)ÚsegmentationÚ
embeddingsri   c                 óÎ   •— |�[|�[|dk    rW‰                      | d¦  «        \  }}|t          ||z  d¦  «        ||z
  z  z   }|‰	d         k    r|‰	d<    ‰
|¦  «         d S d S d S d S d S )Nr   )ri   rk   g      ð?)Úgetr9   )Ú	step_nameÚstep_artifactÚfileÚtotalÚ	completedÚoffsetr0   ÚpctÚ_STEP_RANGESÚlast_pctre   s           €€€r   Úhookz*DiarizationPipeline.__call__.<locals>.hook”   sš   ø€ ØÐ$¨Ð)>À5È1Â9À9Ø".×"2Ò"2°9¸kÑ"JÔ"J‘K�F˜CØ ¥3 y°5Ñ'8¸#Ñ#>Ô#>À#ÈÁ,Ñ#OÑO�CØ˜X aœ[Ò(Ð(Ø&)˜ ™Ø)Ð)¨#Ñ.Ô.Ð.Ð.Ð.ð %Ð$Ð)>Ð)>À9À9ð )Ð(r   )ra   rb   rc   ry   g      Y@T)Úyield_label)ÚsegmentÚlabelÚspeaker)Úcolumnsr{   c                 ó   — | j         S ©N)r/   r   s    r   r   z.DiarizationPipeline.__call__.<locals>.<lambda>«   s   € ÀAÄG€ r   r/   c                 ó   — | j         S r€   )r0   r   s    r   r   z.DiarizationPipeline.__call__.<locals>.<lambda>¬   s   € À!Ä%€ r   r0   c                 óL   •— i | ] \  }}|‰|                               ¦   «         “Œ!S r   )Útolist)r   Úsr}   rm   s      €r   ú
<dictcomp>z0DiarizationPipeline.__call__.<locals>.<dictcomp>¯   s/   ø€ Ð!tÐ!tÐ!tÁjÀaÈ '¨:°a¬=×+?Ò+?Ñ+AÔ+AÐ!tÐ!tÐ!tr   )NNN)rW   rO   r   rX   Ú
from_numpyr	   r]   Úspeaker_diarizationÚspeaker_embeddingsÚpdÚ	DataFrameÚ
itertracksÚapplyÚ	enumerateÚlabels)r,   r`   ra   rb   rc   rd   re   Ú
audio_datary   ÚoutputÚdiarizationÚ
diarize_dfrˆ   rw   rm   rx   s         `      @@@r   Ú__call__zDiarizationPipeline.__call__i   sç  øøøø€ õ4 �e�SÑ!Ô!ð 	&Ý˜uÑ%Ô%ˆEåÔ(¨¨t°Q°Q°Q¨w¬Ñ8Ô8Ý&ð
ð 
ˆ
ð
 ˆØÐ(ð
 !,Ø*ðð ˆLð �uˆHð/ð /ð /ð /ð /ð /ð /ð /ð �”Øð
à%Ø%Ø%ð	
ð 
ð
 "&Ð!1�˜ˆ~ˆ~°rð
ð 
ˆð Ð(ØÐ˜eÑ$Ô$Ð$àÔ0ˆØ2CÐM�VÔ.Ð.Èˆ
å”\ +×"8Ò"8ÀTÐ"8Ñ"JÔ"JÐTsÐTsÐTsÐtÑtÔtˆ
Ø(¨Ô3×9Ò9Ð:KÐ:KÑLÔLˆ
�7ÑØ& yÔ1×7Ò7¸¸ÑHÔHˆ
�5Ñàð 	2 Ð!7Ø!tÐ!tÐ!tÐ!tÕT]Ð^i×^pÒ^pÑ^rÔ^rÑTsÔTsÐ!tÑ!tÔ!tÐØÐ1Ð1Ð1ð ð 	Ø˜tÐ#Ð#àÐr   )NNrR   N)NNNFN)rJ   rK   rL   r   r   rO   rX   rS   r.   r%   ÚndarrayÚintÚboolr   Útupler‰   rŠ   ÚdictÚlistrN   r“   r   r   r   rQ   rQ   [   s  € € € € € ð ØØ5:Øðið ið ˜˜s E¤LÐ0Ô1Ô2ð	ið ið ið ið  '+Ø&*Ø&*Ø"'Ø.2ðMð 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ÐRÔ	SðMð Mð Mð Mð Mð Mr   rQ   Fr’   Útranscript_resultrˆ   Úfill_nearestr1   c                 óÞ  — |                      dg ¦  «        }|r| �t          | ¦  «        dk    r|S d„ |                      ¦   «         D ¦   «         }t          |¦  «        }|D �]…}|                      dd¦  «        }|                      dd¦  «        }	|                     ||	¦  «        }
|
rTi }|
D ]!\  }}|                      |d¦  «        |z   ||<   Œ"t          |                     ¦   «         d„ ¬	¦  «        d         |d
<   n&|r$||	z   dz  }|                     |¦  «        }|r||d
<   d|v rÀ|d         D ]·}d|vrŒ|d         }|                      d|¦  «        }|                     ||¦  «        }|rTi }|D ]!\  }}|                      |d¦  «        |z   ||<   Œ"t          |                     ¦   «         d„ ¬	¦  «        d         |d
<   Œ‘|r$||z   dz  }|                     |¦  «        }|r||d
<   Œ¸�Œ‡|�||d<   |S )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
    ÚsegmentsNr   c                 óB   — g | ]\  }}|d          |d         |d         f‘ŒS ©r/   r0   r}   r   )r   Ú_Úrows      r   r   z(assign_word_speakers.<locals>.<listcomp>Ó   s?   € ð ð ð áˆAˆsð 
ˆWŒ�s˜5”z 3 y¤>Ð2ðð ð r   r/   ri   r0   c                 ó   — | d         S ©Nr"   r   r   s    r   r   z&assign_word_speakers.<locals>.<lambda>å   s   € ÈaÐPQÌd€ r   r   r}   r$   Úwordsc                 ó   — | d         S r£   r   r   s    r   r   z&assign_word_speakers.<locals>.<lambda>ü   s   € ÐWXÐYZÔW[€ r   rˆ   )ro   r5   Úiterrowsr   rB   r:   ÚitemsrI   )r’   rš   rˆ   r›   Útranscript_segmentsr   ÚtreeÚsegÚ	seg_startÚseg_endr>   Úspeaker_intersectionsr}   rA   Úseg_midÚnearest_speakerÚwordÚ
word_startÚword_endÚword_overlapsÚword_mids                        r   Úassign_word_speakersrµ   ¹   sš  € ð* ,×/Ò/°
¸BÑ?Ô?ÐØð ! *Ð"4½¸J¹¼È1Ò8LÐ8LØ Ð ðð à ×)Ò)Ñ+Ô+ðñ ô €Iõ ˜	Ñ"Ô"€Dà"ð (:ñ (:ˆØ—G’G˜G SÑ)Ô)ˆ	Ø—'’'˜% Ñ%Ô%ˆð —:’:˜i¨Ñ1Ô1ˆàð 	1à68Ð!Ø)1ð hð hÑ%�˜Ø1F×1JÒ1JÈ7ÐTWÑ1XÔ1XÐ[gÑ1gÐ% gÑ.Ð.Ý Ð!6×!<Ò!<Ñ!>Ô!>ÀNÀNÐSÑSÔSÐTUÔVˆC�	‰NˆNØð 	1à  7Ñ*¨aÑ/ˆGØ"×/Ò/°Ñ8Ô8ˆOØð 1Ø!0��I‘ð �cˆ>ˆ>Ø˜Gœð :ð :�Ø $Ð&Ð&Øà! 'œ]�
ØŸ8š8 E¨:Ñ6Ô6�à $§
¢
¨:°xÑ @Ô @�à ð 	:Ø,.Ð)Ø1>ð pð pÑ-˜ Ø9N×9RÒ9RÐSZÐ\_Ñ9`Ô9`ÐcoÑ9oÐ-¨gÑ6Ð6Ý&)Ð*?×*EÒ*EÑ*GÔ*GÈ^È^Ð&\Ñ&\Ô&\Ð]^Ô&_�D˜‘O�OØ!ð :Ø *¨XÑ 5¸Ñ:�HØ&*×&7Ò&7¸Ñ&AÔ&A�OØ&ð :Ø*9˜˜Y™øùð Ð%Ø2DÐÐ.Ñ/àÐr   c                   ó0   — e Zd Zddededee         fd„ZdS )ÚSegmentNr/   r0   r}   c                 ó0   — || _         || _        || _        d S r€   rŸ   )r,   r/   r0   r}   s       r   r.   zSegment.__init__  s   € ØˆŒ
ØˆŒØˆŒˆˆr   r€   )rJ   rK   rL   r•   r   rO   r.   r   r   r   r·   r·   
  sF   € € € € € ðð ˜Sð  cð °8¸C´=ð ð ð ð ð ð r   r·   )NF)!Únumpyr%   Úpandasr‰   Úpyannote.audior   Útypingr   r   r   r   rX   Úwhisperx.audior   r	   Úwhisperx.schemar
   r   r   Úwhisperx.log_utilsr   rJ   rY   r   rQ   rŠ   r˜   rO   r™   rN   r–   rµ   r·   r   r   r   ú<module>rÀ      sÏ  ðØ Ð Ð Ð Ø Ð Ð Ð Ø #Ð #Ð #Ð #Ð #Ð #Ø /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ø €€€à 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ø ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]Ø )Ð )Ð )Ð )Ð )Ð )à	ˆ�HÑ	Ô	€ðJ*ð J*ð J*ð J*ð J*ñ J*ô J*ð J*ðZ[ð [ð [ð [ð [ñ [ô [ð [ðB <@Øð	Nð NØ”ðNàÐ7Ð9LÐLÔMðNð !  c¨4°¬;Ð&6Ô!7Ô8ðNð ð	Nð
 Ð%Ð':Ð:Ô;ðNð Nð Nð Nðbð ð ð ð ñ ô ð ð ð r   