
    ;3h\              	          d Z ddlZddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZ ddlmZmZmZmZmZmZmZmZ ddlmZmZ ddlZddlZdd	lmZmZmZm Z  dd
l!m"Z" ddl#m$Z$ ddl%m&Z&m'Z'm(Z(m)Z) ddlm*Z*m+Z+ de,de-deee,e,f   e,f   fdZ.dee/e,f   de/fdZ0de/fdZ1de,de-de-fdZ2de,de-fdZ3de,de-de-fdZ4dee/e,f   de-dee,ddf   fdZ5de"dee/e,f   de/fd Z6de/fd!Z7d"e/d#e/d$e/de/fd%Z8d&e"de-fd'Z9 G d( d)      Z:y)*zCommunicate with the service. Only the Communicate class should be used by
end-users. The other classes and functions are for internal use only.    N)nullcontext)TextIOWrapper)Queue)AsyncGeneratorContextManagerDict	GeneratorListOptionalTupleUnion)escapeunescape   )DEFAULT_VOICESEC_MS_GEC_VERSIONWSS_HEADERSWSS_URL)	TTSConfig)DRM)NoAudioReceivedUnexpectedResponseUnknownResponseWebSocketError)CommunicateStateTTSChunkdataheader_lengthreturnc                     t        | t              st        d      i }| d| j                  d      D ]  }|j                  dd      \  }}|||<    || |dz   d fS )z
    Returns the headers and data from the given data.

    Args:
        data (bytes): The data to be parsed.
        header_length (int): The length of the header.

    Returns:
        tuple: The headers and data to be used in the request.
    zdata must be bytesNs   
   :r      )
isinstancebytes	TypeErrorsplit)r   r   headerslinekeyvalues         T/var/www/html/audio-gradio/venv/lib/python3.12/site-packages/edge_tts/communicate.pyget_headers_and_datar,   (   sw     dE",--G^m$**73 ZZa(
U D*,---    stringc                 J   t        | t              r| j                  d      } t        | t              st	        d      t        |       }t        |      D ]>  \  }}t        |      }d|cxk  rdk  sn d|cxk  rdk  sn d|cxk  rdk  s7n :d	||<   @ d
j                  |      S )aS  
    The service does not support a couple character ranges.
    Most important being the vertical tab character which is
    commonly present in OCR-ed PDFs. Not doing this will
    result in an error from the service.

    Args:
        string (str or bytes): The string to be cleaned.

    Returns:
        str: The cleaned string.
    utf-8zstring must be str or bytesr                    )	r#   r$   decodestrr%   list	enumerateordjoin)r.   charsidxcharcodes        r+   remove_incompatible_charactersrB   @   s     &% w'fc"566F|Eu% 	TINNd 0b 0bD6FB6FE#J
 775>r-   c                  \    t        t        j                               j                  dd      S )zZ
    Returns a UUID without dashes.

    Returns:
        str: A UUID without dashes.
    -r7   )r9   uuiduuid4replace r-   r+   
connect_idrI   \   s"     tzz|$$S"--r-   textlimitc                 \    | j                  dd|      }|dk  r| j                  dd|      }|S )a  
    Finds the index of the rightmost preferred split character (newline or space)
    within the initial `limit` bytes of the text.

    This helps find a natural word or sentence boundary for splitting, prioritizing
    newlines over spaces.

    Args:
        text (bytes): The byte string to search within.
        limit (int): The maximum index (exclusive) to search up to.

    Returns:
        int: The index of the last found newline or space within the limit,
             or -1 if neither is found in that range.
       
r       )rfind)rJ   rK   split_ats      r+   (_find_last_newline_or_space_within_limitrQ   f   s4    " zz%E*H!|::dAu-Or-   text_segmentc                     t        |       }|dkD  r	 | d| j                  d       |S |S # t        $ r |dz  }Y nw xY w|dkD  r3)a  
    Finds the rightmost possible byte index such that the
    segment `text_segment[:index]` is a valid UTF-8 sequence.

    This prevents splitting in the middle of a multi-byte UTF-8 character.

    Args:
        text_segment (bytes): The byte segment being considered for splitting.

    Returns:
        int: The index of the safe split point. Returns 0 if no valid split
             point is found (e.g., if the first byte is part of a multi-byte
             sequence longer than the limit allows).
    r   Nr0   r   )lenr8   UnicodeDecodeError)rR   rP   s     r+   _find_safe_utf8_split_pointrV   ~   s_     < H
