Update app.py
Browse files
app.py
CHANGED
@@ -64,6 +64,8 @@ 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")
|
@@ -94,7 +96,6 @@ if st.button("Gerar Vídeo(s)"):
|
|
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),
|
@@ -113,7 +114,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
113 |
"-y", tutorial_mp4
|
114 |
], check=True, stderr=subprocess.PIPE)
|
115 |
|
116 |
-
# Padronizar vídeos de cortes
|
117 |
cortes_names = []
|
118 |
for idx, corte in enumerate(cortes):
|
119 |
path_in = os.path.join(temp_dir, f"corte_in_{idx}.mp4")
|
@@ -126,7 +127,8 @@ if st.button("Gerar Vídeo(s)"):
|
|
126 |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", path_out
|
127 |
], check=True, stderr=subprocess.PIPE)
|
128 |
cortes_names.append(path_out)
|
129 |
-
|
|
|
130 |
fundo_cortado = os.path.join(temp_dir, "fundo_cortado.mp4")
|
131 |
subprocess.run([
|
132 |
"ffmpeg", "-i", fundo_path, "-t", str(duracao_final),
|
@@ -153,21 +155,33 @@ 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 |
if ativar_zoom_dinamico:
|
158 |
-
|
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 |
-
#
|
169 |
if ativar_transicoes:
|
170 |
-
filtros_corte.append("fade=t=in:st=0:d=0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
|
172 |
filtro_final = ",".join(filtros_corte)
|
173 |
|
@@ -183,8 +197,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
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:
|
190 |
for c in cortes_prontos:
|
@@ -196,8 +209,10 @@ if st.button("Gerar Vídeo(s)"):
|
|
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 |
-
|
|
|
201 |
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
|
202 |
|
203 |
if zoom != 1.0:
|
@@ -214,24 +229,24 @@ if st.button("Gerar Vídeo(s)"):
|
|
214 |
if ativar_filtro_cor:
|
215 |
filtros_main.append("eq=contrast=1.1:saturation=1.2")
|
216 |
|
217 |
-
#
|
218 |
if ativar_color_grading:
|
219 |
opcoes_color = [
|
220 |
"curves=preset=vintage",
|
221 |
"curves=preset=strong_contrast",
|
222 |
-
"
|
223 |
-
"hue=s=0.
|
224 |
-
"
|
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,7 +261,7 @@ if st.button("Gerar Vídeo(s)"):
|
|
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 ""
|
@@ -342,7 +357,6 @@ if st.button("Gerar Vídeo(s)"):
|
|
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 |
|
|
|
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 |
+
ativar_freeze_frame = st.checkbox("Aplicar Freeze Frame automático", value=False)
|
68 |
+
ativar_slow_motion = st.checkbox("Aplicar Slow Motion Inteligente aleatório", value=False)
|
69 |
|
70 |
# Efeitos extras
|
71 |
st.write("### Outros efeitos")
|
|
|
96 |
with open(fundo_path, "wb") as f:
|
97 |
f.write(video_fundo.read())
|
98 |
else:
|
|
|
99 |
subprocess.run([
|
100 |
"ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600",
|
101 |
"-c:v", "libx264", "-t", str(duracao_final),
|
|
|
114 |
"-y", tutorial_mp4
|
115 |
], check=True, stderr=subprocess.PIPE)
|
116 |
|
117 |
+
# Padronizar vídeos de cortes
|
118 |
cortes_names = []
|
119 |
for idx, corte in enumerate(cortes):
|
120 |
path_in = os.path.join(temp_dir, f"corte_in_{idx}.mp4")
|
|
|
127 |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", path_out
|
128 |
], check=True, stderr=subprocess.PIPE)
|
129 |
cortes_names.append(path_out)
|
130 |
+
|
131 |
+
# Cortar fundo para a duração final
|
132 |
fundo_cortado = os.path.join(temp_dir, "fundo_cortado.mp4")
|
133 |
subprocess.run([
|
134 |
"ffmpeg", "-i", fundo_path, "-t", str(duracao_final),
|
|
|
155 |
out = os.path.join(temp_dir, f"cut_{random.randint(1000,9999)}.mp4")
|
156 |
|
157 |
filtros_corte = []
|
158 |
+
|
159 |
+
# ✅ Zoom/Pan Dinâmico corrigido
|
160 |
if ativar_zoom_dinamico:
|
161 |
+
filtros_corte.append("scale=iw*1.05:ih*1.05,crop=iw-20:ih-20")
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
else:
|
163 |
filtros_corte.append("scale=trunc(iw/2)*2:trunc(ih/2)*2")
|
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 Inteligente (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 |
+
# ✅ Freeze Frame (aleatório, 0.5s no meio)
|
175 |
+
aplicar_freeze = ativar_freeze_frame and random.random() < 0.3
|
176 |
+
if aplicar_freeze:
|
177 |
+
freeze_frame = os.path.join(temp_dir, f"freeze_{random.randint(1000,9999)}.mp4")
|
178 |
+
subprocess.run([
|
179 |
+
"ffmpeg", "-ss", str(ini + duracao_corte/2), "-i", c,
|
180 |
+
"-t", "0.001", "-vf", "scale=1280:720", "-c:v", "libx264",
|
181 |
+
"-y", freeze_frame
|
182 |
+
], check=True, stderr=subprocess.PIPE)
|
183 |
+
cortes_prontos.append(freeze_frame)
|
184 |
+
tempo_total += 0.5
|
185 |
|
186 |
filtro_final = ",".join(filtros_corte)
|
187 |
|
|
|
197 |
break
|
198 |
except:
|
199 |
continue
|
200 |
+
# Concatenação dos cortes
|
|
|
201 |
lista = os.path.join(temp_dir, f"lista_{n}.txt")
|
202 |
with open(lista, "w") as f:
|
203 |
for c in cortes_prontos:
|
|
|
209 |
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
210 |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw
|
211 |
], check=True, stderr=subprocess.PIPE)
|
212 |
+
|
213 |
progresso.progress(35 + n * 5)
|
214 |
+
|
215 |
+
# Filtros aplicados ao vídeo principal (sobre o vídeo de cortes)
|
216 |
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"]
|
217 |
|
218 |
if zoom != 1.0:
|
|
|
229 |
if ativar_filtro_cor:
|
230 |
filtros_main.append("eq=contrast=1.1:saturation=1.2")
|
231 |
|
232 |
+
# ✅ Color Grading aleatório
|
233 |
if ativar_color_grading:
|
234 |
opcoes_color = [
|
235 |
"curves=preset=vintage",
|
236 |
"curves=preset=strong_contrast",
|
237 |
+
"eq=brightness=0.05:saturation=1.4:contrast=1.3",
|
238 |
+
"hue=s=0.6",
|
239 |
+
"colorchannelmixer=.5:0:.5:0:.5:0:.5:0:.5:0:.5"
|
240 |
]
|
241 |
filtros_main.append(random.choice(opcoes_color))
|
242 |
|
|
|
243 |
filtros_main.append("scale=trunc(iw/2)*2:trunc(ih/2)*2")
|
244 |
# Montar filter_complex
|
245 |
filtro_complex = (
|
246 |
f"[0:v]scale=720:1280:force_original_aspect_ratio=increase,"
|
247 |
f"crop=720:1280"
|
248 |
)
|
249 |
+
|
250 |
if ativar_blur_fundo:
|
251 |
filtro_complex += f",boxblur={blur_strength}:1"
|
252 |
if ativar_sepia:
|
|
|
261 |
|
262 |
filtro_complex += f"[1:v]{','.join(filtros_main)}[zoomed];"
|
263 |
filtro_complex += "[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]"
|
264 |
+
|
265 |
if ativar_texto and texto_personalizado.strip():
|
266 |
y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100"
|
267 |
enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else ""
|
|
|
357 |
], check=True, stderr=subprocess.PIPE)
|
358 |
|
359 |
else:
|
|
|
360 |
final_name = f"video_final_{n}_{int(time.time())}.mp4"
|
361 |
shutil.copy(video_final_raw, final_name)
|
362 |
|