pcdoido2 commited on
Commit
17762c9
·
verified ·
1 Parent(s): a8d2607

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -61
app.py CHANGED
@@ -9,7 +9,7 @@ import time
9
  st.set_page_config(page_title="TikTok Video Generator", layout="centered")
10
  st.title("🎥 TikTok Video Generator - PRO")
11
 
12
- st.markdown("Envie seus vídeos e gere conteúdo com efeitos modernos e automáticos!")
13
 
14
  # Uploads dos vídeos de cortes (principal)
15
  st.markdown("<div style='background-color:#F0F0FF;padding:10px;border-radius:8px'>", unsafe_allow_html=True)
@@ -37,7 +37,7 @@ velocidade_cortes = st.slider("Velocidade dos cortes", 0.5, 2.0, 1.0, 0.1)
37
  velocidade_final = st.slider("Velocidade final", 0.5, 2.0, 1.0, 0.1)
38
  crf_value = st.slider("Qualidade CRF", 18, 30, 23)
39
 
40
- # Texto (opcional)
41
  st.write("### Texto no Vídeo")
42
  ativar_texto = st.checkbox("Ativar texto", value=False)
43
  if ativar_texto:
@@ -59,15 +59,14 @@ ativar_granulado = st.checkbox("Granulado", False)
59
  ativar_pb = st.checkbox("Preto e branco", False)
60
  ativar_vignette = st.checkbox("Vignette", False)
61
 
62
- # Efeitos selecionados (os únicos que ficarão ativos)
63
- st.write("### Efeitos Visuais Avançados")
64
- movimento_camera = st.selectbox("Movimento de Câmera", ["Nenhum", "Esquerda para Direita", "Direita para Esquerda"])
65
  ativar_color_grading = st.checkbox("Aplicar Color Grading Aleatório", value=False)
66
  ativar_transicoes = st.checkbox("Adicionar Transições Cinemáticas", value=False)
67
- ativar_slow_motion = st.checkbox("Aplicar Slow Motion Inteligente aleatório", value=False)
68
 
69
- # Outros efeitos simples
70
- st.write("### Outros ajustes")
71
  ativar_espelhar = st.checkbox("Espelhar vídeo", True)
72
  ativar_filtro_cor = st.checkbox("Ajuste de cor", True)
73
  remover_borda = st.checkbox("Remover borda do vídeo")
@@ -88,19 +87,21 @@ if st.button("Gerar Vídeo(s)"):
88
  temp_dir = tempfile.mkdtemp()
89
 
90
  try:
91
- # Fundo
92
  fundo_path = os.path.join(temp_dir, "fundo.mp4")
 
93
  if video_fundo:
94
  with open(fundo_path, "wb") as f:
95
  f.write(video_fundo.read())
96
  else:
 
97
  subprocess.run([
98
  "ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600",
99
  "-c:v", "libx264", "-t", str(duracao_final),
100
  "-pix_fmt", "yuv420p", "-y", fundo_path
101
  ], check=True, stderr=subprocess.PIPE)
102
 
103
- # Tutorial
104
  if video_tutorial:
105
  tutorial_path = os.path.join(temp_dir, "tutorial_raw.mp4")
106
  with open(tutorial_path, "wb") as f:
@@ -112,7 +113,7 @@ if st.button("Gerar Vídeo(s)"):
112
  "-y", tutorial_mp4
113
  ], check=True, stderr=subprocess.PIPE)
114
 
115
- # Padronizar vídeos
116
  cortes_names = []
117
  for idx, corte in enumerate(cortes):
118
  path_in = os.path.join(temp_dir, f"corte_in_{idx}.mp4")
@@ -125,8 +126,7 @@ if st.button("Gerar Vídeo(s)"):
125
  "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", path_out
126
  ], check=True, stderr=subprocess.PIPE)
127
  cortes_names.append(path_out)
128
-
129
- # Fundo cortado
130
  fundo_cortado = os.path.join(temp_dir, "fundo_cortado.mp4")
