
    j4I                     b   U 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mZ ddl	m
Z
 ddlmZmZmZmZmZmZ ddlmZ ddlmZ ddlmZ  e
e      j2                  d	z  Z e
e      j2                  d
z  Z e
e      j2                  dz  Zedz  Zedz  Zedz  Zh dZ dZ!dZ"eeeefD ]  Z#e#jI                  dd         ee%      Z& ee&dd       i Z'e(e)d<   dddZ*de(fdZ+de,de(dz  fdZ- ej\                         Z/de(fdZ0de(ddfdZ1de,de(dz  fdZ2d e,de3fd!Z4de5fd"Z6dJd#e,d$e,d%e5d&e,d'e,f
d(Z7d) Z8 e8        	  e6       Z9e9r e:d*e9 d+       e&j{                  d-d.g/      d0        Z>e&j{                  d1d2g/      d3        Z?e&j{                  d4d.g/      d#e,fd5       Z@e&j{                  d6d.g/      d#e,fd7       ZAe&j{                  d8d.g/      d9        ZBe&j{                  d:d.g/      de,fd;       ZCe&j{                  d<d2g/      d=        ZDe%d>k(  rw e:d?        e:d@        e:dAe         e:dBe         e:dCe         e+       ZE e:dD eFeEj                  dEg                      e:d?       e&j                  dFdGdHdI       yy# e;$ rZ< e:d,e<        Y dZ<[<RdZ<[<ww xY w)Kz*Backend do Video Editor - Flask porta 5054    N)datetime	timedelta)Path)Flaskjsonifyrequest	send_fileabortResponse)CORS)secure_filename)processar_videotempoutputmusicuploads
index.jsonzmanifest.json>   .aac.m4a.ogg.wav.mp3i  @   T)exist_okparents*)originssupports_credentialsjobs)mtimedatareturnc                     t         j                         sg g dS t         j                         j                  } t        d   | t        d   k7  rJ	 t        t         dd      5 }t        j                  |      t        d<   ddd       | t        d<   t        d   S t        d   S # 1 sw Y   $xY w# t        $ r}t        d|        g g dcY d}~S d}~ww xY w)	z,Carrega manifest.json (com cache por mtime).)tracksmoodsr!   Nr    rutf-8encodingz[manifest] erro ao carregar: )
MANIFEST_PATHexistsstatst_mtime_manifest_cacheopenjsonload	Exceptionprint)r    fes      ./opt/mia/workspace/video_editor/backend/app.py_load_manifestr7   /   s    !r** ))Ev&%?73K*K	/mS7; 7q*.))A,'7',OG$ 6""?6""7 7  	/1!56 2..	/s6   B,  B =B,  B)%B, ,	C5CCCtrack_idc                 v    t               }|j                  dg       D ]  }|j                  d      | k(  s|c S  y )Nr$   id)r7   get)r8   r!   ts      r6   _track_by_idr=   ?   s>    DXXh# 55;("H     c                      t         j                         si S 	 t        t         dd      5 } t        j                  |       cddd       S # 1 sw Y   yxY w# t
        $ r}t        d|        i cY d}~S d}~ww xY w)u9   Lê o index.json dos uploads (cria vazio se não existe).r&   r'   r(   Nz[uploads] erro lendo index: )UPLOADS_INDEXr+   r/   r0   r1   r2   r3   )r4   r5   s     r6   _load_uploads_indexrA   O   sj    !	-w7 	 199Q<	  	  	  ,QC01	s9   A A		A 	AA A 	A8A3-A83A8r!   c                    t         5  	 t        t        dd      5 }t        j                  | |dd       d d d        d d d        y # 1 sw Y   xY w# t
        $ r}t        d|        Y d }~1d }~ww xY w# 1 sw Y   y xY w)Nwr'   r(   F   ensure_asciiindent[uploads] erro salvando index: )_uploads_lockr/   r@   r0   dumpr2   r3   )r!   r4   r5   s      r6   _save_uploads_indexrK   [   s    	 9	9mS7; Aq		$a@A9 9A A 	93A3788	9	9 9sD   A6AAAA	A	A3A.)A6.A33A66A?c                 `    | r| j                  d      syt               }|j                  |       S )z>Retorna metadata de um upload pelo id (ex: 'uploaded-<uuid>').	uploaded-N)
