Update app.py
Browse files
app.py
CHANGED
|
@@ -22,7 +22,6 @@ for cat in CATEGORIAS:
|
|
| 22 |
os.makedirs(os.path.join(BASE_ASSETS, cat, "tutoriais"), exist_ok=True)
|
| 23 |
os.makedirs(os.path.join(BASE_SALVOS, cat), exist_ok=True)
|
| 24 |
|
| 25 |
-
# ======================== GERADOR DE VÍDEO ========================
|
| 26 |
if pagina == "🎬 Gerador de Vídeo":
|
| 27 |
st.title("🎥 TikTok Video Generator - PRO")
|
| 28 |
|
|
@@ -58,7 +57,7 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 58 |
zoom = st.slider("Zoom", 1.0, 2.0, 1.0, 0.1)
|
| 59 |
blur_strength = st.slider("Blur no fundo", 1, 50, 10)
|
| 60 |
velocidade_final = st.slider("Velocidade final", 0.5, 2.0, 1.0, 0.1)
|
| 61 |
-
crf_value =
|
| 62 |
|
| 63 |
st.write("## Texto no Vídeo")
|
| 64 |
ativar_texto = st.checkbox("Ativar texto")
|
|
@@ -99,8 +98,6 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 99 |
animacao_borda = st.selectbox("Animação", ["Nenhuma", "Pulsante", "Cor Animada", "Neon", "Ondulada"])
|
| 100 |
|
| 101 |
salvar_no_gerenciador = st.checkbox("Salvar no gerenciador de arquivos")
|
| 102 |
-
|
| 103 |
-
# ======================== GERAÇÃO DOS VÍDEOS ========================
|
| 104 |
if st.button("Gerar Vídeo(s)", key="gerar_video_usuario"):
|
| 105 |
with st.spinner("🎬 Processando..."):
|
| 106 |
progresso = st.progress(0)
|
|
@@ -112,10 +109,7 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 112 |
try:
|
| 113 |
cortes_names = cortes_files.copy()
|
| 114 |
tutorials_salvos = tutoriais_files.copy()
|
| 115 |
-
|
| 116 |
-
# 🎵 embaralhar músicas antes do loop
|
| 117 |
musicas_salvas = musicas_files.copy()
|
| 118 |
-
random.shuffle(musicas_salvas)
|
| 119 |
|
| 120 |
for n in range(num_videos_finais):
|
| 121 |
total_etapas = 6
|
|
@@ -124,7 +118,6 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 124 |
cortes_prontos = []
|
| 125 |
cortes_usados = []
|
| 126 |
|
| 127 |
-
# configs aleatórias ou fixas
|
| 128 |
if aleatorizar:
|
| 129 |
duracao_corte_min_n = random.randint(1, 5)
|
| 130 |
duracao_corte_max_n = random.randint(duracao_corte_min_n + 1, 8)
|
|
@@ -145,10 +138,41 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 145 |
tamanho_borda_n = tamanho_borda
|
| 146 |
ativar_blur_fundo_n = ativar_blur_fundo
|
| 147 |
ativar_espelhar_n = ativar_espelhar
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
etapa_atual += 1
|
| 149 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 150 |
|
| 151 |
-
#
|
| 152 |
tentativas = 0
|
| 153 |
while tempo_total < duracao_final and tentativas < 100:
|
| 154 |
tentativas += 1
|
|
@@ -173,7 +197,7 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 173 |
subprocess.run([
|
| 174 |
"ffmpeg", "-ss", str(ini), "-i", c, "-t", str(duracao_aleatoria),
|
| 175 |
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
| 176 |
-
"-an", "-c:v", "libx264", "-preset", "
|
| 177 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 178 |
cortes_prontos.append(out)
|
| 179 |
cortes_usados.append((c, ini, duracao_aleatoria))
|
|
@@ -183,10 +207,6 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 183 |
except:
|
| 184 |
continue
|
| 185 |
|
| 186 |
-
if not cortes_prontos:
|
| 187 |
-
st.error("❌ Nenhum corte válido encontrado para gerar o vídeo.")
|
| 188 |
-
continue
|
| 189 |
-
|
| 190 |
lista = os.path.join(temp_dir, f"lista_{n}.txt")
|
| 191 |
with open(lista, "w") as f:
|
| 192 |
for c in cortes_prontos:
|
|
@@ -195,12 +215,13 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 195 |
video_raw = os.path.join(temp_dir, f"video_raw_{n}.mp4")
|
| 196 |
subprocess.run([
|
| 197 |
"ffmpeg", "-f", "concat", "-safe", "0", "-i", lista,
|
| 198 |
-
"-c:v", "libx264", "-preset", "
|
| 199 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
| 200 |
etapa_atual += 1
|
| 201 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 202 |
|
| 203 |
-
#
|
| 204 |
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
|
| 205 |
if zoom_n != 1.0:
|
| 206 |
filtros_main.append(f"scale=iw*{zoom_n}:ih*{zoom_n}")
|
|
@@ -264,35 +285,26 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 264 |
"ffmpeg", "-i", fundo_convertido, "-i", video_raw,
|
| 265 |
"-filter_complex", filtro_complex,
|
| 266 |
"-map", "[final]",
|
| 267 |
-
"-c:v", "libx264", "-preset", "
|
| 268 |
video_editado
|
| 269 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 270 |
|
| 271 |
etapa_atual += 1
|
| 272 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 273 |
|
| 274 |
-
#
|
| 275 |
-
video_editado_speed = os.path.join(temp_dir, f"video_editado_speed_{n}.mp4")
|
| 276 |
-
subprocess.run([
|
| 277 |
-
"ffmpeg", "-y", "-i", video_editado, "-an",
|
| 278 |
-
"-filter:v", f"setpts=PTS/{velocidade_final_n}",
|
| 279 |
-
"-c:v", "libx264", "-preset", "veryfast", "-crf", str(18),
|
| 280 |
-
video_editado_speed
|
| 281 |
-
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 282 |
-
|
| 283 |
-
# ======================== ETAPA 4 - Tutorial ========================
|
| 284 |
if usar_tutorial and tutorials_salvos:
|
| 285 |
tutorial_path = random.choice(tutorials_salvos)
|
| 286 |
tutorial_mp4 = os.path.join(temp_dir, f"tutorial_conv_{n}.mp4")
|
| 287 |
subprocess.run([
|
| 288 |
"ffmpeg", "-i", tutorial_path, "-vf", "scale=720:1280,fps=30",
|
| 289 |
-
"-c:v", "libx264", "-preset", "
|
| 290 |
"-y", tutorial_mp4
|
| 291 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 292 |
|
| 293 |
dur_proc = subprocess.run([
|
| 294 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
| 295 |
-
"-of", "default=noprint_wrappers=1:nokey=1",
|
| 296 |
], stdout=subprocess.PIPE)
|
| 297 |
dur_f = float(dur_proc.stdout.decode().strip() or 0)
|
| 298 |
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5)
|
|
@@ -300,11 +312,11 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 300 |
part1 = os.path.join(temp_dir, f"part1_{n}.mp4")
|
| 301 |
part2 = os.path.join(temp_dir, f"part2_{n}.mp4")
|
| 302 |
|
| 303 |
-
subprocess.run(["ffmpeg", "-i",
|
| 304 |
-
"-c:v", "libx264", "-preset", "
|
| 305 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 306 |
-
subprocess.run(["ffmpeg", "-i",
|
| 307 |
-
"-c:v", "libx264", "-preset", "
|
| 308 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 309 |
|
| 310 |
final_txt = os.path.join(temp_dir, f"final_{n}.txt")
|
|
@@ -313,17 +325,25 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 313 |
|
| 314 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
| 315 |
subprocess.run(["ffmpeg", "-f", "concat", "-safe", "0", "-i", final_txt,
|
| 316 |
-
"-c:v", "libx264", "-preset", "
|
| 317 |
video_final_raw], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 318 |
else:
|
| 319 |
-
video_final_raw =
|
| 320 |
etapa_atual += 1
|
| 321 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 322 |
|
| 323 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
dur_final_str = subprocess.run([
|
| 325 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
| 326 |
-
"-of", "default=noprint_wrappers=1:nokey=1",
|
| 327 |
], stdout=subprocess.PIPE).stdout.decode().strip()
|
| 328 |
if not dur_final_str:
|
| 329 |
continue
|
|
@@ -331,18 +351,18 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 331 |
|
| 332 |
final_name = f"video_final_{n}_{int(time.time())}.mp4"
|
| 333 |
if usar_musica and musicas_salvas:
|
| 334 |
-
musica_path =
|
| 335 |
musica_cortada = os.path.join(temp_dir, f"musica_{n}.aac")
|
| 336 |
subprocess.run(["ffmpeg", "-i", musica_path, "-ss", "0", "-t", str(dur_video_real),
|
| 337 |
"-vn", "-acodec", "aac", "-y", musica_cortada],
|
| 338 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 339 |
|
| 340 |
-
subprocess.run(["ffmpeg", "-i",
|
| 341 |
"-map", "0:v:0", "-map", "1:a:0",
|
| 342 |
"-c:v", "copy", "-c:a", "aac", "-shortest", final_name],
|
| 343 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 344 |
else:
|
| 345 |
-
shutil.copy(
|
| 346 |
|
| 347 |
if salvar_no_gerenciador:
|
| 348 |
destino = os.path.join(BASE_SALVOS, categoria_selecionada, final_name)
|
|
@@ -357,6 +377,7 @@ if pagina == "🎬 Gerador de Vídeo":
|
|
| 357 |
st.error(f"❌ Erro inesperado: {str(e)}")
|
| 358 |
finally:
|
| 359 |
shutil.rmtree(temp_dir)
|
|
|
|
| 360 |
# ======================== GERENCIADOR ========================
|
| 361 |
elif pagina == "📂 Gerenciador de Arquivos":
|
| 362 |
st.header("📂 Vídeos Salvos por Categoria")
|
|
@@ -434,7 +455,7 @@ elif pagina == "🔐 Admin":
|
|
| 434 |
arquivos = st.file_uploader("Enviar arquivos", accept_multiple_files=True, key=f"{cat}_{tipo}")
|
| 435 |
|
| 436 |
if arquivos:
|
| 437 |
-
with st.spinner("⏳
|
| 438 |
for f in arquivos:
|
| 439 |
extensao = os.path.splitext(f.name)[1].lower()
|
| 440 |
nome_aleatorio = f"{random.randint(1000000, 9999999)}{extensao}"
|
|
@@ -443,9 +464,20 @@ elif pagina == "🔐 Admin":
|
|
| 443 |
out.write(f.read())
|
| 444 |
|
| 445 |
final = os.path.join(path, nome_aleatorio)
|
| 446 |
-
shutil.move(temp, final) # ✅ agora só move, sem padronizar
|
| 447 |
|
| 448 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
|
| 450 |
st.write("Arquivos existentes:")
|
| 451 |
for arq in os.listdir(path):
|
|
@@ -455,4 +487,4 @@ elif pagina == "🔐 Admin":
|
|
| 455 |
with col2:
|
| 456 |
if st.button("🗑", key=f"{tipo}_{cat}_{arq}"):
|
| 457 |
os.remove(os.path.join(path, arq))
|
| 458 |
-
st.rerun()
|
|
|
|
| 22 |
os.makedirs(os.path.join(BASE_ASSETS, cat, "tutoriais"), exist_ok=True)
|
| 23 |
os.makedirs(os.path.join(BASE_SALVOS, cat), exist_ok=True)
|
| 24 |
|
|
|
|
| 25 |
if pagina == "🎬 Gerador de Vídeo":
|
| 26 |
st.title("🎥 TikTok Video Generator - PRO")
|
| 27 |
|
|
|
|
| 57 |
zoom = st.slider("Zoom", 1.0, 2.0, 1.0, 0.1)
|
| 58 |
blur_strength = st.slider("Blur no fundo", 1, 50, 10)
|
| 59 |
velocidade_final = st.slider("Velocidade final", 0.5, 2.0, 1.0, 0.1)
|
| 60 |
+
crf_value = st.slider("Qualidade CRF", 18, 30, 18)
|
| 61 |
|
| 62 |
st.write("## Texto no Vídeo")
|
| 63 |
ativar_texto = st.checkbox("Ativar texto")
|
|
|
|
| 98 |
animacao_borda = st.selectbox("Animação", ["Nenhuma", "Pulsante", "Cor Animada", "Neon", "Ondulada"])
|
| 99 |
|
| 100 |
salvar_no_gerenciador = st.checkbox("Salvar no gerenciador de arquivos")
|
|
|
|
|
|
|
| 101 |
if st.button("Gerar Vídeo(s)", key="gerar_video_usuario"):
|
| 102 |
with st.spinner("🎬 Processando..."):
|
| 103 |
progresso = st.progress(0)
|
|
|
|
| 109 |
try:
|
| 110 |
cortes_names = cortes_files.copy()
|
| 111 |
tutorials_salvos = tutoriais_files.copy()
|
|
|
|
|
|
|
| 112 |
musicas_salvas = musicas_files.copy()
|
|
|
|
| 113 |
|
| 114 |
for n in range(num_videos_finais):
|
| 115 |
total_etapas = 6
|
|
|
|
| 118 |
cortes_prontos = []
|
| 119 |
cortes_usados = []
|
| 120 |
|
|
|
|
| 121 |
if aleatorizar:
|
| 122 |
duracao_corte_min_n = random.randint(1, 5)
|
| 123 |
duracao_corte_max_n = random.randint(duracao_corte_min_n + 1, 8)
|
|
|
|
| 138 |
tamanho_borda_n = tamanho_borda
|
| 139 |
ativar_blur_fundo_n = ativar_blur_fundo
|
| 140 |
ativar_espelhar_n = ativar_espelhar
|
| 141 |
+
|
| 142 |
+
# Etapa 1 - Fundo com ponto aleatório
|
| 143 |
+
if usar_fundo:
|
| 144 |
+
fundo_origem = random.choice(cortes_names)
|
| 145 |
+
fundo_convertido = os.path.join(temp_dir, f"fundo_convertido_{n}.mp4")
|
| 146 |
+
dur_proc = subprocess.run([
|
| 147 |
+
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
| 148 |
+
"-of", "default=noprint_wrappers=1:nokey=1", fundo_origem
|
| 149 |
+
], stdout=subprocess.PIPE)
|
| 150 |
+
try:
|
| 151 |
+
dur_total = float(dur_proc.stdout.decode().strip())
|
| 152 |
+
ponto_inicio = random.uniform(0, max(0, dur_total - duracao_final))
|
| 153 |
+
except:
|
| 154 |
+
ponto_inicio = 0.0
|
| 155 |
+
|
| 156 |
+
subprocess.run([
|
| 157 |
+
"ffmpeg", "-ss", str(ponto_inicio), "-i", fundo_origem,
|
| 158 |
+
"-t", str(duracao_final),
|
| 159 |
+
"-vf",
|
| 160 |
+
"scale='if(gt(iw/ih,720/1280),max(720,iw),-2)':'if(gt(iw/ih,720/1280),-2,max(1280,ih))',"
|
| 161 |
+
"scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280,fps=30",
|
| 162 |
+
"-preset", "ultrafast", "-crf", "25",
|
| 163 |
+
fundo_convertido
|
| 164 |
+
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 165 |
+
else:
|
| 166 |
+
fundo_convertido = os.path.join(temp_dir, f"fundo_vazio_{n}.mp4")
|
| 167 |
+
subprocess.run([
|
| 168 |
+
"ffmpeg", "-f", "lavfi", "-i", f"color=color=black:size=720x1280:d={duracao_final}:rate=30",
|
| 169 |
+
"-c:v", "libx264", "-t", str(duracao_final), "-preset", "ultrafast", "-crf", "25",
|
| 170 |
+
fundo_convertido
|
| 171 |
+
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 172 |
etapa_atual += 1
|
| 173 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 174 |
|
| 175 |
+
# Etapa 2 - Cortes
|
| 176 |
tentativas = 0
|
| 177 |
while tempo_total < duracao_final and tentativas < 100:
|
| 178 |
tentativas += 1
|
|
|
|
| 197 |
subprocess.run([
|
| 198 |
"ffmpeg", "-ss", str(ini), "-i", c, "-t", str(duracao_aleatoria),
|
| 199 |
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
| 200 |
+
"-an", "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", out
|
| 201 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 202 |
cortes_prontos.append(out)
|
| 203 |
cortes_usados.append((c, ini, duracao_aleatoria))
|
|
|
|
| 207 |
except:
|
| 208 |
continue
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
lista = os.path.join(temp_dir, f"lista_{n}.txt")
|
| 211 |
with open(lista, "w") as f:
|
| 212 |
for c in cortes_prontos:
|
|
|
|
| 215 |
video_raw = os.path.join(temp_dir, f"video_raw_{n}.mp4")
|
| 216 |
subprocess.run([
|
| 217 |
"ffmpeg", "-f", "concat", "-safe", "0", "-i", lista,
|
| 218 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw
|
| 219 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 220 |
+
|
| 221 |
etapa_atual += 1
|
| 222 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 223 |
|
| 224 |
+
# Etapa 3 - Filtros e Efeitos
|
| 225 |
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
|
| 226 |
if zoom_n != 1.0:
|
| 227 |
filtros_main.append(f"scale=iw*{zoom_n}:ih*{zoom_n}")
|
|
|
|
| 285 |
"ffmpeg", "-i", fundo_convertido, "-i", video_raw,
|
| 286 |
"-filter_complex", filtro_complex,
|
| 287 |
"-map", "[final]",
|
| 288 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
| 289 |
video_editado
|
| 290 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 291 |
|
| 292 |
etapa_atual += 1
|
| 293 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 294 |
|
| 295 |
+
# Etapa 4 - Tutorial
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
if usar_tutorial and tutorials_salvos:
|
| 297 |
tutorial_path = random.choice(tutorials_salvos)
|
| 298 |
tutorial_mp4 = os.path.join(temp_dir, f"tutorial_conv_{n}.mp4")
|
| 299 |
subprocess.run([
|
| 300 |
"ffmpeg", "-i", tutorial_path, "-vf", "scale=720:1280,fps=30",
|
| 301 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
| 302 |
"-y", tutorial_mp4
|
| 303 |
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 304 |
|
| 305 |
dur_proc = subprocess.run([
|
| 306 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
| 307 |
+
"-of", "default=noprint_wrappers=1:nokey=1", video_editado
|
| 308 |
], stdout=subprocess.PIPE)
|
| 309 |
dur_f = float(dur_proc.stdout.decode().strip() or 0)
|
| 310 |
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5)
|
|
|
|
| 312 |
part1 = os.path.join(temp_dir, f"part1_{n}.mp4")
|
| 313 |
part2 = os.path.join(temp_dir, f"part2_{n}.mp4")
|
| 314 |
|
| 315 |
+
subprocess.run(["ffmpeg", "-i", video_editado, "-ss", "0", "-t", str(pt),
|
| 316 |
+
"-c:v", "libx264", "-preset", "ultrafast", part1],
|
| 317 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 318 |
+
subprocess.run(["ffmpeg", "-i", video_editado, "-ss", str(pt),
|
| 319 |
+
"-c:v", "libx264", "-preset", "ultrafast", part2],
|
| 320 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 321 |
|
| 322 |
final_txt = os.path.join(temp_dir, f"final_{n}.txt")
|
|
|
|
| 325 |
|
| 326 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
| 327 |
subprocess.run(["ffmpeg", "-f", "concat", "-safe", "0", "-i", final_txt,
|
| 328 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
| 329 |
video_final_raw], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 330 |
else:
|
| 331 |
+
video_final_raw = video_editado
|
| 332 |
etapa_atual += 1
|
| 333 |
atualizar_barra(n, etapa_atual, num_videos_finais, total_etapas)
|
| 334 |
|
| 335 |
+
# Etapa 5 - Velocidade final
|
| 336 |
+
video_com_velocidade = os.path.join(temp_dir, f"video_com_velocidade_{n}.mp4")
|
| 337 |
+
subprocess.run([
|
| 338 |
+
"ffmpeg", "-y", "-i", video_final_raw, "-an",
|
| 339 |
+
"-filter:v", f"setpts=PTS/{velocidade_final_n}",
|
| 340 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value),
|
| 341 |
+
video_com_velocidade
|
| 342 |
+
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 343 |
+
|
| 344 |
dur_final_str = subprocess.run([
|
| 345 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
| 346 |
+
"-of", "default=noprint_wrappers=1:nokey=1", video_com_velocidade
|
| 347 |
], stdout=subprocess.PIPE).stdout.decode().strip()
|
| 348 |
if not dur_final_str:
|
| 349 |
continue
|
|
|
|
| 351 |
|
| 352 |
final_name = f"video_final_{n}_{int(time.time())}.mp4"
|
| 353 |
if usar_musica and musicas_salvas:
|
| 354 |
+
musica_path = random.choice(musicas_salvas)
|
| 355 |
musica_cortada = os.path.join(temp_dir, f"musica_{n}.aac")
|
| 356 |
subprocess.run(["ffmpeg", "-i", musica_path, "-ss", "0", "-t", str(dur_video_real),
|
| 357 |
"-vn", "-acodec", "aac", "-y", musica_cortada],
|
| 358 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 359 |
|
| 360 |
+
subprocess.run(["ffmpeg", "-i", video_com_velocidade, "-i", musica_cortada,
|
| 361 |
"-map", "0:v:0", "-map", "1:a:0",
|
| 362 |
"-c:v", "copy", "-c:a", "aac", "-shortest", final_name],
|
| 363 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 364 |
else:
|
| 365 |
+
shutil.copy(video_com_velocidade, final_name)
|
| 366 |
|
| 367 |
if salvar_no_gerenciador:
|
| 368 |
destino = os.path.join(BASE_SALVOS, categoria_selecionada, final_name)
|
|
|
|
| 377 |
st.error(f"❌ Erro inesperado: {str(e)}")
|
| 378 |
finally:
|
| 379 |
shutil.rmtree(temp_dir)
|
| 380 |
+
|
| 381 |
# ======================== GERENCIADOR ========================
|
| 382 |
elif pagina == "📂 Gerenciador de Arquivos":
|
| 383 |
st.header("📂 Vídeos Salvos por Categoria")
|
|
|
|
| 455 |
arquivos = st.file_uploader("Enviar arquivos", accept_multiple_files=True, key=f"{cat}_{tipo}")
|
| 456 |
|
| 457 |
if arquivos:
|
| 458 |
+
with st.spinner("⏳ Processando e padronizando arquivos..."):
|
| 459 |
for f in arquivos:
|
| 460 |
extensao = os.path.splitext(f.name)[1].lower()
|
| 461 |
nome_aleatorio = f"{random.randint(1000000, 9999999)}{extensao}"
|
|
|
|
| 464 |
out.write(f.read())
|
| 465 |
|
| 466 |
final = os.path.join(path, nome_aleatorio)
|
|
|
|
| 467 |
|
| 468 |
+
if tipo != "Músicas":
|
| 469 |
+
subprocess.run([
|
| 470 |
+
"ffmpeg", "-i", temp,
|
| 471 |
+
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2,fps=30",
|
| 472 |
+
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "25",
|
| 473 |
+
"-y", final
|
| 474 |
+
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 475 |
+
else:
|
| 476 |
+
shutil.move(temp, final)
|
| 477 |
+
|
| 478 |
+
os.remove(temp)
|
| 479 |
+
|
| 480 |
+
st.success("✅ Arquivos enviados, padronizados e renomeados com sucesso!")
|
| 481 |
|
| 482 |
st.write("Arquivos existentes:")
|
| 483 |
for arq in os.listdir(path):
|
|
|
|
| 487 |
with col2:
|
| 488 |
if st.button("🗑", key=f"{tipo}_{cat}_{arq}"):
|
| 489 |
os.remove(os.path.join(path, arq))
|
| 490 |
+
st.rerun()
|