Q,	(#**73O
 O	 " 	MH	 Q,s   * ;;rP   c                     |dkD  rBd| d| v r;| j                  dd|      }| j                  d||      dk7  r	 |S |}|dkD  rd| d| v r;|S )a  
    Adjusts a proposed split point backward to prevent splitting inside an XML entity.

    For example, if `text` is `b"this &amp; that"` and `split_at` falls between
    `&` and `;`, this function moves `split_at` to the index before `&`.

    Args:
        text (bytes): The text segment being considered.
        split_at (int): The proposed split point index, determined by whitespace
                        or UTF-8 safety.

    Returns:
        int: The adjusted split point index. It will be moved to the '&'
             if an unterminated entity is detected right before the original `split_at`.
             Otherwise, the original `split_at` is returned.
    r      &N   ;)rindexfind)rJ   rP   ampersand_indexs      r+   "_adjust_split_point_for_xml_entityr^      sm    " Q,44	?2++dAx899T?H5;
 O # Q,44	?2 Or-   byte_lengthc              #     K   t        | t              r| j                  d      } t        | t              st	        d      |dk  rt        d      t        |       |kD  rlt        | |      }|dk  rt        |       }t        | |      }|dk  rt        d      | d| j                         }|r| | |dkD  r|ndd } t        |       |kD  rl| j                         }|r| yyw)a  
    Splits text into chunks, each not exceeding a maximum byte length.

    This function prioritizes splitting at natural boundaries (newlines, spaces)
    while ensuring that:
    1. No chunk exceeds `byte_length` bytes.
    2. Chunks do not end with an incomplete UTF-8 multi-byte character.
    3. Chunks do not split XML entities (like `&amp;`) in the middle.

    Args:
        text (str or bytes): The input text. If str, it's encoded to UTF-8.
        byte_length (int): The maximum allowed byte length for any yielded chunk.
                           Must be positive.

    Yields:
        bytes: Text chunks (UTF-8 encoded, stripped of leading/trailing whitespace)
               that conform to the byte length and integrity constraints.

    Raises:
        TypeError: If `text` is not str or bytes.
        ValueError: If `byte_length` is not positive, or if a split point
                    cannot be determined (e.g., due to extremely small byte_length
                    relative to character/entity sizes).
    r0   ztext must be str or bytesr   z"byte_length must be greater than 0zTMaximum byte length is too small or invalid text structure near '&' or invalid UTF-8Nr   )r#   r9   encoder$   r%   
ValueErrorrT   rQ   rV   r^   strip)rJ   r_   rP   chunkremaining_chunks        r+   split_text_by_byte_lengthrf      s     6 ${{7#dE"344a=>>
d)k
!;D+Na<248H 6dHEa< C  Yh%%'K 1H!565 d)k
!: jjlO s   CC!	C!tcescaped_textc                     t        |t              r|j                  d      }d| j                   d| j                   d| j
                   d| j                   d| dS )z
    Creates a SSML string from the given parameters.

    Args:
        tc (TTSConfig): The TTS configuration.
        escaped_text (str or bytes): The escaped text. If bytes, it must be UTF-8 encoded.

    Returns:
        str: The SSML string.
    r0   z_<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-US'><voice name='z'><prosody pitch='z' rate='z
' volume='z'>z</prosody></voice></speak>)r#   r$   r8   voicepitchratevolume)rg   rh   s     r+   mkssmlrn      sd     ,&#**73	z "88*HRWWIZ		{".			r-   c                  R    t        j                  dt        j                               S )zg
    Return Javascript-style date string.

    Returns:
        str: Javascript-style date string.
    z:%a %b %d %Y %H:%M:%S GMT+0000 (Coordinated Universal Time))timestrftimegmtimerH   r-   r+   date_to_stringrs     s      ==Ddkkm r-   
