Update app.py
Browse files
app.py
CHANGED
@@ -253,6 +253,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
253 |
filtro_complex += ",vignette"
|
254 |
filtro_complex += "[blur];[1:v]" + ",".join(filtros_main) + "[zoomed];[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
|
255 |
|
|
|
256 |
if ativar_texto and texto_personalizado.strip():
|
257 |
y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
|
258 |
enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
|
@@ -279,8 +280,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
279 |
], check=True, stderr=subprocess.PIPE)
|
280 |
|
281 |
progresso.progress(70)
|
282 |
-
|
283 |
-
# Tutorial no meio (se enviado)
|
284 |
video_final_raw = video_acelerado
|
285 |
if video_tutorial:
|
286 |
dur_proc = subprocess.run([
|
@@ -291,20 +291,31 @@ if st.button("Gerar Vídeo(s)"):
|
|
291 |
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5)
|
292 |
part1 = os.path.join(temp_dir, f"part1_{n}.mp4")
|
293 |
part2 = os.path.join(temp_dir, f"part2_{n}.mp4")
|
294 |
-
subprocess.run([
|
295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
296 |
final_txt = os.path.join(temp_dir, f"final_{n}.txt")
|
297 |
with open(final_txt, "w") as f:
|
298 |
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n")
|
299 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
300 |
-
subprocess.run([
|
|
|
|
|
|
|
|
|
301 |
|
302 |
-
# 🎵
|
303 |
dur_proc = subprocess.run([
|
304 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
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 |
if musica:
|
309 |
musica_path = os.path.join(temp_dir, "musica_original.mp3")
|
310 |
with open(musica_path, "wb") as f:
|
@@ -324,14 +335,12 @@ if st.button("Gerar Vídeo(s)"):
|
|
324 |
else:
|
325 |
final_name = f"video_final_{n}_{int(time.time())}.mp4"
|
326 |
shutil.copy(video_final_raw, final_name)
|
327 |
-
|
328 |
# 🔒 APLICAR ANTI-FLOP NO FINAL
|
329 |
if ativar_antiflop:
|
330 |
st.info(f"🔒 Aplicando Anti-Flop no vídeo {n+1}...")
|
331 |
|
332 |
-
# ➡️ Anti-Flop filtros:
|
333 |
-
encoder_name = f"{fingerprint}Cam_{random.randint(1000,9999)}"
|
334 |
antiflop_out = f"antiflop_{n}_{int(time.time())}.mp4"
|
|
|
335 |
|
336 |
vf_af = (
|
337 |
f"scale=iw*{zoom_af}:ih*{zoom_af},"
|
@@ -339,7 +348,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
339 |
f"eq=brightness={brilho_af}:contrast={contraste_af}:saturation={saturacao_af},"
|
340 |
f"noise=alls={ruido_af}:allf=t,"
|
341 |
f"rotate={rotacao_af}*PI/180:[email protected],"
|
342 |
-
"scale=trunc(iw/2)*2:trunc(ih/2)*2"
|
343 |
)
|
344 |
if usar_marca:
|
345 |
vf_af += f",drawtext=text='{encoder_name}':[email protected]:x=10:y=10:fontsize=24"
|
@@ -383,22 +392,25 @@ if st.button("Gerar Vídeo(s)"):
|
|
383 |
with open(final_name, "rb") as f:
|
384 |
st.download_button(f"📥 Baixar vídeo {n+1}", f, file_name=final_name)
|
385 |
|
|
|
386 |
progresso.progress(100)
|
387 |
st.success("✅ Todos os vídeos foram gerados com sucesso!")
|
388 |
|
389 |
-
except subprocess.CalledProcessError as e:
|
390 |
-
|
391 |
-
|
392 |
-
finally:
|
393 |
-
shutil.rmtree(temp_dir)
|
394 |
|
395 |
-
|
|
|
|
|
396 |
if videos_gerados:
|
397 |
st.markdown("### 📥 Baixar Todos os Vídeos")
|
|
|
|
|
398 |
download_links = ""
|
399 |
for v in videos_gerados:
|
400 |
download_links += f"<a href='{v}' download='{v}'></a>\n"
|
401 |
|
|
|
402 |
st.markdown(
|
403 |
f"""
|
404 |
<button onclick="baixarTodos()">📥 Baixar Todos</button>
|
|
|
253 |
filtro_complex += ",vignette"
|
254 |
filtro_complex += "[blur];[1:v]" + ",".join(filtros_main) + "[zoomed];[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
|
255 |
|
256 |
+
# Inserir texto se ativado
|
257 |
if ativar_texto and texto_personalizado.strip():
|
258 |
y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
|
259 |
enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
|
|
|
280 |
], check=True, stderr=subprocess.PIPE)
|
281 |
|
282 |
progresso.progress(70)
|
283 |
+
# Inserir tutorial no meio (se enviado)
|
|
|
284 |
video_final_raw = video_acelerado
|
285 |
if video_tutorial:
|
286 |
dur_proc = subprocess.run([
|
|
|
291 |
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5)
|
292 |
part1 = os.path.join(temp_dir, f"part1_{n}.mp4")
|
293 |
part2 = os.path.join(temp_dir, f"part2_{n}.mp4")
|
294 |
+
subprocess.run([
|
295 |
+
"ffmpeg", "-i", video_acelerado, "-ss", "0", "-t", str(pt),
|
296 |
+
"-c:v", "libx264", "-preset", "ultrafast", part1
|
297 |
+
], check=True, stderr=subprocess.PIPE)
|
298 |
+
subprocess.run([
|
299 |
+
"ffmpeg", "-i", video_acelerado, "-ss", str(pt),
|
300 |
+
"-c:v", "libx264", "-preset", "ultrafast", part2
|
301 |
+
], check=True, stderr=subprocess.PIPE)
|
302 |
final_txt = os.path.join(temp_dir, f"final_{n}.txt")
|
303 |
with open(final_txt, "w") as f:
|
304 |
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n")
|
305 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
306 |
+
subprocess.run([
|
307 |
+
"ffmpeg", "-f", "concat", "-safe", "0", "-i", final_txt,
|
308 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
309 |
+
video_final_raw
|
310 |
+
], check=True, stderr=subprocess.PIPE)
|
311 |
|
312 |
+
# 🎵 Inserir música no final
|
313 |
dur_proc = subprocess.run([
|
314 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
315 |
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
|
316 |
], stdout=subprocess.PIPE)
|
317 |
dur_video_real = float(dur_proc.stdout.decode().strip())
|
318 |
+
|
319 |
if musica:
|
320 |
musica_path = os.path.join(temp_dir, "musica_original.mp3")
|
321 |
with open(musica_path, "wb") as f:
|
|
|
335 |
else:
|
336 |
final_name = f"video_final_{n}_{int(time.time())}.mp4"
|
337 |
shutil.copy(video_final_raw, final_name)
|
|
|
338 |
# 🔒 APLICAR ANTI-FLOP NO FINAL
|
339 |
if ativar_antiflop:
|
340 |
st.info(f"🔒 Aplicando Anti-Flop no vídeo {n+1}...")
|
341 |
|
|
|
|
|
342 |
antiflop_out = f"antiflop_{n}_{int(time.time())}.mp4"
|
343 |
+
encoder_name = f"{fingerprint}Cam_{random.randint(1000,9999)}"
|
344 |
|
345 |
vf_af = (
|
346 |
f"scale=iw*{zoom_af}:ih*{zoom_af},"
|
|
|
348 |
f"eq=brightness={brilho_af}:contrast={contraste_af}:saturation={saturacao_af},"
|
349 |
f"noise=alls={ruido_af}:allf=t,"
|
350 |
f"rotate={rotacao_af}*PI/180:[email protected],"
|
351 |
+
"scale=trunc(iw/2)*2:trunc(ih/2)*2"
|
352 |
)
|
353 |
if usar_marca:
|
354 |
vf_af += f",drawtext=text='{encoder_name}':[email protected]:x=10:y=10:fontsize=24"
|
|
|
392 |
with open(final_name, "rb") as f:
|
393 |
st.download_button(f"📥 Baixar vídeo {n+1}", f, file_name=final_name)
|
394 |
|
395 |
+
# Fim do for n in range(num_videos_finais)
|
396 |
progresso.progress(100)
|
397 |
st.success("✅ Todos os vídeos foram gerados com sucesso!")
|
398 |
|
399 |
+
except subprocess.CalledProcessError as e:
|
400 |
+
st.error(f"❌ Erro ao gerar vídeo:\n\n{e.stderr.decode(errors='ignore')}")
|
|
|
|
|
|
|
401 |
|
402 |
+
finally:
|
403 |
+
shutil.rmtree(temp_dir)
|
404 |
+
# Fora do try-finally: Botão "Baixar Todos os Vídeos"
|
405 |
if videos_gerados:
|
406 |
st.markdown("### 📥 Baixar Todos os Vídeos")
|
407 |
+
|
408 |
+
# Criar links ocultos para todos os vídeos
|
409 |
download_links = ""
|
410 |
for v in videos_gerados:
|
411 |
download_links += f"<a href='{v}' download='{v}'></a>\n"
|
412 |
|
413 |
+
# Botão com JavaScript para clicar em todos os links
|
414 |
st.markdown(
|
415 |
f"""
|
416 |
<button onclick="baixarTodos()">📥 Baixar Todos</button>
|