
    %;j<              
         d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	 ddl
mZ  e ee      j                  dz  dz        Zd	Zd
ZdZdZdZdZd.dZd/dZd0dZd1dZd2dZ	 	 	 	 d3dZ	 	 	 	 	 	 	 	 d4dZ	 	 	 	 	 	 	 	 	 	 d5dZdddedf	 	 	 	 	 	 	 	 	 	 	 	 	 d6dZedk(  rddl Z ddl!Z! e"e!jF                        dk\  rY e$e!jF                  d   dd      5 Z% e jL                  e%      Z'ddd        ee'      Z( e)e(dd          e)d! e"e(       d"       yd#d$d%d&d'd$d(d)d*d(d+d)d,d+d%d)gd-giZ* ee*ddd      Z+ e)e+       yy# 1 sw Y   ]xY w)7u`  
Karaoke IG style - legenda word-level estilo Instagram/TikTok.

Gera filtros drawtext do ffmpeg que renderizam:
- Blocos curtos de 2-4 palavras
- Texto branco caixa-alta com contorno preto
- Palavra falada destacada com pill laranja por trás
- Posicionamento ~67% do topo do vídeo
- Fonte Manrope ExtraBold

Uso:
    from karaoke_legenda import gerar_karaoke_drawtext_filters
    filtros = gerar_karaoke_drawtext_filters(transcricao, 1080, 1920, fontsize=70)
    # filtros é uma string longa com 1+N drawtext (1 por bloco branco, N por palavras ativas)
    ffmpeg -i input.mp4 -vf "{filtros}" output.mp4
    )annotationsN)Path)ListDictAny)	ImageFontfontszManrope-ExtraBold.ttf0xF47B3F      g
ףp=
?      c                >    | j                         j                         S )u9   Caixa alta + remove caracteres não-imprimíveis simples.)stripupper)palavras    :/opt/mia/workspace/video_editor/backend/karaoke_legenda.py_normalizarr   1   s    ==?  ""    c                    | j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  d	d
      }|S )u   
    Escapa texto pra entrar no filter drawtext.
    Drawtext usa : como separador e \ como escape. Aspas simples
    também precisam de cuidado quando o filtro é quotado.
    \z\\:z\:'u   ’,z\,%z\%)replace)textoouts     r   _escapar_drawtextr   6   sY     --f
%C
++c5
!C
++c5
!C
++c5
!C
++c5
!CJr   c                   g }| j                  d      xs | j                  d      xs g }|D ]  }|j                  d      xs |j                  d      xs g }|D ]  }|j                  d      xs( |j                  d      xs |j                  d      xs dj                         }|sOt        |j                  d	|j                  d
d                  }t        |j                  d|j                  d|dz                     }||k  r|dz   }|j                  t	        |      ||d         |s|D ]  }|j                  d      xs |j                  d      xs dj                         }|s=t        |j                  d
|j                  d	d                  }	t        |j                  d|j                  d|	dz                     }
t        j                  d|      D cg c]  }|s|	 }}|s|
|	z
  t        dt        |            z  }t        |      D ]2  \  }}|j                  t	        |      |	||z  z   |	|dz   |z  z   d       4  |j                  d        d}|t        |      k  r|}|dz   t        |      k  rE||dz      d	   ||   d	   dz   k  r.|dz  }|dz   t        |      k  r||dz      d	   ||   d	   dz   k  r.||kD  r||   d	   }t        d |||dz    D              }|dz   t        |      k  rt        |||dz      d	         }t        ||z
  d||z
  dz   z        }|||z
  dz   z  }t        ||dz         D ]'  }|||z
  |z  z   ||   d	<   |||z
  dz   |z  z   ||   d<   ) |dz   }|t        |      k  rt        t        |      dz
        D ]J  }||   d   ||dz      d	   kD  r||dz      d	   ||   d<   ||   d   ||   d	   k  s:||   d	   dz   ||   d<   L |S c c}w )u7  
    Pega a transcrição do Whisper (que já vem com word_timestamps=True)
    e extrai a lista flat de palavras com {word, start, end}.

    A função tenta primeiro `transcricao["segmentos"][i]["words"]`
    (formato do nosso wrapper). Se não tiver, tenta `segments[i]["words"]`
    direto do Whisper.
    	segmentossegmentswordspalavraswordtextr    startinicior   endfimg333333?皙?r%   r(   r*   r   g      ?z\s+   c                    | d   | d   fS )Nr(   r*    )ps    r   <lambda>z#_coletar_palavras.<locals>.<lambda>w   s    7QuX 6 r   key{Gz?c              3  &   K   | ]	  }|d      yw)r*   Nr0   .0r1   s     r   	<genexpr>z$_coletar_palavras.<locals>.<genexpr>   s     B1ahB   皙?)getr   floatappendr   resplitmaxlen	enumeratesortminrange)transcricaor$   r!   segr#   wtxtr(   r*   inir+   ttokensdtitkjstart_clusterend_clusterspanks                        r   _coletar_palavrasrV   E   sX    H,Q