request_id	timestampssmlc                     d|  d| d| S )z
    Returns the headers and data to be used in the request.

    Returns:
        str: The headers and data to be used in the request.
    zX-RequestId:z1
Content-Type:application/ssml+xml
X-Timestamp:zZ
Path:ssml

rH   )rt   ru   rv   s      r+   ssml_headers_plus_datarx   &  s)     zl # k "&		r-   
tts_configc           
      v    d}t        t        t               t               t	        | d                  dz   }||z
  S )zCalculates the maximum message size for the given voice, rate, and volume.

    Returns:
        int: The maximum message size.
    i   r7   2   )rT   rx   rI   rs   rn   )ry   websocket_max_sizeoverhead_per_messages      r+   calc_max_mesg_sizer~   7  sL     $" z2&	
 	   444r-   c                   6   e Zd ZdZefddddddddded	ed
edededeej                     dee   dee	   dee	   fdZ
dedefdZdeedf   fdZdeedf   fdZ	 ddeeef   deeeef      ddfdZdeeddf   fdZ	 ddeeef   deeeef      ddfdZy)Communicatez'
    Communicate with the service.
    z+0%z+0HzN
   <   )rl   rm   rk   	connectorproxyconnect_timeoutreceive_timeoutrJ   rj   rl   rm   rk   r   r   r   r   c                0   t        ||||      | _        t        |t              st	        d      t        t        t        |            t        | j                              | _	        |t        |t              st	        d      || _
        t        |t              st	        d      t        |	t              st	        d      t        j                  d d ||	      | _        |%t        |t        j                        st	        d      || _        dddd	d
| _        y )Nztext must be strzproxy must be strzconnect_timeout must be intzreceive_timeout must be int)totalconnectsock_connect	sock_readz'connector must be aiohttp.BaseConnectorr-   r   F)partial_textoffset_compensationlast_duration_offsetstream_was_called)r   ry   r#   r9   r%   rf   r   rB   r~   textsr   intaiohttpClientTimeoutsession_timeoutBaseConnectorr   state)
selfrJ   rj   rl   rm   rk   r   r   r   r   s
             r+   __init__zCommunicate.__init__P  s    $E4? $$.// /1$78t/

 Zs%;/00$)
 /3/9::/3/9::&44(%	 
  Iw?T?T)UEFF:C  #$$%!&	(

r-   r   r   c                     t        j                  |      d   D ]Y  }|d   }|dk(  r;|d   d   | j                  d   z   }|d   d   }|||t        |d   d   d	         d
c S |dv rMt	        d|        t        d      )NMetadataTypeWordBoundaryDataOffsetr   DurationrJ   Text)typeoffsetdurationrJ   )
SessionEndzUnknown metadata type: zNo WordBoundary metadata found)jsonloadsr   r   r   r   )r   r   meta_obj	meta_typecurrent_offsetcurrent_durations         r+   __parse_metadatazCommunicate.__parse_metadata  s    

