+
    Yi{                    2   ^ RI Ht ^ RIt^ RIHt ^ RIHtHt ^ RIt^ RI	H
t
Ht ^ RIHt ^ RIHtHt ^ RIHt ^ R	IHt ^ R
IHt R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R lt R R  lt!R! R" lt"]! R#.4      ]! ].4      R$ 4       4       t#]! R%.4      ]! ].4      R& 4       4       t$]! R#.4      ]! ].4      R' R( l4       4       t%]! R#.4      ]! ].4      R) 4       4       t&]! R#.4      ]! ].4      R* 4       4       t'R# )+    )annotationsN)Any)	urlencodeurlparse)connectionsDatabaseError)HttpResponse)api_viewpermission_classes)IsAuthenticated)Response)_require_master_adminc                   V ^8  d   QhRR/# )   returndict )formats   "GE:\live-kit-agent\livekit_voicebot\backend\config\master_voice_views.py__annotate__r      s              c                *    \        \        W4      4      # )N)r   zip)rowcolss   &&r   _row_to_voicer      s    Dr   c               $    V ^8  d   QhRRRRRR/# )r   labelsr   
candidatesstrr   r   )r   s   "r   r   r      s!       3 3 r   c                   \        V \        4      '       g   R# / pV P                  4        FF  w  r4Vf   K  \        V4      P	                  4       P                  4       P                  RR4      pWBV&   KH  	  V F  pVP	                  4       P                  4       P                  RR4      pVP                  V4      pVf   KH  \        V4      P	                  4       '       g   Ki  \        V4      P	                  4       u # 	  R# )z
ElevenLabs `labels` is a free-form string map; keys vary by casing/spacing
(e.g. "use case" vs "use_case"). Match case-insensitively.
  _)
isinstancer   itemsr    striplowerreplaceget)r   r   normkvalnkcandvs   &*      r   _label_valuer1      s    
 fd##D,,.9V\\^!!#++C5R	 !
 ZZ\!))#s3HHRL=SV\\^^q6<<>!	 
 r   c                    V ^8  d   QhRRRR/# )r   rawr   r   tuple[str, str]r   )r   s   "r   r   r   *   s      S _ r   c                   V e   \        V 4      P                  4       MR;'       g    RpV'       g   R# RV9   dB   VP                  R^4      w  r#VP                  4       P                  4       pV'       d   W!3# RV3# RV9   dR   VP                  R^4      w  r#VP                  4       P                  4       pV'       d   W!P	                  RR4      3# RV3# VP                  4       R3# )z
Cartesia `language` is either a bare code (`en`) or a locale pair (`en_GB`, `en-GB`).
Store the base code in `language` and the full locale token in `locale` when present.
r"   r$   -)r"   r"   )r    r'   splitr(   r)   )r3   slang_rests   &   r   _split_cartesia_lang_localer;   *   s    
 !_S	";;A