0KQrI  =CGGJ$7=2 	A55=KAEE&MKQUU95EKRRTC!%%x);<=EeQUU5%#+%>?@Ce|ckOO#C( 	$  	C777#<swwv<"CCEC#'''1*=>?CswwucCi'@ABC!#&#!6<A!a<F<)s1c&k22B"6* 2'O 1r6\!a%2-! 	& MM6M7 	
A
c(m
!ec(m#Q(@HQKPWDX[_D_(_FA !ec(m#Q(@HQKPWDX[_D_(_q5$QK0MB1q50ABBK1us8}$!+xAw/GH{]2DAEAI4FGDQ#B1a!e_ F'4A|'CG$%2a!eai25E%EE"F E# c(m
( 3x=1$% =A;uQ 88!)!a%!9HQKA;u!W!55!)!W!5!<HQK	= OY =s   "O/*O/c                   | sg S g }g }t        |       D ]  \  }}|s|j                  |       |d   |d   d   z
  }|d   d   }|j                  d      }|dkD  xs |}t        |      t        k\  }	t        |      t
        k\  }
|	s|r|
r|j                  |       |g}|j                  |        |r|j                  |       |S )u   
    Agrupa palavras em blocos de 2-4. Tenta quebrar em pontuação natural
    (final de segmento com pausa > 0.5s entre palavras), senão divide por tamanho.

    Retorna lista de blocos, cada bloco é lista de palavras consecutivas.
    r(   r*   r%   ).!?r   ?)rC   r>   endswithrB   MAX_WORDS_POR_BLOCOMIN_WORDS_POR_BLOCO)r$   blocosatualrO   r1   gapulttem_pontquebra_naturalcheiorazoavels              r   _agrupar_blocosrh      s     	FE(# 1LLOj59U++ Bi<< 45t/xE
11u:!44^MM% CELLO%( eMr   c                6    t        | j                  |            S )z'Retorna largura do texto na fonte (px).)r=   	getlength)fontr   s     r   _medirrl      s    &''r   c           	        |D cg c]  }|d   	 }}dj                  |      }t        | |      }t        | d      t        | d      z
  }t        | d      }||z
  dz  }g }	|}
t        |      D ]P  \  }}t        | |d         }|	j                  |d   |
||d   |d   d       |
|z  }
|t	        |      dz
  k  sL|
|z  }
R | j                  d	      }|d
   |d   z
  }|||||	dS c c}w )u  
    Calcula a string completa do bloco e a posição x de cada palavra
    quando o bloco é centralizado horizontalmente.

    Retorna:
        {
            "texto": "MEU CACHORRO",
            "x_inicial": 256,   # x do canto esquerdo do bloco no vídeo
            "largura_total": 567,
            "altura": 76,
            "palavras_pos": [
                {"word": "MEU", "x": 256, "largura": 152, "start":.., "end":..},
                {"word": "CACHORRO", "x": 422, "largura": 400, ...},
            ]
        }
    r%    z         @r(   r*   )r%   xlargurar(   r*   r.   
   ÁGÊMQpgy   )r   	x_iniciallargura_totalalturapalavras_pos)joinrl   rC   r>   rB   getbbox)rk   blocovideo_widthr1   palavras_txttexto_completoru   largura_espacort   poscursorrO   rI   bboxrv   s                  r   _posicoes_blocor      s/   & (--!AfI-L-XXl+N 40MD$'&s*;;ND#&N},3I
CF%  %146#

fIwZU8
 	 	!s5zA~n$F% <<%D!WtAwF  & = .s   C#c                P    t        |       }d| d| d| d|dd|dd|dd	|dd
S )z0Drawtext do texto branco completo (1 por bloco).drawtext=fontfile='':text='':fontsize=z/:fontcolor=white:borderw=3:bordercolor=black:x=.1f:y=:enable='between(t,.3fr   )')r   )r   rp   yfontsizet_iniciot_fim	font_path	texto_escs           r   _drawtext_brancor      sZ     "%(I
i[ ) JWC#w
hs^1U3Kr	;r   c                d    t        |       }d| d| d| dt         d| d|dd|dd	|d
d|d
dS )u   
    Drawtext da palavra ativa, com box laranja (pill) + borda preta no texto
    pra manter consistência com o texto branco abaixo.
    r   r   r   z<:fontcolor=white:borderw=3:bordercolor=black:box=1:boxcolor=z@1.0:boxborderw=z:x=r   r   r   r   r   r   )r   
PILL_COLOR)	r   rp   r   r   r   r   r   pill_paddingr   s	            r   _drawtext_pillr     sh     "'*I
i[ ) J%,&6|n
aWC#w
hs^1U3Kr	;	r   i8  i  F   c                   |t         }t        |      j                         st        d|       t	        |       }|syt        |      }|syt        j                  ||      }|j                  d      }	|	d   |	d   z
  }