4(4 	IH (IN*V$X.<Q1RR  $,F#3J#? %, 0$Xf%5f%=f%EF	  O+!$;I;"GHH	I  !!ABBr-   c                   K   d(fd}d( fd}d}t        j                  t        j                               }t	        j
                   j                  d j                        4 d {   }|j                  t         dt        j                          dt         d	t                d
 j                  t        |      4 d {    |        d {     |        d {    2 3 d {   }|j                   t        j"                  j$                  k(  r|j&                  j)                  d      }t+        ||j-                  d            \  }}	|j/                  dd       }
|
dk(  r/ j1                  |	      }| |d   |d   z    j2                  d<   |
dk(  r6 j2                  d    j2                  d<    j2                  dxx   dz  cc<    n|
dvst5        d      |j                   t        j"                  j6                  k(  rt9        |j&                        dk  rt;        d      t<        j?                  |j&                  d d d      }|t9        |j&                        kD  rt;        d      t+        |j&                  |      \  }}	|j/                  d      dk7  rt;        d      |j/                  dd       }|dvrt;        d       |t9        |	      d!k(  rt;        d"      t9        |	      d!k(  rt;        d#      d}d$|	d% &|j                   t        j"                  j@                  k(  sOtC        |j&                  r|j&                        d&      |stE        d'      d d d       d {    d d d       d {    y 7 #7 7 7 7 6 ?7 &# 1 d {  7  sw Y   6xY w7 -# 1 d {  7  sw Y   y xY ww))Nc                  \   K    j                  dt                d       d{    y7 w)z)Sends the command request to the service.zX-Timestamp:z
Content-Type:application/json; charset=utf-8
Path:speech.config

{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":"false","wordBoundaryEnabled":"true"},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}
N)send_strrs   )	websockets   r+   send_command_requestz2Communicate.__stream.<locals>.send_command_request  s4     $$~/0 1   s   !,*,c                     K   j                  t        t               t               t	         j
                   j                  d                      d{    y7 w)z&Sends the SSML request to the service.r   N)r   rx   rI   rs   rn   ry   r   )r   r   s   r+   send_ssml_requestz/Communicate.__stream.<locals>.send_ssml_request  sM     $$&L"$

>2	 	 	s   AAAAF)cafileT)r   	trust_envtimeoutz&Sec-MS-GEC=z&Sec-MS-GEC-Version=z&ConnectionId=   )compressr   r'   sslr0   s   

s   Paths   audio.metadatar   r   r   s   turn.endr   i )s   responses
   turn.startzUnknown path receivedr"   zBWe received a binary message, but it is missing the header length.bigz9The header length is greater than the length of the data.s   audioz3Received binary message, but the path is not audio.s   Content-Type)s
   audio/mpegNz=Received binary message, but with an unexpected Content-Type.r   z<Received binary message with no Content-Type, but with data.z:Received binary message, but it is missing the audio data.audio)r   r   zUnknown errorzFNo audio was received. Please verify that your parameters are correct.r   N)#r   create_default_contextcertifiwherer   ClientSessionr   r   
ws_connectr   r   generate_sec_ms_gecr   rI   r   r   r   	WSMsgTypeTEXTr   ra   r,   r\   get_Communicate__parse_metadatar   r   BINARYrT   r   r   
from_bytesERRORr   r   )r   r   r   audio_was_receivedssl_ctxsessionreceivedencoded_data
parametersr   pathparsed_metadatar   content_typer   s   `             @r+   __streamzCommunicate.__stream  s    
		  # ,,GMMOD((nn((
 p	 p	 g((i|C$;$;$=#>"#5"6Z\N, ** ) 
	p	 p	 &(((#%%%"+ Z Zh==G$5$5$:$::*2--*>*>w*GL';$l&7&7&D($J &>>'48D00*.*?*?*E-- ,H5
8SS 

#9: ,<@JJ2=

#89 

#89YF9 %AA-.EFF]]g&7&7&>&>>8==)A-0` 
 %(NN8==!3De$LM$s8=='990W 
 (< }($J
 "~~g.(:0Q  $.>>/4#HL#+@@0[ 
 $+t9>$ 1Z 
 4yA~0X 
 *.&#*D99]]g&7&7&=&==()1 <K  &%\ ]p	 p	 p	 p	 p	 p	 )%Z)#p	 p	 p	 p	 p	 p	 p	 p	s   A(O,N-O0AN9NN9
N"NN"$N%N"+N/N
0N3C&N"EN":4N".N99N :N9>O	N7
ON9N"N"NN" N9"N4	(N+)N4	0N97O9O?O OOc                  K   | j                   d   rt        d      d| j                   d<   | j                  D ].  | j                   d<   	 | j                         2 3 d{   }|  y7 6 6# t        j
                  $ rP}|j                  dk7  r t        j                  |       | j                         2 3 d{  7  }| 6 Y d}~d}~ww xY ww)au  
        Streams audio and metadata from the service.

        Raises:
            NoAudioReceived: If no audio is received from the service.
            UnexpectedResponse: If the response from the service is unexpected.
            UnknownResponse: If the response from the service is unknown.
            WebSocketError: If there is an error with the websocket.
        r   zstream can only be called once.Tr   Ni  )	r   RuntimeErrorr   _Communicate__streamr   ClientResponseErrorstatusr   handle_client_response_error)r   messagees      r+   streamzCommunicate.stream.  s      ::)*@AA*.

