Update app.py
Browse files
app.py
CHANGED
@@ -7,7 +7,7 @@ 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")
|
11 |
|
12 |
st.markdown("Envie seus vídeos e gere conteúdo com efeitos, zoom, texto, música, filtros e antiflop!")
|
13 |
|
@@ -71,16 +71,50 @@ if ativar_borda_personalizada:
|
|
71 |
cor_borda = st.color_picker("Cor da borda", "#FF0000")
|
72 |
animacao_borda = st.selectbox("Animação da borda", ["Nenhuma", "Borda Pulsante", "Cor Animada", "Neon", "Ondulada"])
|
73 |
|
74 |
-
# 🔒 Anti-Flop
|
75 |
st.write("### 🔒 Anti-Flop (Evitar Detecção)")
|
76 |
ativar_antiflop = st.checkbox("Ativar Anti-Flop", value=False)
|
77 |
if ativar_antiflop:
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
# Botão principal
|
85 |
if st.button("Gerar Vídeo(s)"):
|
86 |
if not cortes:
|
@@ -97,7 +131,6 @@ if st.button("Gerar Vídeo(s)"):
|
|
97 |
with open(fundo_path, "wb") as f:
|
98 |
f.write(video_fundo.read())
|
99 |
else:
|
100 |
-
# Gera fundo preto 720x1280
|
101 |
subprocess.run([
|
102 |
"ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600",
|
103 |
"-c:v", "libx264", "-t", str(duracao_final),
|
@@ -233,7 +266,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
233 |
video_editado
|
234 |
], check=True, stderr=subprocess.PIPE)
|
235 |
|
236 |
-
# Acelerar vídeo final
|
237 |
video_acelerado = os.path.join(temp_dir, f"video_acelerado_{n}.mp4")
|
238 |
subprocess.run([
|
239 |
"ffmpeg", "-y", "-i", video_editado, "-an",
|
@@ -242,9 +275,9 @@ if st.button("Gerar Vídeo(s)"):
|
|
242 |
video_acelerado
|
243 |
], check=True, stderr=subprocess.PIPE)
|
244 |
|
245 |
-
progresso.progress(
|
246 |
|
247 |
-
# Tutorial no meio
|
248 |
video_final_raw = video_acelerado
|
249 |
if video_tutorial:
|
250 |
dur_proc = subprocess.run([
|
@@ -262,13 +295,13 @@ if st.button("Gerar Vídeo(s)"):
|
|
262 |
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n")
|
263 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
264 |
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)
|
|
|
265 |
# 🎵 Música final
|
266 |
dur_proc = subprocess.run([
|
267 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
268 |
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
|
269 |
], stdout=subprocess.PIPE)
|
270 |
dur_video_real = float(dur_proc.stdout.decode().strip())
|
271 |
-
|
272 |
if musica:
|
273 |
musica_path = os.path.join(temp_dir, "musica_original.mp3")
|
274 |
with open(musica_path, "wb") as f:
|
@@ -291,26 +324,108 @@ if st.button("Gerar Vídeo(s)"):
|
|
291 |
|
292 |
# 🔒 APLICAR ANTI-FLOP NO FINAL
|
293 |
if ativar_antiflop:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
antiflop_out = f"antiflop_{n}_{int(time.time())}.mp4"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
vf_af = (
|
296 |
-
f"scale=iw*{
|
297 |
-
f"crop=iw/{
|
298 |
-
f"eq=brightness={
|
299 |
-
f"noise=alls={
|
300 |
-
f"rotate={
|
301 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
subprocess.run([
|
303 |
-
|
304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
305 |
antiflop_out
|
306 |
], check=True)
|
|
|
|
|
307 |
st.video(antiflop_out)
|
308 |
with open(antiflop_out, "rb") as f:
|
309 |
-
|
|
|
|
|
|
|
310 |
else:
|
|
|
311 |
st.video(final_name)
|
312 |
with open(final_name, "rb") as f:
|
313 |
-
|
|
|
|
|
|
|
314 |
|
315 |
progresso.progress(100)
|
316 |
st.success("✅ Todos os vídeos foram gerados com sucesso!")
|
|
|
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!")
|
13 |
|
|
|
71 |
cor_borda = st.color_picker("Cor da borda", "#FF0000")
|
72 |
animacao_borda = st.selectbox("Animação da borda", ["Nenhuma", "Borda Pulsante", "Cor Animada", "Neon", "Ondulada"])
|
73 |
|
74 |
+
# 🔒 Anti-Flop Completo
|
75 |
st.write("### 🔒 Anti-Flop (Evitar Detecção)")
|
76 |
ativar_antiflop = st.checkbox("Ativar Anti-Flop", value=False)
|
77 |
if ativar_antiflop:
|
78 |
+
aplicar_random = st.button("🎲 RANDOMIZAR Anti-Flop")
|
79 |
+
if 'antiflop_settings' not in st.session_state or aplicar_random:
|
80 |
+
st.session_state.antiflop_settings = {
|
81 |
+
'zoom': random.uniform(1.00, 1.05),
|
82 |
+
'brilho': random.uniform(-0.05, 0.05),
|
83 |
+
'contraste': random.uniform(0.95, 1.05),
|
84 |
+
'saturacao': random.uniform(0.95, 1.05),
|
85 |
+
'ruido': random.randint(3, 15),
|
86 |
+
'rotacao': random.uniform(0.2, 1.5),
|
87 |
+
'pitch': random.uniform(1.00, 1.08),
|
88 |
+
'velocidade': random.uniform(1.00, 1.03),
|
89 |
+
'bitrate': random.randint(900, 1500),
|
90 |
+
'fps': random.choice([30, 29.97, 25])
|
91 |
+
}
|
92 |
+
antiflop = st.session_state.antiflop_settings
|
93 |
+
zoom_af = st.slider("Zoom Anti-Flop (%)", 100, 105, int(antiflop['zoom']*100)) / 100
|
94 |
+
brilho_af = st.slider("Brilho Anti-Flop", -0.1, 0.1, antiflop['brilho'])
|
95 |
+
contraste_af = st.slider("Contraste Anti-Flop", 0.9, 1.1, antiflop['contraste'])
|
96 |
+
saturacao_af = st.slider("Saturação Anti-Flop", 0.9, 1.1, antiflop['saturacao'])
|
97 |
+
ruido_af = st.slider("Ruído Visual Anti-Flop (0-20)", 0, 20, antiflop['ruido'])
|
98 |
+
rotacao_af = st.slider("Rotação Anti-Flop (graus)", 0.0, 2.0, antiflop['rotacao'])
|
99 |
+
pitch_af = st.slider("Pitch do Áudio Anti-Flop (%)", 100, 110, int(antiflop['pitch']*100)) / 100
|
100 |
+
velocidade_af = st.slider("Velocidade do Vídeo Anti-Flop (%)", 100, 103, int(antiflop['velocidade']*100)) / 100
|
101 |
+
bitrate_af = st.slider("Bitrate Anti-Flop (kbps)", 800, 2000, antiflop['bitrate'])
|
102 |
+
fps_af = st.selectbox("FPS Final Anti-Flop", [30, 29.97, 25], index=[30, 29.97, 25].index(antiflop['fps']))
|
103 |
+
st.subheader("🧩 Extras Anti-Detecção")
|
104 |
+
usar_marca = st.checkbox("Marca d’água Fantasma com perfil", value=True)
|
105 |
+
usar_glitch = st.checkbox("Inserir Frame Preto Aleatório", value=True)
|
106 |
+
res_random = st.checkbox("Resolução Aleatória", value=True)
|
107 |
+
cortar = st.checkbox("Cortar trecho aleatório do vídeo", value=True)
|
108 |
+
embaralhar = st.checkbox("Embaralhar trechos do vídeo", value=True)
|
109 |
+
st.subheader("🎨 Avançado Anti-Flop")
|
110 |
+
lote_efeitos = st.checkbox("Aplicar efeitos por lote (aleatórios)", value=True)
|
111 |
+
transicoes = st.checkbox("Transições visuais aleatórias", value=True)
|
112 |
+
st.subheader("🕵️♂️ Perfil Fingerprint")
|
113 |
+
fingerprint = st.selectbox("Escolha o Perfil Anti-Flop", ["Android", "iPhone", "Câmera", "CapCut", "Samsung", "Xiaomi", "LG"])
|
114 |
+
|
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:
|
|
|
131 |
with open(fundo_path, "wb") as f:
|
132 |
f.write(video_fundo.read())
|
133 |
else:
|
|
|
134 |
subprocess.run([
|
135 |
"ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600",
|
136 |
"-c:v", "libx264", "-t", str(duracao_final),
|
|
|
266 |
video_editado
|
267 |
], check=True, stderr=subprocess.PIPE)
|
268 |
|
269 |
+
# Acelerar vídeo final (sem áudio)
|
270 |
video_acelerado = os.path.join(temp_dir, f"video_acelerado_{n}.mp4")
|
271 |
subprocess.run([
|
272 |
"ffmpeg", "-y", "-i", video_editado, "-an",
|
|
|
275 |
video_acelerado
|
276 |
], check=True, stderr=subprocess.PIPE)
|
277 |
|
278 |
+
progresso.progress(70)
|
279 |
|
280 |
+
# Tutorial no meio (se enviado)
|
281 |
video_final_raw = video_acelerado
|
282 |
if video_tutorial:
|
283 |
dur_proc = subprocess.run([
|
|
|
295 |
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n")
|
296 |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4")
|
297 |
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)
|
298 |
+
|
299 |
# 🎵 Música final
|
300 |
dur_proc = subprocess.run([
|
301 |
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
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 |
if musica:
|
306 |
musica_path = os.path.join(temp_dir, "musica_original.mp3")
|
307 |
with open(musica_path, "wb") as f:
|
|
|
324 |
|
325 |
# 🔒 APLICAR ANTI-FLOP NO FINAL
|
326 |
if ativar_antiflop:
|
327 |
+
st.info(f"🔒 Aplicando Anti-Flop no vídeo {n+1}...")
|
328 |
+
base_video = final_name
|
329 |
+
# 🌀 Embaralhar trechos se ativado
|
330 |
+
if embaralhar:
|
331 |
+
temp_af_dir = f"temp_af_{random.randint(1000,9999)}"
|
332 |
+
os.makedirs(temp_af_dir, exist_ok=True)
|
333 |
+
subprocess.run(['ffmpeg', '-i', base_video, '-c', 'copy', '-f', 'segment', '-segment_time', '2', f'{temp_af_dir}/out%03d.mp4'])
|
334 |
+
files = os.listdir(temp_af_dir)
|
335 |
+
files.sort()
|
336 |
+
random.shuffle(files)
|
337 |
+
concat_list = f"{temp_af_dir}/list.txt"
|
338 |
+
with open(concat_list, 'w') as f:
|
339 |
+
for file in files:
|
340 |
+
f.write(f"file '{os.path.join(temp_af_dir, file)}'\n")
|
341 |
+
shuffled_input = f"shuffled_af_{random.randint(1000,9999)}.mp4"
|
342 |
+
subprocess.run(['ffmpeg', '-f', 'concat', '-safe', '0', '-i', concat_list, '-c', 'copy', shuffled_input])
|
343 |
+
shutil.rmtree(temp_af_dir)
|
344 |
+
base_video = shuffled_input if os.path.exists(shuffled_input) else final_name
|
345 |
+
|
346 |
+
encoder_name = f"{fingerprint}Cam_{random.randint(1000,9999)}"
|
347 |
antiflop_out = f"antiflop_{n}_{int(time.time())}.mp4"
|
348 |
+
|
349 |
+
# 🎨 Efeitos
|
350 |
+
if lote_efeitos:
|
351 |
+
zoom_r = random.uniform(1.00, 1.05)
|
352 |
+
brilho_r = random.uniform(-0.05, 0.05)
|
353 |
+
contraste_r = random.uniform(0.95, 1.05)
|
354 |
+
saturacao_r = random.uniform(0.95, 1.05)
|
355 |
+
ruido_r = random.randint(3, 15)
|
356 |
+
rot_r = random.uniform(0.2, 1.5) * 0.01745
|
357 |
+
velocidade_r = random.uniform(1.00, 1.03)
|
358 |
+
bitrate_r = f"{random.randint(900, 1500)}k"
|
359 |
+
pitch_r = random.uniform(1.00, 1.08)
|
360 |
+
else:
|
361 |
+
zoom_r = zoom_af
|
362 |
+
brilho_r = brilho_af
|
363 |
+
contraste_r = contraste_af
|
364 |
+
saturacao_r = saturacao_af
|
365 |
+
ruido_r = ruido_af
|
366 |
+
rot_r = rotacao_af * 0.01745
|
367 |
+
velocidade_r = velocidade_af
|
368 |
+
bitrate_r = f"{bitrate_af}k"
|
369 |
+
pitch_r = pitch_af
|
370 |
+
|
371 |
vf_af = (
|
372 |
+
f"scale=iw*{zoom_r}:ih*{zoom_r},"
|
373 |
+
f"crop=iw/{zoom_r}:ih/{zoom_r},"
|
374 |
+
f"eq=brightness={brilho_r}:contrast={contraste_r}:saturation={saturacao_r},"
|
375 |
+
f"noise=alls={ruido_r}:allf=t,"
|
376 |
+
f"rotate={rot_r}:[email protected]"
|
377 |
)
|
378 |
+
if usar_marca:
|
379 |
+
vf_af += f",drawtext=text='{encoder_name}':[email protected]:x=10:y=10:fontsize=24"
|
380 |
+
if usar_glitch:
|
381 |
+
vf_af += ",blackframe=1:0"
|
382 |
+
if res_random:
|
383 |
+
w = random.choice([720, 960, 1080])
|
384 |
+
h = random.choice([1280, 1440, 1920])
|
385 |
+
vf_af += f",scale={w}:{h}"
|
386 |
+
if transicoes:
|
387 |
+
effects = [
|
388 |
+
"fade=t=in:st=0:d=0.5",
|
389 |
+
"fade=t=out:st=3:d=0.5",
|
390 |
+
"zoompan=z='min(pzoom+0.0015,1.5)':d=1",
|
391 |
+
"drawbox=x=0:y=0:w=iw:h=ih:[email protected]:t=fill"
|
392 |
+
]
|
393 |
+
vf_af += "," + random.choice(effects)
|
394 |
+
|
395 |
+
af = f"asetrate=44100*{pitch_r},aresample=44100"
|
396 |
+
profile = "baseline" if fingerprint in ["Android", "Xiaomi"] else "main"
|
397 |
+
if fingerprint == "iPhone":
|
398 |
+
profile = "high"
|
399 |
+
level = "4.0" if fingerprint == "Samsung" else "3.1"
|
400 |
+
ar = "44100" if fingerprint in ["iPhone", "Xiaomi"] else "48000"
|
401 |
+
|
402 |
subprocess.run([
|
403 |
+
'ffmpeg', '-i', base_video,
|
404 |
+
'-vf', vf_af,
|
405 |
+
'-af', af,
|
406 |
+
'-r', str(fps_af),
|
407 |
+
'-c:v', 'libx264', '-profile:v', profile, '-level', level,
|
408 |
+
'-b:v', bitrate_r, '-ar', ar,
|
409 |
+
'-preset', 'fast', '-tune', 'zerolatency',
|
410 |
+
'-movflags', '+faststart',
|
411 |
antiflop_out
|
412 |
], check=True)
|
413 |
+
|
414 |
+
# Download Anti-Flop
|
415 |
st.video(antiflop_out)
|
416 |
with open(antiflop_out, "rb") as f:
|
417 |
+
if download_automatico:
|
418 |
+
st.download_button(f"📥 Baixar vídeo {n+1} Anti-Flop", f, file_name=antiflop_out, disabled=True)
|
419 |
+
else:
|
420 |
+
st.download_button(f"📥 Baixar vídeo {n+1} Anti-Flop", f, file_name=antiflop_out)
|
421 |
else:
|
422 |
+
# Download normal
|
423 |
st.video(final_name)
|
424 |
with open(final_name, "rb") as f:
|
425 |
+
if download_automatico:
|
426 |
+
st.download_button(f"📥 Baixar vídeo {n+1}", f, file_name=final_name, disabled=True)
|
427 |
+
else:
|
428 |
+
st.download_button(f"📥 Baixar vídeo {n+1}", f, file_name=final_name)
|
429 |
|
430 |
progresso.progress(100)
|
431 |
st.success("✅ Todos os vídeos foram gerados com sucesso!")
|