131
  subprocess.run([
132
  "ffmpeg", "-i", fundo_path, "-t", str(duracao_final),
@@ -153,23 +153,21 @@ if st.button("Gerar Vídeo(s)"):
153
  out = os.path.join(temp_dir, f"cut_{random.randint(1000,9999)}.mp4")
154
 
155
  filtros_corte = []
156
-
157
- # ✅ Movimento de Câmera (corrigido)
158
- if movimento_camera == "Esquerda para Direita":
159
- filtros_corte.append("scale=800:1280,crop=720:1280:x='t*5':y=0")
160
- elif movimento_camera == "Direita para Esquerda":
161
- filtros_corte.append("scale=800:1280,crop=720:1280:x='(in_w-w)-t*5':y=0")
 
 
 
162
  else:
163
- filtros_corte.append("scale=720:1280")
164
 
165
- # Transições Cinemáticas
166
  if ativar_transicoes:
167
- filtros_corte.append("fade=t=in:st=0:d=0.3,fade=t=out:st=4.7:d=0.3")
168
-
169
- # ✅ Slow Motion aleatório
170
- aplicar_slow = ativar_slow_motion and random.random() < 0.3
171
- if aplicar_slow:
172
- filtros_corte.append("setpts=1.5*PTS")
173
 
174
  filtro_final = ",".join(filtros_corte)
175
 
@@ -185,6 +183,7 @@ if st.button("Gerar Vídeo(s)"):
185
  break
186
  except:
187
  continue
 
188
  # Concatenação dos cortes com ajuste para múltiplos de 2
189
  lista = os.path.join(temp_dir, f"lista_{n}.txt")
190
  with open(lista, "w") as f:
@@ -197,10 +196,8 @@ if st.button("Gerar Vídeo(s)"):
197
  "-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
198
  "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw
199
  ], check=True, stderr=subprocess.PIPE)
200
-
201
  progresso.progress(35 + n * 5)
202
-
203
- # Filtros principais sobre o vídeo principal
204
  filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
205
 
206
  if zoom != 1.0:
@@ -217,24 +214,24 @@ if st.button("Gerar Vídeo(s)"):
217
  if ativar_filtro_cor:
218
  filtros_main.append("eq=contrast=1.1:saturation=1.2")
219
 
220
- # Color Grading aleatório
221
  if ativar_color_grading:
222
- filtros_main.append(random.choice([
223
  "curves=preset=vintage",
224
  "curves=preset=strong_contrast",
225
- "eq=brightness=0.05:saturation=1.5:contrast=1.2",
226
- "hue=s=0.6",
227
- "colorchannelmixer=.5:0:.5:0:.5:0:.5:0:.5:0:.5"
228
- ]))
 
229
 
230
- # Garantir múltiplos de 2 no final
231
  filtros_main.append("scale=trunc(iw/2)*2:trunc(ih/2)*2")
232
  # Montar filter_complex
233
  filtro_complex = (
234
  f"[0:v]scale=720:1280:force_original_aspect_ratio=increase,"
235
  f"crop=720:1280"
236
  )
237
-
238
  if ativar_blur_fundo:
239
  filtro_complex += f",boxblur={blur_strength}:1"
240
  if ativar_sepia:
@@ -249,29 +246,12 @@ if st.button("Gerar Vídeo(s)"):
249
 
250
  filtro_complex += f"[1:v]{','.join(filtros_main)}[zoomed];"
251
  filtro_complex += "[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
252
-
253
- if ativar_borda_personalizada:
254
- cor_ffmpeg = f"0x{cor_borda.lstrip('#')}FF"
255
- anim_map = {
256
- "Nenhuma": "",
257
- "Borda Pulsante": "enable='lt(mod(t,1),0.5)'",
258
- "Cor Animada": "enable='lt(mod(t,1),0.5)'",
259
- "Neon": "enable='lt(mod(t,0.5),0.25)'",
260
- "Ondulada": "enable='gt(sin(t*3.14),0)'"
261
- }
262
- drawbox = f"drawbox=x=0:y=0:w=iw:h=ih:color={cor_ffmpeg}:t=5"
263
- if anim_map[animacao_borda]:
264
- drawbox += f":{anim_map[animacao_borda]}"
265
- filtro_complex += f";[base]{drawbox}[borda]"
266
- filtro_complex += ";[borda]null[texto]"
267
- else:
268
- filtro_complex += ";[base]null[texto]"
269
-
270
  if ativar_texto and texto_personalizado.strip():
271
  y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
272
  enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
273
  texto_clean = texto_personalizado.replace(":", "\\:").replace("'", "\\'")
274
- filtro_complex += f";[texto]drawtext=text='{texto_clean}':"
275
  filtro_complex += (
276
  f"fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:"
277
  f"fontcolor={cor_texto}:fontsize={tamanho_texto}:"
@@ -279,7 +259,7 @@ if st.button("Gerar Vídeo(s)"):
279
  f"x=(w-text_w)/2:y={y_pos}{enable}[final]"
280
  )
281
  else:
282
- filtro_complex += ";[texto]null[final]"
283
 
284
  # Gerar vídeo com filtros
285
  video_editado = os.path.join(temp_dir, f"video_editado_{n}.mp4")
@@ -302,7 +282,7 @@ if st.button("Gerar Vídeo(s)"):
302
 
303
  progresso.progress(90)
304
  # Tutorial no meio, se fornecido
305
- video_final_raw = video_acelerado
306
  if video_tutorial:
307
  dur_proc = subprocess.run([
308
  "ffprobe", "-v", "error", "-show_entries", "format=duration",
@@ -334,14 +314,14 @@ if st.button("Gerar Vídeo(s)"):
334
  video_final_raw
335
  ], check=True, stderr=subprocess.PIPE)
336
 
337
- # Duração real para sincronizar música
338
  dur_proc = subprocess.run([
339
  "ffprobe", "-v", "error", "-show_entries", "format=duration",
340
  "-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
341
  ], stdout=subprocess.PIPE)
342
  dur_video_real = float(dur_proc.stdout.decode().strip())
343
 
344
- # Música final
345
  if musica:
346
  musica_path = os.path.join(temp_dir, "musica_original.mp3")
347
  with open(musica_path, "wb") as f:
@@ -362,6 +342,7 @@ if st.button("Gerar Vídeo(s)"):
362
  ], check=True, stderr=subprocess.PIPE)
363
 
364
  else:
 
365
  final_name = f"video_final_{n}_{int(time.time())}.mp4"
366
  shutil.copy(video_final_raw, final_name)
367
 
 
9
  st.set_page_config(page_title="TikTok Video Generator", 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 e filtros!")
13
 
14
  # Uploads dos vídeos de cortes (principal)
15
  st.markdown("<div style='background-color:#F0F0FF;padding:10px;border-radius:8px'>", unsafe_allow_html=True)
 
37
  velocidade_final = st.slider("Velocidade final", 0.5, 2.0, 1.0, 0.1)
38
  crf_value = st.slider("Qualidade CRF", 18, 30, 23)
39
 
40
+ # Texto com emojis
41
  st.write("### Texto no Vídeo")
42
  ativar_texto = st.checkbox("Ativar texto", value=False)
43
  if ativar_texto:
 
59
  ativar_pb = st.checkbox("Preto e branco", False)
60
  ativar_vignette = st.checkbox("Vignette", False)
61
 
62
+ # Novos efeitos PRO
63
+ st.write("### Efeitos PRO")
64
+ ativar_zoom_dinamico = st.checkbox("Ativar Zoom/Pan Dinâmico (Movimento de câmera)", value=False)
65
  ativar_color_grading = st.checkbox("Aplicar Color Grading Aleatório", value=False)
66
  ativar_transicoes = st.checkbox("Adicionar Transições Cinemáticas", value=False)
 
67
 
68
+ # Efeitos extras
69
+ st.write("### Outros efeitos")
70
  ativar_espelhar = st.checkbox("Espelhar vídeo", True)
71
  ativar_filtro_cor = st.checkbox("Ajuste de cor", True)
72
  remover_borda = st.checkbox("Remover borda do vídeo")
 
87
  temp_dir = tempfile.mkdtemp()
88
 
89
  try:
90
+ # Salvar ou gerar fundo
91
  fundo_path = os.path.join(temp_dir, "fundo.mp4")
92
+
93
  if video_fundo:
94
  with open(fundo_path, "wb") as f:
95
  f.write(video_fundo.read())
96
  else:
97
+ # Gera fundo preto 720x1280 com duração longa
98
  subprocess.run([
99
  "ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600",
100
  "-c:v", "libx264", "-t", str(duracao_final),
101
  "-pix_fmt", "yuv420p", "-y", fundo_path
102
  ], check=True, stderr=subprocess.PIPE)
103
 
104
+ # Salvar e converter tutorial (se enviado)
105
  if video_tutorial:
106
  tutorial_path = os.path.join(temp_dir, "tutorial_raw.mp4")
107
  with open(tutorial_path, "wb") as f:
 
113
  "-y", tutorial_mp4
114
  ], check=True, stderr=subprocess.PIPE)
115
 
116
+ # Padronizar vídeos de cortes para 1280x720 com múltiplos de 2
117
  cortes_names = []
118
  for idx, corte in enumerate(cortes):
119
  path_in = os.path.join(temp_dir, f"corte_in_{idx}.mp4")
 
126
  "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", path_out
127
  ], check=True, stderr=subprocess.PIPE)
128
  cortes_names.append(path_out)
129
+ # ✅ Cortar o vídeo de fundo para a duração final
 
130
  fundo_cortado = os.path.join(temp_dir, "fundo_cortado.mp4")