startswithrA   r;   )r8   r!   s     r6   _upload_by_idrO   d   s,    8..{; D88Hr>   pathc           	      F   	 t        j                  dddddd| gdd      }|j                  d	k7  ry
t        j                  |j
                  xs d      }t        |j                  di       j                  dd	            S # t        $ r}t        d|        Y d}~y
d}~ww xY w)u?   Retorna duração em segundos via ffprobe (0 em caso de falha).ffprobez-vquietz-print_formatr0   z-show_formatT)capture_outputtextr   g        z{}formatdurationz[ffprobe] erro: N)

subprocessrun
returncoder0   loadsstdoutfloatr;   r2   r3   )rP   r&   infor5   s       r6   _ffprobe_duracaor_   l   s    NNgT#d

 <<1zz!((*d+TXXh+//
A>??  $%s   .A? AA? ?	B BB c                  r   t        j                         t        t              z
  } t	               }|sd}t
        j                         rt
        j                         D ]l  }|j                         s|j                  dk7  s$t        j                  |j                         j                        }|| k  sW	 |j                          |dz  }n |S d}i }|j                         D ]  \  }}	 t        j                   |j#                  dd            }|| k  rL|j#                  d      }	|	r3t%        |	      j                         r	 t%        |	      j                          |dz  }}|||<    |dkD  rt'        |       |S # t        $ r Y 'w xY w# t        $ r t        j                         }Y w xY w# t        $ r Y ]w xY w)zs
    Remove uploads com mais de UPLOAD_TTL_HORAS.
    Atualiza o index.json em disco. Retorna quantos removeu.
    hoursr   r      
created_at rP   )r   utcnowr   UPLOAD_TTL_HORASrA   UPLOADS_DIRr+   iterdiris_filenameutcfromtimestampr,   r-   unlinkr2   itemsfromisoformatr;   r   rK   )
cutoffr!   	removidosr4   r    novostidmetacriadops
             r6   _limpar_uploads_antigosrw   }   s   
 __1A!BBF D	 ((* !99;166\#9$55affh6G6GHEv~!HHJ%NI! IEZZ\ 	T	'++DHH\2,FGF F? AT!W^^%GNN$ NIE#J" 1}E"3  ) ! !  	'__&F	' ! s6   4E7(%F?F*7	FFF'&F'*	F65F6job_idstatus	progressomensagem
video_pathc                     | t         vry t         |    j                  |||t        j                         j	                         d       |r|t         |    d<   y y )N)ry   rz   r{   
updated_atr|   )r   updater   rf   	isoformat)rx   ry   rz   r{   r|   s        r6   callback_statusr      sX    TLoo'113	  %/V\" r>   c                  Z   t        j                         t        d      z
  } t        t        fD ]l  }|j                         D ]W  }|j                         st        j                  |j                         j                        }|| k  sG	 |j                          Y n y# t        $ r Y hw xY w)u>   Remove arquivos temporários e de output com mais de 24 horas.r   ra   N)r   nowr   TEMP_DIR
OUTPUT_DIRri   rj   fromtimestampr,   r-   rm   r2   )rp   dr4   r    s       r6   cleanup_old_filesr      s    \\^ib11F
#  	Ayy{ ..qvvx/@/@A6>
	 % s   	B	B*)B*z[uploads] Limpeza inicial: z# upload(s) expirado(s) removido(s).z#[uploads] erro na limpeza inicial: z/api/healthGET)methodsc                  ^    t        dt        j                         j                         d      S )Nok)ry   	timestamp)r   r   rf   r    r>   r6   healthr      s"    d1B1L1L1NOPPr>   z/api/processarPOSTc                  4   dt         j                  vrt        ddi      dfS t         j                  d   } | j                  st        ddi      dfS t	        | j                        j
                  j                         }|dvrt        dd| i      dfS t         j                  j                  dd	      }d4d} |d |dd
            } |dd
      } |dd
      }t         j                  j                  d      xs dj                         j                         }|dvrd}|dk(  rd}t         j                  j                  d      xs dj                         xs d}d}	|r|j                  d      rgt        |      }