axggc1ozz|!!# y-r1g-
axggc1ozz|!!#.2iiS)*?Q?779b=r   c                    V ^8  d   QhRRRR/# )r   voicedict[str, Any]r   r   )r   s   "r   r   r   =   s     4 4^ 4 4r   c                   V P                  R4      p\        V\        4      '       d	   V'       g   / # V Fh  p\        V\        4      '       g   K  \	        VP                  R4      ;'       g    R4      P                  4       pVP                  R4      '       g   Kf  Vu # 	  V^ ,          p\        V\        4      '       d   V# / # )zGPrefer an English verified_language row; otherwise use the first entry.verified_languageslanguager"   en)r*   r%   listr   r    r(   
startswith)r=   r3   entryr9   firsts   &    r   _elevenlabs_verified_pickrG   =   s    
))(
)Cc4  	%&&599Z(..B/557??4  L  FEud++533r   c                    V ^8  d   QhRRRR/# r   r=   r>   r   r    r   )r   s   "r   r   r   L   s      > c r   c                H   V P                  R4      p\        V\        4      '       g   R# V Fr  p\        V\        4      '       g   K  \	        VP                  R4      ;'       g    VP                  R4      ;'       g    R4      P                  4       pV'       g   Kp  Vu # 	  R# )uP   Scan all verified_language rows — preview URLs are often only on some locales.r@   r"   preview_url
previewUrlr*   r%   rC   r   r    r'   )r=   r3   rE   us   &   r   '_elevenlabs_first_preview_from_verifiedrO   L   s    
))(
)Cc4  %&&		-(IIEIIl,CIIrJPPR1H  r   c                    V ^8  d   QhRRRR/# )r   r=   r>   r   r   r   )r   s   "r   r   r   Z   s      > c r   c                   V P                  R4      p\        V\        4      '       d   RpRpV F  p\        V\        4      '       g   K  VP                  R4      ;'       g    VP                  R4      pVe   \	        V4      P                  4       R8X  d   Ki  Vf   Tp\	        VP                  R4      ;'       g    R4      P                  4       pVP                  R4      '       g   K  TpK  	  Ve   V# Ve   V# V P                  R4      p\        V\        4      '       d7   V'       d/   V^ ,          pVe"   \	        V4      P                  4       '       d   V# R# )	zYPick model_id from verified_languages (prefer English), else high_quality_base_model_ids.r@   Nmodel_idmodelIdr"   rA   rB   high_quality_base_model_ids)r*   r%   rC   r   r    r'   r(   rD   )	r=   r3   	preferredfallbackrE   midr9   hqrF   s	   &        r   _elevenlabs_model_id_from_voicerY   Z   s   
))(
)C#t	EeT**))J'??599Y+?C{c#hnn."4uyy,22399;Dt$$	   O	0	1B"d1U!1!1!3!3Lr   c                    V ^8  d   QhRRRR/# rI   r   )r   s   "r   r   r   w   s      N s r   c                   V P                  R4      p\        V\        4      '       g   R# V F  p\        V\        4      '       g   K  VP                  R4      ;'       g]    VP                  R4      ;'       gD    VP                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      pVf   K  \	        V4      P                  4       '       g   K  \	        V4      P                  4       u # 	  R# )zLSome list payloads expose sample audio URLs even when preview_url is absent.samplesr"   rK   rL   url	audio_url
sample_urlrM   )r=   r\   r8   rN   s   &   r    _elevenlabs_preview_from_samplesr`   w   s    ii	"Ggt$$!T""EE-  # #uu\"# #uuU|# # uu[!# # uu\" 	
 =SV\\^^q6<<>!  r   c               $    V ^8  d   QhRRRRRR/# )r   rA   r    localer   r4   r   )r   s   "r   r   r      s!       S _ r   c                H   T ;'       g    RP                  4       p T;'       g    RP                  4       pV'       d   W3# RV 9   d[   V P                  R4      ^8X  dE   RV 9  d>   V P                  R^4      w  r#VP                  4       P                  4       pV'       d   W 3# W3# )z
If ElevenLabs returns a BCP-47 tag in `language` (e.g. en-US) but `locale` is empty,
split into base language + full tag for the locale column.
r"   r6   r$   )r'   countr7   r(   )rA   rb   base_regions   &&  r   _split_bcp47_language_tagrg      s    
 B%%'Hll!!#F
h8>>#.!388K sA.zz|!!#>!r   c                    V ^8  d   QhRRRR/# )r   r0   r>   r   r    r   )r   s   "r   r   r      s      ^  r   c                   R FT  pV P                  V4      pVf   K  \        V4      P                  4       '       g   K;  \        V4      P                  4       u # 	  V P                  R4      p\        V\        4      '       d[   R FT  pVP                  V4      pVf   K  \        V4      P                  4       '       g   K;  \        V4      P                  4       u # 	  R# )preview_file_urlpreviewr"   )rj   rK   rL   )file_urlr]   rj   rK   )r*   r    r'   r%   r   )r0   keyr-   prevs   &   r   _cartesia_preview_urlro      s    @eeCj?s3x~~//s8>>## A 55D$IC((3-C3s8>>#3#33x~~'' J r   c                   V ^8  d   QhRR/# )r   r   r    r   )r   s   "r   r   r      s     "= "=c "=r   c                 d   \         P                  ! R4      ;'       g    RP                  4       P                  4       p V R	9   d   V # R R lpR R lpV! R4      pV! R4      pV'       d   V'       g   R# V'       d   V'       g   R# V'       g   V'       g   R# V! R4      pV! R4      pWe8  d   R# R# )
z
Voice tables may live in either DB depending on environment.
Prefer the DB that actually has `voice_plan` and the larger row count.
VOICE_PLAN_DBr"   defaultclusterc                    V ^8  d   QhRRRR/# )r   aliasr    r   boolr   )r   s   "r   r   *_voice_plan_db_alias.<locals>.__annotate__   s     . .S .T .r   c                    \         V ,          P                  4       ;_uu_ 4       pVP                  R 4       VP                  4       RJuuRRR4       #   + '       g   i     R# ; i)zSHOW TABLES LIKE 'voice_plan'N)r   cursorexecutefetchonerv   curs   & r   _table_exists+_voice_plan_db_alias.<locals>._table_exists   sB    &&((CKK78<<>- )(((s   #AA#	c                    V ^8  d   QhRRRR/# )r   rv   r    r   intr   )r   s   "r   r   rx      s      c c r   c                $    \         V ,          P                  4       ;_uu_ 4       pVP                  R 4       \        VP	                  4       ^ ,          ;'       g    ^ 4      uuRRR4       #   + '       g   i     R# ; i  \
         d    Ru # i ; i)zSELECT COUNT(*) FROM voice_planN)r   rz   r{   r   r|   	Exceptionr}   s   & r   _count$_voice_plan_db_alias.<locals>._count   si    	U#**,,=>3<<>!,112 -,,,  	I	s.   #A? ;A+ 
A? +A<	6A? <A? ?BB>   rt   rs   )osgetenvr'   r(   )rU   r   r   has_defaulthas_cluster	c_default	c_clusters          r   _voice_plan_db_aliasr      s    
 ?+11r88:@@BI**.
  	*K	*K;;{y!Iy!I!-9<9<r   c                   V ^8  d   QhRR/# r   r   Noner   )r   s   "r   r   r      s     K K4 Kr   c                    \        4       p \        V ,          P                  4       ;_uu_ 4       pVP                  R4       VP	                  4       pV'       g    RRR4       R# \        V^ ,          ;'       g    R4      P                  4       pVR9   d    RRR4       R# VP                  R4       RRR4       R#   + '       g   i     R# ; i)z
The existing DB in some environments defines `voice_plan.voice_id` as a numeric type.
Provider voice IDs (ElevenLabs/Cartesia) are strings, so we must ensure it is VARCHAR.
z
            SELECT DATA_TYPE
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE()
              AND TABLE_NAME = 'voice_plan'
              AND COLUMN_NAME = 'voice_id'
            Nr"   z3ALTER TABLE voice_plan MODIFY voice_id VARCHAR(255)>   chartextvarcharlongtext
mediumtext)r   r   rz   r{   r|   r    r(   )rv   r~   r   	data_types       r   _ensure_voice_plan_schemar      s    
 !"E	U		"	"	$	$	
 lln 
%	$ A"%++-	MM 
%	$" 	IJ# 
%	$	$	$s   +B:#B:8B:B::C	c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r      s     +G +GD +Gr   c            	     (   \        4       p \        V ,          P                  4       ;_uu_ 4       pVP                  R4       VP	                  4       pV'       dC   \        V^ ,          ;'       g    R4      P                  4       pVR9  d   VP                  R4       VP                  R4       VP                  4       ;'       g    .  Uu0 uF-  p\        V^ ,          ;'       g    R4      P                  4       kK/  	  pp. pRV9  d   VP                  R4       RV9  d   VP                  R4       R	V9  d   VP                  R
4       V'       d$   VP                  RRP                  V4       24       RRR4       R# u upi   + '       g   i     R# ; i)z
Ensure `voice_plan` can store provider voice metadata we want to expose in the UI.
Older environments may be missing these columns and/or have `model_id` as INT.
z
            SELECT DATA_TYPE
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE()
              AND TABLE_NAME = 'voice_plan'
              AND COLUMN_NAME = 'model_id'
            r"   z8ALTER TABLE voice_plan MODIFY model_id VARCHAR(255) NULLz
            SELECT COLUMN_NAME
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE()
              AND TABLE_NAME = 'voice_plan'
            rA   z$ADD COLUMN language VARCHAR(64) NULLrb   z"ADD COLUMN locale VARCHAR(64) NULLrK   z ADD COLUMN preview_url TEXT NULLzALTER TABLE voice_plan z, N>   r   r   r   r   r   )
r   r   rz   r{   r|   r    r(   fetchallappendjoin)rv   r~   r   r   rexistingto_adds          r   _ensure_voice_plan_columnsr      sI   
 !"E	U		"	"	$	$	
 llnCFLLb)//1I UUVW 		
 7:lln6J6J6JL6JC!

O))+6JLX%MM@A8#MM>?(MM<=KK1$))F2C1DEFK 
%	$4 M5 
%	$	$s7   >F -AF F E;!E;:AF #F ;F  F	GETc                   \        V 4      w  rV'       d   V# V P                  pVP                  R 4      ;'       g    RP                  4       pVP                  R4      ;'       g    RP                  4       pVP                  R4      ;'       g    RP                  4       pVP                  R4      ;'       g    RP                  4       pVP                  R4      ;'       g    RP                  4       p\	        ^\        ^\        VP                  R4      ;'       g    ^24      4      4      p	\	        ^\        VP                  R4      ;'       g    ^4      4      p
. p. pV'       d%   VP                  R4       VR	V R	2R	V R	2.,          pV'       d#   VP                  R
4       VP                  V4       V'       d#   VP                  R4       VP                  V4       V'       d#   VP                  R4       VP                  V4       V'       d#   VP                  R4       VP                  V4       V'       d   RRP                  V4      ,           MRpRpVP                  R4      ;'       g    RP                  4       R8X  d   RMRp \        4        \        4        \        4       p\        V,          P                  4       ;_uu_ 4       pVP                  RV 2V4       VP!                  4       ^ ,          pV
^,
          V	,          pVP                  RV RV RV RV R2	WV.,           4       VP"                   Uu. uF  pV^ ,          NK  	  ppVP%                  4        Uu. uF  p\'        VV4      NK  	  ppRRR4       \+        RR!R"XR#RT
RT	R$XR%T'       d   TT	,           ^,
          T	,          //4      # ^ //4      # u upi u upi   + '       g   i     LX; i  \(         d%   p\+        RRR\-        T4      /RR 7      u Rp?# Rp?ii ; i)&searchr"   plancategoryrR   genderper_pagepagez"(name LIKE %s OR voice_id LIKE %s)%z
plans = %szcategory = %szmodel_id = %szgender = %szWHERE z AND name
sort_orderascASCDESCz0SELECT COUNT(DISTINCT voice_id) FROM voice_plan a  
                SELECT voice_id, name, category, model_id, plans, gender, accent, age, use_case, description, language, locale, preview_url
                FROM (
                    SELECT voice_id, name, category, model_id, plans, gender, accent, age, use_case, description, language, locale, preview_url,
                           ROW_NUMBER() OVER (
                               PARTITION BY voice_id
                               ORDER BY
                                   (preview_url IS NOT NULL AND preview_url <> '') DESC,
                                   (locale IS NOT NULL AND locale <> '') DESC,
                                   (language IS NOT NULL AND language <> '') DESC,
                                   (accent IS NOT NULL AND accent <> '') DESC,
                                   (age IS NOT NULL AND age <> '') DESC,
                                   (use_case IS NOT NULL AND use_case <> '') DESC,
                                   name z,
                                   plans ASC,
                                   category ASC,
                                   model_id ASC
                           ) AS rn
                    FROM voice_plan vp
                    zW
                ) ranked
                WHERE ranked.rn = 1
                ORDER BY r#   z4
                LIMIT %s OFFSET %s
                NsuccessFmessage  statusTdata
paginationtotaltotal_pages)r   query_paramsr*   r'   maxminr   r   r   r(   r   r   r   r   rz   r{   r|   descriptionr   r   r   r   r    )requestr$   errqr   r   r   rR   r   r   r   
conditionsparamswheresort_colsort_dirrv   r~   r   offsetcr   r   rowses   &                        r   master_voice_plansr     s    #7+FA

AeeHo##**,FEE&MR&&(Dj!''R..0Hj!''R..0HeeHo##**,F1c#s155#4#:#:;<=Hq#aeeFm((q)*DJF>?Qvha=AfXQ-00,'d/*h/*h-(f5?XZ00REH|,55<<>%GuVH*K!#"$$&&&((CKKJ5'RTZ[LLN1%EQh(*FKK) *2
 3 G  #1XJ /-2 F++58 #&//2/QAaDD/D247LLNCNqM!T*NDCI )P 4DUEH,q0X=	
	 	 	 MN	
	 	 	 3CI )(J  KE9c!f=cJJKs\   AO A2O9N<OO4O6O <
OO	O O P$P=PPPOSTc                  a	a
aaaaaaa \        V 4      w  rV'       d   V# \        P                  ! R 4      ;'       g    RP                  4       o\        P                  ! R4      ;'       g    RP                  4       oR R lpR V3R llo
R V3R llo	\	        V P
                  \        4      '       d   V P
                  P                  R	4      MR
;'       g    Ro\        S4      P                  4       P                  4       o\        S4      o\        S4      o. oRRSR^ R^ R^ /RRSR^ R^ R^ //p R^ R^ /oR V	V
VVVVV3R llp^ R
IpVP                  V! 4       4       S'       g   \        RRRR^ R^ R^ RRRVRS//4      #  V! S4      pSR"9   d   TS,          P                  T4       MYtR&   \        RRR/ TCRRRTRS/C/4      #   \         d,   p\        RRRR\        T4       2RRT//RR7      u R
p?# R
p?ii ; i  \         P"                  \$        \&        3 d,   p\        RRRR \        T4       2RRT//R!R7      u R
p?# R
p?ii ; i)#ELEVENLABS_API_KEYr"   CARTESIA_API_KEYc                    V ^8  d   QhRRRR/# )r   r&   list[dict[str, Any]]r   zdict[str, int]r   )r   s   "r   r   'master_voice_sync.<locals>.__annotate__  s#     AQ AQ#7 AQN AQr   c                :   ^ p^ p\        4        \        4        \        4       p\        V,          P	                  4       ;_uu_ 4       pV  EF&  pVP                  R4      ;'       g    RP                  4       pV'       g   K7  VP                  R4      ;'       g    RP                  4       ;'       g    TpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      e)   \        VP                  R4      4      P                  4       MR;'       g    Rp	VP                  R4      ;'       g    RP                  4       ;'       g    Rp
VP                  R	4      ;'       g    RP                  4       ;'       g    RpVP                  R
4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  RV.4       VP                  4       RJpV'       d(   VP                  RWxWWWVVVVV.4       V^,          pEK  VP                  RWgWWWWVVV.4       V^,          pEK)  	  RRR4       RVRVRW,           /#   + '       g   i     L; i)z
Upserts into `voice_plan` table (existing DB schema).

Expected keys per item:
  voice_id, name, category, model_id, plans, gender
  (+ optional accent, age, use_case, description, language, locale, preview_url)
voice_idr"   r   r   rR   Nplansbasicr   accentageuse_caser   rA   rb   rK   z2SELECT 1 FROM voice_plan WHERE voice_id=%s LIMIT 1aL  
                        UPDATE voice_plan
                        SET name=%s,
                            category=%s,
                            model_id=%s,
                            plans=%s,
                            gender=%s,
                            accent=%s,
                            age=%s,
                            use_case=%s,
                            description=%s,
                            language=%s,
                            locale=%s,
                            preview_url=%s
                        WHERE voice_id=%s
                        a  
                        INSERT INTO voice_plan (voice_id, name, category, model_id, plans, gender, accent, age, use_case, description, language, locale, preview_url)
                        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
                        syncedupdatedr   )
r   r   r   r   rz   r*   r'   r    r{   r|   )r&   r   r   rv   r~   r0   r   r   r   rR   r   r   r   r   r   r   rA   rb   rK   existss   &                   r   _upsert_voice_plans.master_voice_sync.<locals>._upsert_voice_plans  s    !#"$$&&&((CEE*-33::<f++224@@EE*-33::<BB>?eeJ>O>[Cj 1288:acllhlw--2446AA'%%///R668>>B%%///R668>>BuuU|))r00288bEE*-33::<BB uu]399r@@BHHbEE*-33::<BB%%///R668>>B uu]399r@@BHHb PS[R\]t3KK  &#Ydfnpv  yD  FN  O#& qLGKK "UFYapx  {A  CN  O aKFe  )h &)Wgv?OPPi )(s   N
%N
>N
N
0N
N
#AN
*N
N
N
8N
N
+N
N
N
6N
N
)N
N
N
7N
N
*N
N
N
5/N
%AN

N	c                   V ^8  d   QhRR/# r   r   r   r   )r   s   "r   r   r     s     @ @,@ @r   c            !     	  <"   \         P                  ! ^R7      ;_uu_4       GRj  xL
 p V P                  RRS/R7      G Rj  xL
 pVP                  4        VP	                  4       ;'       g    / pRRR4      GRj  xL
  XP                  R4      ;'       g    . p. pV EF  p\        V\        4      '       g   K  VP                  R4      p\        V\        4      '       d   TM/ p\        V4      p\        VP                  R4      ;'       g    R	4      P                  4       ;'       g    \        VRR
4      p	\        VP                  R4      ;'       g    R	4      P                  4       ;'       g    \        VRR4      p
\        W4      w  r\        V4      pVe   \        V4      P                  4       R	8X  d+   VP                  R4      ;'       g    VP                  R4      pVe9   \        V4      P                  4       '       d   \        V4      P                  4       MRp\        VP                  R4      ;'       g    R	4      P                  4       ;'       g    \        VRR4      p\        VP                  R4      ;'       g    VP                  R4      ;'       g    R	4      P                  4       ;'       gr    \        VP                  R4      ;'       g    VP                  R4      ;'       g    R	4      P                  4       ;'       g    \        V4      ;'       g    \        V4      pVP                  R4      p\        V\        4      '       dW   \        VP                  R4      ;'       g    VP                  R4      ;'       g    R4      P                  4       ;'       g    RpM-\        T;'       g    R4      P                  4       ;'       g    RpTP                  RVP                  R4      ;'       g    VP                  R4      ;'       g    R	RVP                  R4      ;'       g    R	RTRTRRR\        VRR4      RTR\        VRR4      R\        VRRR 4      RT	RT
RTR!\        VP                  R!4      ;'       g    R	4      P                  4       /4       EK  	  V#  EL ELg EL0  + GRj  xL 
 '       g   i     ELH; i5i)"   timeoutNz#https://api.elevenlabs.io/v1/voices
xi-api-keyheadersvoicesr   rA   r"   Languagerb   LocalerR   rS   r   AccentrK   rL   r   valuer   
elevenlabsr   voiceIdr   r   Genderr   Ager   usecasezuse caser   )httpxAsyncClientr*   raise_for_statusjsonr%   r   rG   r    r'   r1   rg   rY   rO   r`   r   )clientrespr   r   outr0   
raw_labelsr   vlrA   rb   	model_rawrR   r   rK   cat_rawr   eleven_api_keys                    r   _fetch_elevenlabs_voices3master_voice_sync.<locals>._fetch_elevenlabs_voices  s     $$R000F5%~6 $  D !!#99;$$"D 10 (#))r$&Aa&&xJ#-j$#?#?ZRF*1-B BFF:&,,"-335 @ @
J?  )//R0668ddLQY[c<dF8JH7:I C	N$8$8$:b$@EE*-AAy1A	1:1F3y>K_K_KaKas9~++-gkH BFF8$**+113 < <(; 
 AEE-(EEAEE,,?EE2FLLN 7 7rvvm,JJ|0DJJKQQS7 7:1=7 7 4A6	  eeJ'G'4((w{{73ZZw{{67JZZl[aacssgsw66,7==?OO<JJj 1 K KQUU95E K KAEE&M//R\l68XFf<u=VZJ Wf!;!3quu]';'A'Ar#B#H#H#JG h 
{ 1 1000s   $SR(SR1R+*R10R13S>R.?SA5SS-*SS4ASA,SS*SS!S=SS4SS#AS3SS(S:S&S=SS1A8S+R1.S1S	7R:8
S	S		Sc                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r     s     _ _*> _r   c                 2
  <"   R RS 2R\         P                  ! RR4      /p . pRp\        P                  ! RR7      ;_uu_4       GRj  xL
 p R'R(.pV'       d   VP	                  RV34       \        VR	R
7      pVP                  RV 2V R7      G Rj  xL
 pVP                  4        VP                  4       p\        V\        4      '       d7   VP                  R4      ;'       g    . p\        VP                  R4      4      p	MT;'       g    . pRp	Rp
V EF  p\        V\        4      '       g   K  VP                  R4      ;'       g    VP                  R4      ;'       g    RpVe   \        V4      MT
p
VP                  R4      p\        V4      w  r\        V4      pVP                  R4      ;'       gD    VP                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      pVe9   \        V4      P                  4       '       d   \        V4      P                  4       MRp\        VP                  R4      ;'       g    R4      P                  4       pV'       g7   V'       d/   RV9   d(   VP!                  R^4      R),          P                  4       pTP	                  RTRVP                  R4      ;'       g    RRRRTRRRVP                  R4      ;'       g    RRTR\        VP                  R4      ;'       g    R4      P                  4       R \        VP                  R 4      ;'       g    VP                  R!4      ;'       g    R4      P                  4       R"\        VP                  R"4      ;'       g    R4      P                  4       RVR#VR$V/4       EK  	  V	'       g   MV
'       g   MT
pEK  \        R(.R	R
7      p^pV F  pV^ 8:  d    M\        VP                  R$4      ;'       g    R4      P                  4       '       d   KF  VP                  R4      pV'       g   Ka  V^,          p VP                  R%V R&V 2V R7      G Rj  xL
 pVP                  4        VP                  4       p\        T\        4      '       g   K  \        T4      pT'       g   K  TTR$&   K  	  RRR4      GRj  xL
  V#  EL ELB Lr  \        P"                   d     EK  i ; i L+  + GRj  xL 
 '       g   i     T# ; i5i)*AuthorizationBearer Cartesia-VersionCARTESIA_VERSION
2025-04-16N      ^@r   starting_after[]safez https://api.cartesia.ai/voices/?r   r   has_moreFidr   r"   rA   rR   rS   
base_modelmodelr   r$   r   r   cartesiar   r   r   r   useCaser   rb   rK   https://api.cartesia.ai/voices//?)limit100zexpand[]rj   r   )r   r   r   r   r   r   r*   r   r   r%   r   rw   r    r;   ro   r'   r7   	HTTPError)r   r   r  r   pairsqueryr   r   r   r  last_idr0   vidlang_rawrA   rb   rK   r   rR   r   q_enrichenrich_budgetitemdetailbodypucartesia_api_keys                             r   _fetch_cartesia_voices1master_voice_sync.<locals>._fetch_cartesia_voices  s[    
 w'7&89		*<l K

 %'%)$$U333v)+KL!LL"2N!CD!%d3#ZZ*J5'(R\cZdd%%'yy{dD))!XXf-33F#DHHZ$89H!ZZRF$H&*A%a.. %%+@@z):@@bC*-/c#hwG uuZ0H'B8'L$H"7":K !j 1 n nQUU95E n n|I\ n n`a`e`efm`nI9B9NSVW`SaSgSgSiSis9~335osH x!6!6B7==?F!f!'c1!5b!9!?!?!AJJ&"AEE&M$7$7R&
&#Z$quuX'<'<"$f!3quuU|'9'9r#:#@#@#B&AEE*,=,W,WyAQ,W,WUW(X(^(^(`)3quu]/C/I/Ir+J+P+P+R&$f);  >  !( !"B!C$OHM A%txx.44"5;;==hhz*"#)::9#b
K ' $. $ F ++-!;;=D dD)).t4Br.0]++ y 43f 
g 4 e|  Y 4333f 
s   AT	S
TAS:SAS:'&S:<S:S:$A-S:S:+A,S:S:5S:=AS: S:2!S:1S:S:1S:,S:>S:A S:S:#S:>S:
S&S
'$SS:#S:8	S:TS8TS:SS5	0S:4S5	5S:8T:T	 T
T	T		TproviderNr   enabledr   r   r   r  c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r   v  s     		 		D 		r   c                    <"   SR9   d/   S'       d'   S! 4       G Rj  xL
 p \        V 4      SR&   SV ,          oSR9   d3   S'       d)   S! 4       G Rj  xL
 p\        V4      SR&   SV,          oR# R# R#  LV L#5i)r"   r   Nr  >   r"   r   >   r"   r  )len)	evcvr#  r   can_cartesia
can_elevenfetched_countsr&   r%  s	     r   _runmaster_voice_sync.<locals>._runv  sr     ..J355/22w|,,,,133-0Wz* 3?, 6 4s,   A8
A8A4)A8
A8A6"A86A8r   Tr   r   z<No provider API keys configured (or provider has no voices).	providersfetchedFz$Database error while saving voices: r   r   combinedzVoice plans synced.zVoice sync failed:   >   r  r   )r   r   r   r'   r%   r   r   r*   r    r(   rw   asynciorunr   r   updater   r  
ValueErrorRuntimeError)r   r$   r   r   provider_resultsr/  r5  upr   r#  r   r,  r-  r"  r   r.  r&   r%  s   &        @@@@@@@@@r   master_voice_syncr<  v  s    #7+FA

ii 45;;BBDN		"45;;BBDAQF@ @D_ _B 1;7<<0N0N  ,TX__]_H8}""$**,Hn%J()L #%Ey*h9aRSTYh9aRST(
E
*6:q)I		 		 	DFt !!1!#a#%5!>
 
	$U+B 11X&--b1 ,.Z(4 4!1~	

 
	
#  	u!Ec!fXN[*:;
  	8 OOZ6 
y,?Ax*H&S^`pRqr
 	

sO   2AH G
 <H 
H  G;5H 6H ;H  H  I# I	I	Ic                   V ^8  d   QhRR/# )r   r   r    r   )r   s   "r   r   r     s     5 5 5r   c           	        \        V 4      w  r#V'       d   V#  \        R ,          P                  4       ;_uu_ 4       pVP                  RV.4       VP                   Uu. uF  qU^ ,          NK  	  ppVP                  4        Uu. uF  p\        Wv4      NK  	  ppRRR4       \        RRR	X/4      # u upi u upi   + '       g   i     L); i  \         d%   p	\        RRR\        T	4      /RR7      u Rp	?	# Rp	?	ii ; i)
rs   zlSELECT voice_id, name, category, model_id, plans, gender FROM voice_plans WHERE plans = %s ORDER BY name ASCNr   Fr   r   r   Tr   )
r   r   rz   r{   r   r   r   r   r   r    )
r   r   r$   r   r~   r   r   r   r   r   s
   &&        r   master_voice_by_planr?    s     #7+FA


K#**,,KKF
 #&//2/QaDD/D247LLNCNqM!*NDC - Yfd344 3C -,  KE9c!f=cJJKsX   #C "B:B0/B:B5B:C 0
B::C
	C 
C C<C71C<7C<c                   \        V 4      w  rV'       d   V# V P                  P                  R4      ;'       g    RP                  4       pV'       g   \	        RRRR/RR7      #  \        V4      pTP                  R 9  g   TP                  '       g   \	        RRRR	/RR7      # TP                  ;'       g    RP                  4       p\        R
R04      pYV9  d   \	        RRRR/RR7      # \        P                  ! R4      ;'       g    RP                  4       pT'       g   \	        RRRR/RR7      # RRT 2R\        P                  ! RR4      /p \        P                  ! RRR7      ;_uu_ 4       p	T	P                  Y8R7      p
RRR4       X
P"                  P                  R4      ;'       g    RpT
P$                  ^8w  d#   \'        T
P(                  T
P$                  TR7      # \'        T
P(                  TR7      #   \         d    \	        RRRR	/RR7      u # i ; i  + '       g   i     L; i  \        P                    d    p\	        RRRRT: 2/RR7      u Rp?# Rp?ii ; i)!z
Cartesia preview URLs under files.cartesia.ai require `Authorization: Bearer`.
The HTML <audio> element cannot send that header, so the UI calls this
endpoint with the user's master JWT; we forward the request with the server
Cartesia API key.
r]   r"   r   Fr   zMissing url parameter.  r   zInvalid url.zfiles.cartesia.aizapi.cartesia.aizPreview host not allowed.i  r    Cartesia API key not configured.r4  r  r  r  r  r  r  Tr   follow_redirectsr   NUpstream error: content-typeapplication/octet-streamr   content_typerI  )httphttps)r   r   r*   r'   r   r   r   schemenetlochostnamer(   	frozensetr   r   r   Clientr  r   status_coder	   content)r   r$   r   raw_urlparsedhostallowed_hostscartesia_keyr   r   upstreamr   cts   &            r   master_voice_preview_proxyr[    s:    #7+FA

{{u%++224GE96NOX[\\S'" }}--V]]]E9nEcRROO!!r((*D24EFGM E96QR[^__II0177R>>@LE96XYbeff 	7<.1BII&8,GG
]\\%$??6zz'z;H @
 
				n	-	K	K1KBs"''
 	
 ((r::E  SE9nEcRRS* @??? ]E98H6NOX[\\]sN   %G<  H1 2HH1 <HHH.	)H1 .H1 1I%I I% I%c                <   \        V 4      w  rV'       d   V# V P                  P                  R4      ;'       g    RP                  4       P	                  4       pV P                  P                  R4      ;'       g    RP                  4       pVRF9  d   \        RRRR/R	R
7      # V'       g   \        RRRR/R	R
7      # VR8X  Ed   \        P                  ! R4      ;'       g    RP                  4       pV'       g   \        RRRR/RR
7      # RRV 2R\        P                  ! RR4      /pRpRp \        4       p	\        V	,          P                  4       ;_uu_ 4       p
V
P                  RV.4       V
P                  4       pV'       do   V^ ,          e!   \        V^ ,          4      P                  4       MR;'       g    RpV^,          e!   \        V^,          4      P                  4       MR;'       g    RpRRR4       \        RG.RR7      p \         P"                  ! RRR7      ;_uu_ 4       pTP                  RT RT 2TR7      pTP%                  4        TP'                  4       p\)        T\*        4      '       g   \        RRRR/RR
7      uuRRR4       # \-        T4      pT'       d   TP                  TTR7      pMT;'       g    RpT;'       g=    \        TP                  R 4      ;'       g    R4      P                  4       ;'       g    R!P/                  R"R#4      pTP1                  R$/ TCR%R&/CR'TR(R)R*R+R,R,T/R-R.R/R0R1R2R3/R T/R47      pRRR4       XP:                  P                  R74      ;'       g    R8pTP6                  ^8w  d#   \=        TP>                  TP6                  TR97      # \=        TP>                  TR:7      # \        P                  ! R;4      ;'       g    RP                  4       pV'       g   \        RRRR</RR
7      #  \         P"                  ! R=RR7      ;_uu_ 4       pVP                  R>V 2R?V/R7      pVP%                  4        VP'                  4       p\)        V\*        4      '       g   \        RRRR@/RR
7      uuRRR4       # \        VP                  RA4      ;'       g    VP                  RB4      ;'       g    R4      P                  4       pV'       g   \A        V4      ;'       g    \C        V4      pV'       g   \        RRRRC/RDR
7      uuRRR4       # VP                  V4      pRRR4       XP:                  P                  R74      ;'       g    R8pTP6                  ^8w  d#   \=        TP>                  TP6                  TR97      # \=        TP>                  TR:7      #   + '       g   i     EL; i  \         d     ELi ; i  + '       g   i     EL; i  \         P2                   dE   pTP4                  e   TP4                  P6                  MRp\        RRRR5T 2/RR
7      u Rp?# Rp?i\         P8                   d    p\        RRRR6T: 2/RR
7      u Rp?# Rp?ii ; i  + '       g   i     ELH; i  \         P2                   dE   pTP4                  e   TP4                  P6                  MRp\        RRRRET 2/RR
7      u Rp?# Rp?i\         P8                   d    p\        RRRR6T: 2/RR
7      u Rp?# Rp?ii ; i)Ha  
On-demand preview fetch by voice_id when preview_url is missing in DB.

- provider=cartesia: GET /voices/{id}?expand[]=preview_file_url, then download preview_file_url via Cartesia auth.
- provider=elevenlabs: GET /v1/voices/{id}, then fetch preview_url if present (no auth expected).
r%  r"   r   r  r   Fr   zInvalid provider.rA  r   zMissing voice_id.r   rB  r4  r  r  r  r  r  NzSELECT model_id, language FROM voice_plan WHERE voice_id=%s ORDER BY (language IS NOT NULL AND language <> '') DESC, (model_id IS NOT NULL AND model_id <> '') DESC LIMIT 1r  r	  r  TrC  r  r  r   zUnexpected Cartesia response.zsonic-3rA   rB   r6   r$   z!https://api.cartesia.ai/tts/byteszContent-Typezapplication/jsonrR   
transcriptzHello! This is a voice preview.r=   moder  output_format	containerwavencoding	pcm_s16lesample_rateiD  )r   r   zCartesia error: rE  rF  rG  rH  rJ  r   z"ElevenLabs API key not configured.g      N@z$https://api.elevenlabs.io/v1/voices/r   zUnexpected ElevenLabs response.rK   rL   z$No preview available for this voice.i  zElevenLabs error: >   r  r   r  )"r   r   r*   r'   r(   r   r   r   r   r   rz   r{   r|   r    r   r   r   rQ  r   r   r%   r   ro   r)   postHTTPStatusErrorresponserR  r  r   r	   rS  rO   r`   )r   r$   r   r%  r   rX  r   rR   rA   rv   r~   r   r   r   r  payloadrK   rY  rW   r9   r   r   rZ  
eleven_keyr0   s   &                        r   master_voice_preview_by_idrj  	  s]    #7+FA


+11r88:@@BH
+11r88:H11E96IJSVWWE96IJSVWW:		"45;;BBDYy:\]fijj w|n5		*<l K

  $#	(*EU#**,, J lln7:1v7ICF 1 1 3rZZVZH7:1v7ICF 1 1 3rZZVZH - 78tD	aedCCv&EhZrRSQT$U_fg'') ++-!'400#YyBa$bkno DC 4G<%zz+wzGH #//iC$ZZGKK
,C,I,Ir(J(P(P(RZZVZccdgilmD%{{; O7 ON<N O&(*K#fdD(%C+k5*k[hjo-p&  + 
 H D@ !!.1OO5O3& 0 09M9M\^__H,,2>> ))0177R>>@JE96Z[dghh]\\$>>&ZZ6xjA%z2   F ##%Aa&&E9>_ `ilm ?> aeeM2OOaeeL6IOORPVVXKEaHooLlmnLoE9>d enqr ?> zz+.H ?* 
				n	-	K	K1KBs"H,,X5I5IXZ[[((r::i -,,  		
 DCC4 $$ 	d/0zz/EQZZ++3FYy<LVH:UV_bcc 	aYy<LQE:RS\_``	a ?>>    b+,::+A''sE98J6(6ST]`aa?? ]E98H6NOX[\\]sB  /-W AV4<6V43V46W  W. -A W
W. /WWW2WAWW.  Z .AY<
Z Y<5Y<Y<+Y<=Y<Y<!
Z ,Y<=Z 4W	?W W WWW+	%W. +W. .Y99Y<Y9Y9Y9Y4.Y94Y9<Z	Z Z \%9[$\$\;\<\\\)(
__future__r   r   typingr   urllib.parser   r   r   	django.dbr   r   django.httpr	   rest_framework.decoratorsr
   r   rest_framework.permissionsr   rest_framework.responser   config.master_business_viewsr   r   r1   r;   rG   rO   rY   r`   rg   ro   r   r   r   r   r<  r?  r[  rj  r   r   r   <module>rt     sF   " 	  ,  0 $ B 6 , > *&4:(""=HK4+G\ 
5'_%&[ ' [| 
6(_%&@
 ' @
F
 
5'_%&5 ' 5( 
5'_%&3; ' 3;l 
5'_%&t; ' t;r   