131
  subprocess.run([
132
  "ffmpeg", "-i", fundo_path, "-t", str(duracao_final),
 
153
  out = os.path.join(temp_dir, f"cut_{random.randint(1000,9999)}.mp4")
154
 
155
  filtros_corte = []
156
+ # Aplica zoom dinâmico se ativado
157
+ if ativar_zoom_dinamico:
158
+ # Movimento aleatório para cima/baixo/esquerda/direita
159
+ movimentos = [
160
+ "crop=in_w-20:in_h-20:0:0,zoompan=z='zoom+0.001':d=125",
161
+ "crop=in_w-20:in_h-20:20:0,zoompan=z='zoom+0.0015':d=125",
162
+ "crop=in_w-20:in_h-20:0:20,zoompan=z='zoom+0.0015':d=125"
163
+ ]
164
+ filtros_corte.append(random.choice(movimentos))
165
  else:
166
+ filtros_corte.append("scale=trunc(iw/2)*2:trunc(ih/2)*2")
167
 
168
+ # Transição: Adiciona fade in/out aleatório
169
  if ativar_transicoes:
170
+ filtros_corte.append("fade=t=in:st=0:d=0.5,fade=t=out:st=4.5:d=0.5")
 
 
 
 
 
171
 
172
  filtro_final = ",".join(filtros_corte)
173
 
 
183
  break
184
  except:
185
  continue
186
+
187
  # Concatenação dos cortes com ajuste para múltiplos de 2
188
  lista = os.path.join(temp_dir, f"lista_{n}.txt")
189
  with open(lista, "w") as f:
 
196
  "-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
197
  "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw
198
  ], check=True, stderr=subprocess.PIPE)
 
199
  progresso.progress(35 + n * 5)
200
+ # Filtros aplicados ao vídeo principal
 
201
  filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
202
 
203
  if zoom != 1.0:
 
214
  if ativar_filtro_cor:
215
  filtros_main.append("eq=contrast=1.1:saturation=1.2")
216
 
217
+ # Aplica Color Grading aleatório se ativado
218
  if ativar_color_grading:
219
+ opcoes_color = [
220
  "curves=preset=vintage",
221
  "curves=preset=strong_contrast",
222
+ "curves=preset=color_negative",
223
+ "hue=s=0.5",
224
+ "eq=brightness=0.05:saturation=1.5:contrast=1.2"
225
+ ]
226
+ filtros_main.append(random.choice(opcoes_color))
227
 
228
+ # Garantir múltiplos de 2 no final (sempre)
229
  filtros_main.append("scale=trunc(iw/2)*2:trunc(ih/2)*2")
230
  # Montar filter_complex
231
  filtro_complex = (
232
  f"[0:v]scale=720:1280:force_original_aspect_ratio=increase,"
233
  f"crop=720:1280"
234
  )
 
235
  if ativar_blur_fundo:
236
  filtro_complex += f",boxblur={blur_strength}:1"
237
  if ativar_sepia:
 
246
 
247
  filtro_complex += f"[1:v]{','.join(filtros_main)}[zoomed];"
248
  filtro_complex += "[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
249
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  if ativar_texto and texto_personalizado.strip():
251
  y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
252
  enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
253
  texto_clean = texto_personalizado.replace(":", "\\:").replace("'", "\\'")
254
+ filtro_complex += f";[base]drawtext=text='{texto_clean}':"
255
  filtro_complex += (
256
  f"fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:"
257
  f"fontcolor={cor_texto}:fontsize={tamanho_texto}:"
 
259
  f"x=(w-text_w)/2:y={y_pos}{enable}[final]"
260
  )
261
  else:
262
+ filtro_complex += ";[base]null[final]"
263
 
264
  # Gerar vídeo com filtros
265
  video_editado = os.path.join(temp_dir, f"video_editado_{n}.mp4")
 
282
 
283
  progresso.progress(90)
284
  # Tutorial no meio, se fornecido
285
+ video_final_raw = video_acelerado # Começa com o vídeo acelerado
286
  if video_tutorial:
287
  dur_proc = subprocess.run([
288
  "ffprobe", "-v", "error", "-show_entries", "format=duration",
 
314
  video_final_raw
315
  ], check=True, stderr=subprocess.PIPE)
316
 
317
+ # 🧠 Obter duração real do vídeo final (após tudo)
318
  dur_proc = subprocess.run([
319
  "ffprobe", "-v", "error", "-show_entries", "format=duration",
320
  "-of", "default=noprint_wrappers=1:nokey=1", video_final_raw
321
  ], stdout=subprocess.PIPE)
322
  dur_video_real = float(dur_proc.stdout.decode().strip())
323
 
324
+ # 🎵 Cortar música para a duração real
325
  if musica:
326
  musica_path = os.path.join(temp_dir, "musica_original.mp3")
327
  with open(musica_path, "wb") as f:
 
342
  ], check=True, stderr=subprocess.PIPE)
343
 
344
  else:
345
+ # Se não tiver música, só copia o vídeo final
346
  final_name = f"video_final_{n}_{int(time.time())}.mp4"
347
  shutil.copy(video_final_raw, final_name)
348