&' +/** 
	"DJJ~&	"%)]]_ " "'!M
	""_.. "88s?003%)]]_ " "'!M &5"sr   AC	A.A,A*A,!A.(C*A,,A.-C.C5C6C:B=
;C?CCCCaudio_fnamemetadata_fnamec                   K   |t        |dd      n	t               }|5  t        |d      5 }| j                         2 3 d{   }|d   dk(  r|j                  |d          &t	        |t
              s7|d   d	k(  s@t        j                  ||       |j                  d
       h7 c6 	 ddd       n# 1 sw Y   nxY wddd       y# 1 sw Y   yxY ww)zE
        Save the audio and metadata to the specified files.
        Nwr0   )encodingwbr   r   r   r   
)openr   r   writer#   r   r   dump)r   r   r   metadatar   r   s         r+   savezCommunicate.saveM  s      ) w7 	
  		)tK. 		)%!% ) )g6?g-KK0x7>9IIgx0NN4()		) 		) 		) 		) 		) 		)sb   CCB2B'B%
B'.B24B2=(B2%B''B2)	C2B;	7C>	CCCc              #       K   dt         ddf fd}t               }t        j                  j                         5 }|j	                  ||       	 |j                         }|n| 	 ddd       y# 1 sw Y   yxY ww)z-Synchronous interface for async stream methodqueuer   Nc                      d fd}t        j                         }t        j                  |       |j                   |              |j	                          y )Nc                     K   j                         2 3 d {   } j                  |        7 6 j                  d        y wN)r   put)itemr   r   s    r+   	get_itemszECommunicate.stream_sync.<locals>.fetch_async_items.<locals>.get_itemsi  s:     "&++- $ $$IIdO$-		$s   A1/1A1Ar   )asyncionew_event_loopset_event_looprun_until_completeclose)r   r   loopr   s   `  r+   fetch_async_itemsz2Communicate.stream_sync.<locals>.fetch_async_itemsh  s?     
 ))+D""4(##IK0JJLr-   )r   
concurrentfuturesThreadPoolExecutorsubmitr   )r   r   r   executorr   s   `    r+   stream_synczCommunicate.stream_synce  s     		U 		t 		 w224 	OO-u5yy{<
	  	 	 	s   7A<,A0'	A<0A95A<c                     t         j                  j                         5 }|j                  t        j
                  | j                  ||            }|j                          ddd       y# 1 sw Y   yxY w)z,Synchronous interface for async save method.N)r   r   r   r   r   runr   result)r   r   r   r   futures        r+   	save_synczCommunicate.save_sync~  sZ     224 	__TYY{NCF MMO		 	 	s   AA))A2r   )__name__
__module____qualname____doc__r   r9   r   r   r   r   r   r$   r   r   r   r   r   r   r   r	   r   r  rH   r-   r+   r   r   K  st    #6

 59#)+)+6
6
 6

 6
 6
 6
 G1126
 }6
 "#6
 "#6
pCU Cx C&Qx~ > Qf"	$	'"D 7;)3:&) !sEz!23) 
	)0Yxt';< 8 7;
3:&
 !sEz!23
 
	
r-   r   );r  r   concurrent.futuresr   r   r   rp   rE   
contextlibr   ior   r   r   typingr   r   r   r	   r
   r   r   r   xml.sax.saxutilsr   r   r   r   	constantsr   r   r   r   data_classesr   drmr   
exceptionsr   r   r   r   r   r   r$   r   r,   r9   rB   rI   rQ   rV   r^   rf   rn   rs   rx   r~   r   rH   r-   r+   <module>r     s  I    
   "  	 	 	 .   N N #   /.
. #.
4uu$%.05e+<  8.C .5   0e  8U c c <B
U

B*-BudD !BJy c5j(9 c 2  s s # # "59 5 5(} }r-   