|
st        dd| i      dfS t	        |
j                  dd            }|j                         st        ddi      dfS t        |      }	d
}n]t        |      }|st        dd| i      dfS t        |d   z  }|j                         st        dd |d    i      dfS t        |      }	d
}	 t!        t         j                  j                  d!d"            }t'        d$t)        d%|            }t         j                  j                  d&d'      }t         j                  j                  d(d)      }|d*vr	 t!        |       g }t         j                  j                  d+d,      }	 t+        j,                  |      }t        t1        j2                               }t        t4        | d-| z        }| j7                  |       d.d$d/dt9        j:                         j=                         t9        j:                         j=                         d0t>        |<   tA        jB                  tD        ||||||tF        f||	||||d1d
2      }|jI                          t        d3|i      S # t"        t$        f$ r d#}Y w xY w# t$        $ r d)}Y ?w xY w# t.        $ r Y w xY w)5u$   Inicia o processamento de um vídeo.videoerrou   Arquivo de vídeo não enviado.  u   Nome do arquivo inválido.>   .avi.mkv.mov.webm.mp4   Formato não suportado: modoclipTc                     t         j                  j                  |       }||S t        |      j	                         j                         dv S )N>   1onsimyestrue)r   formr;   strlowerstrip)rk   defaultvs      r6   _boolzprocessar.<locals>._bool   s?    LLT"9N1v||~##%)JJJr>   usar_musicamusica)r   usar_legenda
usar_brollbroll_fontepexels>   noner   seedancer   F	musica_idre   NrM   u+   musica_id de upload inválido ou expirado: rP   u?   arquivo enviado não encontrado no servidor (pode ter expirado)  u   musica_id inválido: arquivou0   arquivo da música não encontrado no servidor: volume_musica12   r   2   estilo_legenda
karaoke_igtamanho_legendamedio>   r   grandepequeno	segmentosz[]_inputfilaz
Na fila...)ry   rz   r{   r|   rd   r~   )r   musica_path_diretor   r   r   r   )targetargskwargsdaemonrx   )T)%r   filesr   filenamer   suffixr   r   r;   r   rN   rO   r+   r   r=   	MUSIC_DIRint	TypeError
ValueErrormaxminr0   r[   r2   uuiduuid4r   saver   rf   r   r   	threadingThreadr   r   start)
video_fileextr   r   r   r   r   r   r   r   upcandr<   r   r   r   r   seg_jsonrx   r|   s                       r6   	processarr      s    gmm# ABCSHHw'J <=>CC z""
#
*
*
0
0
2C
;;":3% @ABCGG<<FF+DK uXt/LMK6L|T2J <<##M2>hEEGMMOK88f
!!+.4";;=EI ,y)B*UV_U`(abcehhhvr*+D;;=(ijkmppp!$TKY'A*?	{(KLMsRRq|+D;;=*Z[\]f[gZh(ijkmppp!$TKGLL,,_dCD 3r=12M\\%%&6ENll&&'8'BO<<	&  I||T2HJJx(	 F X6(& 667JOOJ  oo'113oo'113DL 	j$	;P_`."4($*&
 	A GGIHf%&&g z"   	&%O	&  s6   8)O  <O8 *P
  O54O58PP
	PPz/api/job/<job_id>c                     | t         vrt        ddi      dfS t         |    j                         }|d   |d   |d   d}|d   dk(  r|j                  d	      rd
|  |d<   t        |      S )zRetorna status atual de um job.r      Job não encontrado.  ry   rz   r{   )ry   rz   r{   	concluidor|   z/api/download/	video_url)r   r   copyr;   )rx   jobresults      r6   
status_jobr   P  s     T 678#==
v,


