Update app.py
Browse files
app.py
CHANGED
@@ -6,7 +6,7 @@ import tempfile
|
|
6 |
import shutil
|
7 |
import time
|
8 |
|
9 |
-
st.set_page_config(page_title="TikTok Video Generator - PRO", layout="centered")
|
10 |
st.title("🎥 TikTok Video Generator - PRO + Anti-Flop")
|
11 |
|
12 |
st.markdown("Envie seus vídeos e gere conteúdo com efeitos, zoom, texto, música, filtros e antiflop!")
|
@@ -115,6 +115,9 @@ if ativar_antiflop:
|
|
115 |
# 💾 Download Automático
|
116 |
st.write("### 💾 Download Automático")
|
117 |
download_automatico = st.checkbox("Ativar Download Automático", value=False)
|
|
|
|
|
|
|
118 |
# Botão principal
|
119 |
if st.button("Gerar Vídeo(s)"):
|
120 |
if not cortes:
|
@@ -302,70 +305,96 @@ if st.button("Gerar Vídeo(s)"):
|
|
302 |
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
|
303 |
], stdout=subprocess.PIPE)
|
304 |
dur_video_real = float(dur_proc.stdout.decode().strip())
|
|
|
305 |
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
"
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
else:
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import shutil
|
7 |
import time
|
8 |
|
9 |
+
st.set_page_config(page_title="TikTok Video Generator - PRO + Anti-Flop", layout="centered")
|
10 |
st.title("🎥 TikTok Video Generator - PRO + Anti-Flop")
|
11 |
|
12 |
st.markdown("Envie seus vídeos e gere conteúdo com efeitos, zoom, texto, música, filtros e antiflop!")
|
|
|
115 |
# 💾 Download Automático
|
116 |
st.write("### 💾 Download Automático")
|
117 |
download_automatico = st.checkbox("Ativar Download Automático", value=False)
|
118 |
+
# Lista para armazenar os vídeos gerados
|
119 |
+
videos_gerados = []
|
120 |
+
|
121 |
# Botão principal
|
122 |
if st.button("Gerar Vídeo(s)"):
|
123 |
if not cortes:
|
|
|
305 |
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
|
306 |
], stdout=subprocess.PIPE)
|
307 |
dur_video_real = float(dur_proc.stdout.decode().strip())
|
308 |
+
progresso.progress(35 + n * 5)
|
309 |
|
310 |
+
# Filtros no vídeo principal (zoom, espelho, borda, etc.)
|
311 |
+
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
|
312 |
+
if zoom != 1.0:
|
313 |
+
filtros_main.append(f"scale=iw*{zoom}:ih*{zoom}")
|
314 |
+
filtros_main.append(f"setpts=PTS/{velocidade_cortes}")
|
315 |
+
if ativar_espelhar:
|
316 |
+
filtros_main.append("hflip")
|
317 |
+
if remover_borda and tamanho_borda > 0:
|
318 |
+
filtros_main.append(f"crop=in_w-{tamanho_borda*2}:in_h-{tamanho_borda*2}")
|
319 |
+
if ativar_filtro_cor:
|
320 |
+
filtros_main.append("eq=contrast=1.1:saturation=1.2")
|
321 |
+
if ativar_borda_personalizada:
|
322 |
+
cor_ffmpeg = f"0x{cor_borda.lstrip('#')}FF"
|
323 |
+
anim_map = {
|
324 |
+
"Nenhuma": "",
|
325 |
+
"Borda Pulsante": "enable='lt(mod(t,1),0.5)'",
|
326 |
+
"Cor Animada": "enable='lt(mod(t,1),0.5)'",
|
327 |
+
"Neon": "enable='lt(mod(t,0.5),0.25)'",
|
328 |
+
"Ondulada": "enable='gt(sin(t*3.14),0)'"
|
329 |
+
}
|
330 |
+
drawbox = f"drawbox=x=0:y=0:w=iw:h=ih:color={cor_ffmpeg}:t=5"
|
331 |
+
if anim_map[animacao_borda]:
|
332 |
+
drawbox += f":{anim_map[animacao_borda]}"
|
333 |
+
filtros_main.append(drawbox)
|
334 |
+
|
335 |
+
# Fundo com filtros
|
336 |
+
filtro_complex = f"[0:v]scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280"
|
337 |
+
if ativar_blur_fundo:
|
338 |
+
filtro_complex += f",boxblur={blur_strength}:1"
|
339 |
+
if ativar_sepia:
|
340 |
+
filtro_complex += ",colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131"
|
341 |
+
if ativar_granulado:
|
342 |
+
filtro_complex += ",noise=alls=20:allf=t+u"
|
343 |
+
if ativar_pb:
|
344 |
+
filtro_complex += ",hue=s=0.3"
|
345 |
+
if ativar_vignette:
|
346 |
+
filtro_complex += ",vignette"
|
347 |
+
filtro_complex += "[blur];[1:v]" + ",".join(filtros_main) + "[zoomed];[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
|
348 |
+
|
349 |
+
if ativar_texto and texto_personalizado.strip():
|
350 |
+
y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
|
351 |
+
enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
|
352 |
+
texto_clean = texto_personalizado.replace(":", "\\:").replace("'", "\\'")
|
353 |
+
filtro_complex += f";[base]drawtext=text='{texto_clean}':fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontcolor={cor_texto}:fontsize={tamanho_texto}:shadowcolor={cor_sombra}:shadowx=3:shadowy=3:x=(w-text_w)/2:y={y_pos}{enable}[final]"
|
354 |
else:
|
355 |
+
filtro_complex += ";[base]null[final]"
|
356 |
+
|
357 |
+
video_editado = os.path.join(temp_dir, f"video_editado_{n}.mp4")
|
358 |
+
subprocess.run([
|
359 |
+
"ffmpeg", "-i", fundo_cortado, "-i", video_raw,
|
360 |
+
"-filter_complex", filtro_complex,
|
361 |
+
"-map", "[final]", "-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
362 |
+
video_editado
|
363 |
+
], check=True, stderr=subprocess.PIPE)
|
364 |
+
|
365 |
+
# Acelerar vídeo final (sem áudio)
|
366 |
+
video_acelerado = os.path.join(temp_dir, f"video_acelerado_{n}.mp4")
|
367 |
+
subprocess.run([
|
368 |
+
"ffmpeg", "-y", "-i", video_editado, "-an",
|
369 |
+
"-filter:v", f"setpts=PTS/{velocidade_final}",
|
370 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
371 |
+
video_acelerado
|
372 |
+
], check=True, stderr=subprocess.PIPE)
|
373 |
+
|
374 |
+
progresso.progress(70)
|
375 |
+
|
376 |
+
# Tutorial no meio (se enviado)
|
377 |
+
video_final_raw = video_acelerado
|
378 |
+
if video_tutorial:
|
379 |
+
dur_proc = subprocess.run([
|
380 |
+
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
381 |
+
"-of", "default=noprint_wrappers=1:nokey=1", video_acelerado
|
382 |
+
], stdout=subprocess.PIPE)
|
383 |
+
dur_f = float(dur_proc.stdout.decode().strip())
|
384 |
+
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5)
|
385 |
+
part1 = os.path.join(temp_dir, f"part1_{n}.mp4")
|
386 |
+
part2 = os.path.join(temp_dir, f"part2_{n}.mp4")
|
387 |
+
subprocess.run(["ffmpeg", "-i", video_acelerado, "-ss", "0", "-t", str(pt), "-c:v", "libx264", "-preset", "ultrafast", part1], check=True, stderr=subprocess.PIPE)
|
388 |
+
subprocess.run(["ffmpeg", "-i", video_acelerado, "-ss", str(pt), "-c:v", "libx264", "-preset", "ultrafast", part2], check=True, stderr=subprocess.PIPE)
|
389 |
+
final_txt = os.path.join(temp_dir, f"final_{n}.txt")
|
390 |
+
with open(final_txt, "w") as f:
|
391 |
+
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n")
|
392 |
+
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
393 |
+
subprocess.run(["ffmpeg", "-f", "concat", "-safe", "0", "-i", final_txt, "-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), video_final_raw], check=True, stderr=subprocess.PIPE)
|
394 |
+
|
395 |
+
# 🎵 Música final
|
396 |
+
dur_proc = subprocess.run([
|
397 |
+
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
398 |
+
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
|
399 |
+
], stdout=subprocess.PIPE)
|
400 |
+
dur_video_real = float(dur_proc.stdout.decode().strip())
|