||z  |
dz  z
  }|dz  }g }|D ]  }dj                  d	 |D              }t        |j                  |            |k  st        |      dk  r|j                  |       Xg }|D ]~  }|j                  |       dj                  d
 |D              }t        |j                  |            |kD  sJt        |      dkD  sY|j                         }|r|j                  |       |g} |s|j                  |        |}|j                  d        g }t        |      }t!        |      D ]  \  }}t#        |||      }|d   d   }|d   d   dz   }|dz   |k  r||dz      d   d   }t%        ||dz
        }n|}||k  rU|j                  t'        |d   |d   |||||             |d   }t!        |      D ]~  \  }}|dz   t        |      k  rt%        |d   dz   ||dz      d   dz
        }nt%        |d   dz   |      }||d   k  rQ|j                  t)        |d   |d   |||d   ||t*                       dj                  |      S )um  
    Função principal. Gera string de filtros drawtext separados por vírgula
    pra ser usada em ffmpeg -vf.

    Args:
        transcricao: dict com chave 'segmentos' (cada um tem 'words' do Whisper)
        video_width, video_height: tamanho do vídeo de saída
        fontsize: tamanho da fonte (px). Padrão 70 (~3.6% da altura).
        y_fracao: posição vertical (0 = topo, 1 = base). Padrão 0.67.
        font_path: override do caminho da fonte. Padrão Manrope-ExtraBold.

    Returns:
        string única com todos os filtros drawtext separados por vírgula.
        Vazio se não houver palavras.
    u   Fonte não encontrada: r'   rr   rs   r.   r   gq=
ףp?rn   c              3  &   K   | ]	  }|d      ywr%   Nr0   r7   s     r   r9   z1gerar_karaoke_drawtext_filters.<locals>.<genexpr>S  s     7Aai7r:   c              3  &   K   | ]	  }|d      ywr   r0   )r8   pps     r   r9   z1gerar_karaoke_drawtext_filters.<locals>.<genexpr>[  s     $BBRZ$Br:   c                    | d   d   S )Nr   r(   r0   )bs    r   r2   z0gerar_karaoke_drawtext_filters.<locals>.<lambda>g  s    ad7m r   r3   r   r(   rX   r*   r,   r5   r   rt   rw   r;   g{Gzt?r%   rp   r   )	FONT_PATHr   existsFileNotFoundErrorrV   rh   r   truetypery   rx   r=   rj   rB   r>   poprD   rC   r   rE   r   r   PILL_PADDING_X) rG   r{   video_heightr   y_fracaor   r$   r`   rk   r   altura_textoy_baselargura_maxblocos_ajustadosrz   
texto_fullparcialr1   texto_parcialultimofiltrosnrO   infot_inicio_blocot_fim_naturalt_inicio_proximot_fim_blocoppsrQ   r   end_pills                                    r   gerar_karaoke_drawtext_filtersr   !  s=   . 		?!!#"9) EFF -HX&F i2D <<%D7T!W$LX%,*:;F $K -XX777

+,;s5zQ##E* 	#ANN1HH$B'$BBMT^^M23kAc'lUVFV $++G4!(	# ##G,#-$ F KK+K,G 	FAf% -5tUK8q'*b	%(4/q519%a!e}Q/8m-=-DEK'K.( 	'M
 	 >"s^ 	EAr1us3xr%y4/QUG1Du1LMr%y4/=2g;&NN>6
37	 		;-^ 88Gr   __main__r.   rzutf-8)encodingi  z
... (z chars total)r!   g        ro   zmeu cachorro brincameur\   r-   cachorrog333333?brinca)r)   r+   r   r#   )r   strreturnr   )r   r   r   r   )rG   Dict[str, Any]r   List[Dict[str, Any]])r$   r   r   zList[List[Dict[str, Any]]])rk   ImageFont.FreeTypeFontr   r   r   r=   )rk   r   rz   r   r{   intr   r   )r   r   rp   r=   r   r=   r   r   r   r=   r   r=   r   r   r   r   )r   r   rp   r=   r   r=   r   r   r   r=   r   r=   r   r   r   r   r   r   )rG   r   r{   r   r   r   r   r   r   r=   r   r   r   r   ),__doc__
__future__r   r?   pathlibr   typingr   r   r   PILr   r   __file__parentr   r   r^   r_   Y_FRACAOr   PILL_PADDING_Yr   r   rV   rh   rl   r   r   r   r   __name__jsonsysrB   argvopenfloadtrr   printmockr   r0   r   r   <module>r      s5  " # 	  " "  X%%/2IIJ	 
     
#
Qh#L(
7!$7)77t %.3@CHK#,1>A"%*-, {{{ { 	{
 { { 	{B z 388}#((1+sW5 	1B	04getnG~]34 ."S>'$tD%TB		 	
 -T4rBc
5 	 	s   EE