Ch-%
OF 8}#(= .vh7{6?r>   z/api/download/<job_id>c           	         | t         vrt        ddi      dfS t         |    }|d   dk7  s|j                  d      st        ddi      dfS |d   }t        |      j	                         st        ddi      dfS t        |d	d
d| dd  d      S )u   Baixa o vídeo processado.r   r   r   ry   r   r|   u   Vídeo ainda não disponível.u$   Arquivo não encontrado no servidor.z	video/mp4Treel_N   r   )mimetypeas_attachmentdownload_name)r   r   r;   r   r+   r	   )rx   r   r|   s      r6   download_videor   c  s     T 678#==
v,C
8}#377<+@ @ABCGG\"J
""$ FGH#MMfRaj\.	 r>   z/api/musicasc                     t               } g }| j                  dg       D ]x  }t        |j                  dd      z  }|j                         s-|j	                  |d   |d   |d   |d   |j                  dd      |j                  d	d      d
|d    dd       z t        || j                  dg       | j                  dd      | j                  dd      d      S )zCRetorna a lista de trilhas do manifest enriquecida com preview_url.r$   r   re   r:   nomemoodduracaofontecreditos/api/musicas//preview)r:   r   r   r   r   r   preview_urlr%   licenca)r$   r%   r   r   )r7   r;   r   r+   appendr   )r!   r$   r<   r   s       r6   listar_musicasr   }  s     DFXXh# aeeIr22~~D'fIfI|UU7B'j"-*1T7)8<
 		 '2&'2&88Ir*	  r>   z/api/musicas/<track_id>/previewc                 `   d}d}|  d}| j                  d      rt        |       }|st        d       t        |j	                  dd            }|j                         st        d       |}|j                  j                         j                  d      }dd	d
ddd}|j	                  |d      }|  d| }nAt        |       }|st        d       t        |d   z  }	|	j                         st        d       |	}t        t        |      |dd|      }
d|
j                  d<   d|
j                  d<   |
S )uK   Serve o MP3 com suporte a Range (necessário pro <audio> HTML5 fazer seek).Nz
audio/mpegr   rM   r   rP   re   .z	audio/mp4z	audio/aacz	audio/wavz	audio/ogg)mp3m4aaacwavoggzapplication/octet-streamr   TF)r   conditionalr   r   zpublic, max-age=3600zCache-ControlbyteszAccept-Ranges)rN   rO   r
   r   r;   r+   r   r   lstripr=   r   r	   r   headers)r8   r   r   r   r   rv   r   mime_mapr<   r   resps              r6   preview_musicar    s3     GHj%M;'8$#J#$xxz#Jhhnn%%c*
 <<%?@#*AcU+"#J1Y<'{{}#J G#D %;DLL!$+DLL!Kr>   z/api/musicas/uploadc                  >   dt         j                  vrt        ddi      dfS t         j                  d   } | r| j                  st        ddi      dfS t	        | j                        xs d}t        |      j                  j                         }|t        vr5t        dd|xs d d	d
j                  t        t                     i      dfS t         j                  }|r&|t        dz   kD  rt        ddt        dz   di      dfS t        j                         j                  }d| }t         | | z  }	 d}t#        |d      5 }	 | j$                  j'                  d      }	|	snm|t)        |	      z  }|t        kD  rD|j+                          	 |j-                          t        ddt        dz   di      dfcddd       S |j1                  |	       	 ddd       t5        t7        |            }|dk  r 	 |j-                          t        ddi      dfS |t        |      j8                  xs ddt;        |d      |j<                  t7        |      t?        j@                         jC                         d}tD        5  tG               }|||<   	 t#        tH        dd      5 }tK        jL                  ||dd        ddd       ddd       t        ||d"   d|d#   d$| d%d&      S # t.        $ r Y Ow xY w# 1 sw Y   xY w# t.        $ rM}
	 |j3                         r|j-                          n# t.        $ r Y nw xY wt        dd|
 i      dfcY d}
~
S d}
~
ww xY w# t.        $ r Y ]w xY w# 1 sw Y   xY w# t.        $ r}
tO        d!|
        Y d}
~
d}
~
ww xY w# 1 sw Y   xY w)'u   
    Aceita upload de uma trilha sonora do usuário.
    multipart/form-data, campo 'arquivo'.
    Salva em music/uploads/<uuid>.<ext> e registra em index.json.
    Retorna metadata pro frontend.
    r   r   u   Campo 'arquivo' não enviado.r   u   Arquivo inválido.r   r   u   (sem extensão)z. Aceitos: z, i    zArquivo excede o limite de i   zMB.i  rM   r   wbi   NzFalha ao salvar arquivo: r   u5   Não foi possível ler o arquivo como áudio válido.u   Minha músicauploadedrD   )r:   r   r   r   r   rP   rd   rC   r'   r(   FrE   rH   r   r   r   r   )r:   r   r   r   r   )(r   r   r   r   r   r   r   r   UPLOAD_EXTS_PERMITIDASjoinsortedcontent_lengthUPLOAD_MAX_BYTESr   r   hexrh   r/   streamreadlencloserm   r2   writer+   r_   r   stemroundrk   r   rf   r   rI   rA   r@   r0   rJ   r3   )r4   nome_originalr   cluid	upload_iddesttotaloutchunkr5   r   rt   idxf_outs                  r6   upload_musicar     s    % ?@A3FFi AAJJ 456;;#AJJ/;8M
}

$
$
*
*
,C
((.s/G6G.H I  $		&1G*H IJL
   	 
		B	b#d**"=>NS\>]=^^a bcdfiii
**,

CC5!IC5&DG$ 	!i0U#++IIK #F.IJZ_hJiIjjm,n#opruu	! 	! 		%   		!, s4y)G!|	KKM  WXY[^^^ ]#((;O!$99D	oo'113D 
 9!#I	9mS7; Du		#u5CD	9 V	?&yk:  S % 	! 	!  G	{{} 		";A3 ?@A3FFG  		&D D 	93A3788	99 9s   4K: AK-
KK-3	K: =K-K: 2M 5NM/M#2M/	K*&K-)K**K--K72K: :	M L%$M%	L1.M0L11MMM	M M #M,	(M//	N8NNNNN__main__z<============================================================z!Video Editor Backend - Porta 5054zTemp dir:   zOutput dir: zMusic dir:  zMusic tracks no manifest: r$   z0.0.0.0i  F)hostportdebugthreaded)N)I__doc__r0   osrX   r   timer   r   r   pathlibr   flaskr   r   r   r	   r
   r   
flask_corsr   werkzeug.utilsr   	processorr   __file__parentr   r   r   rh   r@   r*   r	  r  rg   r   mkdir__name__appr   dict__annotations__r.   r7   r   r=   LockrI   rA   rK   rO   r]   r_   r   rw   r   r   _rmr3   r2   _erouter   r   r   r   r   r  r   manifestr  r;   rY   r   r>   r6   <module>r:     s   0  	     (  E E  * %
 >  6)(^""X-
N!!G+	)#l*O+ B #  
J	;	7 )AGGT4G() Ho S#D 1 d t,# # 3 4$;  	 	T 	9d 9t 9C D4K 3 5 "+ +d
0C 
0 
0 
0 
0Y\ 
0  6
!
#C
+C50STU =5'*Q +Q fX.q' /q'h 0s  1$ #eW53  62 >E7+ ,2 ,ug>+S + ?+\  6(3\ 4\D z	(O	
-.	L

#$	L
%&	L
$%H	&s8<<"+E'F&G
HI	(OGGUTGB u
  6	/t
4556s   J J.J))J.