Update app.py
Browse files
app.py
CHANGED
@@ -28,7 +28,6 @@ if "tutorial_path" not in st.session_state:
|
|
28 |
st.session_state.tutorial_path = None
|
29 |
if "video_final" not in st.session_state:
|
30 |
st.session_state.video_final = None
|
31 |
-
|
32 |
# ----- BOTÃO PUXAR VÍDEO -----
|
33 |
if url and st.button("🔄 Puxar vídeo do TikTok"):
|
34 |
with st.spinner("Baixando vídeo do TikTok..."):
|
@@ -55,7 +54,6 @@ if video and not st.session_state.video_path:
|
|
55 |
st.session_state.video_path = tmp.name
|
56 |
|
57 |
video_path = st.session_state.video_path
|
58 |
-
|
59 |
# ----- PLAYER DO VÍDEO ANTES DOS CORTES -----
|
60 |
if video_path and not st.session_state.processando:
|
61 |
st.subheader("🎥 Pré-visualização do vídeo original")
|
@@ -65,6 +63,8 @@ if video_path and not st.session_state.processando:
|
|
65 |
excluir_inicio = st.number_input("Excluir a partir do segundo", min_value=0, value=8, key="excluir_inicio")
|
66 |
excluir_fim = st.number_input("Excluir até o segundo", min_value=0, value=12, key="excluir_fim")
|
67 |
|
|
|
|
|
68 |
corte_min = st.number_input("Tamanho mínimo do corte (segundos)", min_value=1, value=2, key="corte_min")
|
69 |
corte_max = st.number_input("Tamanho máximo do corte (segundos)", min_value=3, value=5, key="corte_max")
|
70 |
|
@@ -121,7 +121,6 @@ if st.session_state.processando:
|
|
121 |
)
|
122 |
|
123 |
status = st.empty()
|
124 |
-
|
125 |
status.markdown("🔎 Analisando duração e resolução do vídeo...")
|
126 |
barra.progress(5)
|
127 |
|
@@ -148,7 +147,6 @@ if st.session_state.processando:
|
|
148 |
excluir_fim = st.session_state.excluir_fim
|
149 |
corte_min = st.session_state.corte_min
|
150 |
corte_max = st.session_state.corte_max
|
151 |
-
|
152 |
velocidade_cortes = st.session_state.velocidade_cortes
|
153 |
|
154 |
partes = []
|
@@ -159,29 +157,33 @@ if st.session_state.processando:
|
|
159 |
|
160 |
cortes = []
|
161 |
usados = set()
|
162 |
-
|
163 |
barra.progress(15)
|
164 |
status.markdown("✂️ Realizando cortes...")
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
|
|
|
|
|
|
|
|
|
|
182 |
barra.progress(25)
|
183 |
|
184 |
-
# --- PROCESSAR OS CORTES
|
185 |
with tempfile.TemporaryDirectory() as tmpdir:
|
186 |
arquivos_cortes = []
|
187 |
total_cortes = len(cortes)
|
@@ -226,7 +228,6 @@ if st.session_state.processando:
|
|
226 |
barra.progress(70)
|
227 |
status.markdown("🔍 Aplicando zoom e espelhamento no vídeo unido...")
|
228 |
|
229 |
-
# --- APLICAR ZOOM E ESPELHAMENTO NO VÍDEO UNIDO ---
|
230 |
video_processado = os.path.join(tmpdir, "video_processado.mp4")
|
231 |
zoom_factor = st.session_state.zoom / 100
|
232 |
filtro_zoom = f"scale=iw*{zoom_factor}:ih*{zoom_factor},crop={largura}:{altura}"
|
@@ -253,13 +254,12 @@ if st.session_state.processando:
|
|
253 |
resultado = subprocess.run(duracao_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
254 |
duracao_video = float(resultado.stdout.decode().strip())
|
255 |
|
256 |
-
# Escolhe posição aleatória
|
257 |
pos_tutorial = random.uniform(3, duracao_video - 1)
|
258 |
|
259 |
parte1 = os.path.join(tmpdir, "parte1.mp4")
|
260 |
parte2 = os.path.join(tmpdir, "parte2.mp4")
|
261 |
|
262 |
-
# Corta a parte 1
|
263 |
subprocess.call([
|
264 |
"ffmpeg", "-y", "-i", video_processado,
|
265 |
"-t", str(pos_tutorial),
|
@@ -267,7 +267,6 @@ if st.session_state.processando:
|
|
267 |
parte1
|
268 |
])
|
269 |
|
270 |
-
# Corta a parte 2
|
271 |
subprocess.call([
|
272 |
"ffmpeg", "-y", "-i", video_processado,
|
273 |
"-ss", str(pos_tutorial),
|
@@ -275,7 +274,6 @@ if st.session_state.processando:
|
|
275 |
parte2
|
276 |
])
|
277 |
|
278 |
-
# Prepara tutorial padronizado
|
279 |
tutorial_pad = os.path.join(tmpdir, "tutorial_pad.mp4")
|
280 |
subprocess.call([
|
281 |
"ffmpeg", "-y", "-i", st.session_state.tutorial_path,
|
@@ -285,7 +283,6 @@ if st.session_state.processando:
|
|
285 |
tutorial_pad
|
286 |
])
|
287 |
|
288 |
-
# Criar lista para concatenação
|
289 |
lista_final_txt = os.path.join(tmpdir, "lista_final.txt")
|
290 |
with open(lista_final_txt, "w") as f:
|
291 |
f.write(f"file '{parte1}'\n")
|
@@ -298,7 +295,6 @@ if st.session_state.processando:
|
|
298 |
"-c:v", "libx264", "-preset", "veryfast",
|
299 |
video_com_tutorial
|
300 |
])
|
301 |
-
|
302 |
else:
|
303 |
video_com_tutorial = video_processado
|
304 |
|
@@ -326,11 +322,9 @@ if st.session_state.processando:
|
|
326 |
])
|
327 |
else:
|
328 |
os.rename(video_final_temp, video_final)
|
329 |
-
|
330 |
barra.progress(95)
|
331 |
status.markdown("📋 Adicionando metadata...")
|
332 |
|
333 |
-
# METADATA ALEATÓRIA
|
334 |
marcas = {
|
335 |
"samsung": ["SM-S918B", "SM-A546E", "SM-M536B"],
|
336 |
"xiaomi": ["Redmi Note 12", "Poco X5", "Mi 11 Lite"],
|
@@ -368,17 +362,14 @@ if st.session_state.processando:
|
|
368 |
|
369 |
barra.progress(100)
|
370 |
|
371 |
-
# Copiar vídeo final para fora do tmpdir
|
372 |
video_final_permanente = os.path.join(tempfile.gettempdir(), "video_final_final.mp4")
|
373 |
shutil.copy(video_final_meta, video_final_permanente)
|
374 |
st.session_state.video_final = video_final_permanente
|
375 |
# --- PLAYER DOS VÍDEOS E BOTÕES ---
|
376 |
-
|
377 |
if st.session_state.video_final:
|
378 |
|
379 |
st.success("✅ Vídeo criado com sucesso!")
|
380 |
|
381 |
-
# Loader "Aguarde os players"
|
382 |
load_players = st.empty()
|
383 |
load_players.markdown(
|
384 |
"""
|
@@ -424,17 +415,14 @@ if st.session_state.video_final:
|
|
424 |
|
425 |
load_players.empty()
|
426 |
|
427 |
-
# Botão para download (sem reset automático)
|
428 |
with open(st.session_state.video_final, "rb") as f:
|
429 |
st.download_button("⬇️ Baixar vídeo", f, file_name="video_novo.mp4")
|
430 |
|
431 |
-
# Botão separado para reset
|
432 |
if st.button("🔄 Criar novo"):
|
433 |
for key in list(st.session_state.keys()):
|
434 |
del st.session_state[key]
|
435 |
st.experimental_rerun()
|
436 |
|
437 |
-
# Botão para manter as mesmas configurações
|
438 |
if st.button("🔄 Gerar novamente com os mesmos ajustes"):
|
439 |
st.session_state.processando = True
|
440 |
st.session_state.video_final = None
|
|
|
28 |
st.session_state.tutorial_path = None
|
29 |
if "video_final" not in st.session_state:
|
30 |
st.session_state.video_final = None
|
|
|
31 |
# ----- BOTÃO PUXAR VÍDEO -----
|
32 |
if url and st.button("🔄 Puxar vídeo do TikTok"):
|
33 |
with st.spinner("Baixando vídeo do TikTok..."):
|
|
|
54 |
st.session_state.video_path = tmp.name
|
55 |
|
56 |
video_path = st.session_state.video_path
|
|
|
57 |
# ----- PLAYER DO VÍDEO ANTES DOS CORTES -----
|
58 |
if video_path and not st.session_state.processando:
|
59 |
st.subheader("🎥 Pré-visualização do vídeo original")
|
|
|
63 |
excluir_inicio = st.number_input("Excluir a partir do segundo", min_value=0, value=8, key="excluir_inicio")
|
64 |
excluir_fim = st.number_input("Excluir até o segundo", min_value=0, value=12, key="excluir_fim")
|
65 |
|
66 |
+
st.session_state.embaralhar_cortes = st.checkbox("✂️ Ativar cortes e embaralhamento aleatório", value=True, key="embaralhar_cortes")
|
67 |
+
|
68 |
corte_min = st.number_input("Tamanho mínimo do corte (segundos)", min_value=1, value=2, key="corte_min")
|
69 |
corte_max = st.number_input("Tamanho máximo do corte (segundos)", min_value=3, value=5, key="corte_max")
|
70 |
|
|
|
121 |
)
|
122 |
|
123 |
status = st.empty()
|
|
|
124 |
status.markdown("🔎 Analisando duração e resolução do vídeo...")
|
125 |
barra.progress(5)
|
126 |
|
|
|
147 |
excluir_fim = st.session_state.excluir_fim
|
148 |
corte_min = st.session_state.corte_min
|
149 |
corte_max = st.session_state.corte_max
|
|
|
150 |
velocidade_cortes = st.session_state.velocidade_cortes
|
151 |
|
152 |
partes = []
|
|
|
157 |
|
158 |
cortes = []
|
159 |
usados = set()
|
|
|
160 |
barra.progress(15)
|
161 |
status.markdown("✂️ Realizando cortes...")
|
162 |
|
163 |
+
if st.session_state.embaralhar_cortes:
|
164 |
+
for inicio, fim in partes:
|
165 |
+
pos = inicio
|
166 |
+
while pos < fim:
|
167 |
+
tam_possivel = min(corte_max, fim - pos)
|
168 |
+
if tam_possivel < corte_min:
|
169 |
+
break
|
170 |
+
tamanho = random.randint(corte_min, tam_possivel)
|
171 |
+
fim_corte = pos + tamanho
|
172 |
+
|
173 |
+
if (pos, fim_corte) not in usados:
|
174 |
+
cortes.append((int(pos), int(fim_corte)))
|
175 |
+
usados.add((pos, fim_corte))
|
176 |
+
pos = fim_corte
|
177 |
+
|
178 |
+
random.shuffle(cortes)
|
179 |
+
else:
|
180 |
+
# Apenas um corte simples direto
|
181 |
+
start = excluir_inicio
|
182 |
+
end = excluir_fim if excluir_fim > excluir_inicio else dur
|
183 |
+
cortes = [(start, end)]
|
184 |
barra.progress(25)
|
185 |
|
186 |
+
# --- PROCESSAR OS CORTES ---
|
187 |
with tempfile.TemporaryDirectory() as tmpdir:
|
188 |
arquivos_cortes = []
|
189 |
total_cortes = len(cortes)
|
|
|
228 |
barra.progress(70)
|
229 |
status.markdown("🔍 Aplicando zoom e espelhamento no vídeo unido...")
|
230 |
|
|
|
231 |
video_processado = os.path.join(tmpdir, "video_processado.mp4")
|
232 |
zoom_factor = st.session_state.zoom / 100
|
233 |
filtro_zoom = f"scale=iw*{zoom_factor}:ih*{zoom_factor},crop={largura}:{altura}"
|
|
|
254 |
resultado = subprocess.run(duracao_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
255 |
duracao_video = float(resultado.stdout.decode().strip())
|
256 |
|
257 |
+
# Escolhe posição aleatória entre 3s e final - 1s
|
258 |
pos_tutorial = random.uniform(3, duracao_video - 1)
|
259 |
|
260 |
parte1 = os.path.join(tmpdir, "parte1.mp4")
|
261 |
parte2 = os.path.join(tmpdir, "parte2.mp4")
|
262 |
|
|
|
263 |
subprocess.call([
|
264 |
"ffmpeg", "-y", "-i", video_processado,
|
265 |
"-t", str(pos_tutorial),
|
|
|
267 |
parte1
|
268 |
])
|
269 |
|
|
|
270 |
subprocess.call([
|
271 |
"ffmpeg", "-y", "-i", video_processado,
|
272 |
"-ss", str(pos_tutorial),
|
|
|
274 |
parte2
|
275 |
])
|
276 |
|
|
|
277 |
tutorial_pad = os.path.join(tmpdir, "tutorial_pad.mp4")
|
278 |
subprocess.call([
|
279 |
"ffmpeg", "-y", "-i", st.session_state.tutorial_path,
|
|
|
283 |
tutorial_pad
|
284 |
])
|
285 |
|
|
|
286 |
lista_final_txt = os.path.join(tmpdir, "lista_final.txt")
|
287 |
with open(lista_final_txt, "w") as f:
|
288 |
f.write(f"file '{parte1}'\n")
|
|
|
295 |
"-c:v", "libx264", "-preset", "veryfast",
|
296 |
video_com_tutorial
|
297 |
])
|
|
|
298 |
else:
|
299 |
video_com_tutorial = video_processado
|
300 |
|
|
|
322 |
])
|
323 |
else:
|
324 |
os.rename(video_final_temp, video_final)
|
|
|
325 |
barra.progress(95)
|
326 |
status.markdown("📋 Adicionando metadata...")
|
327 |
|
|
|
328 |
marcas = {
|
329 |
"samsung": ["SM-S918B", "SM-A546E", "SM-M536B"],
|
330 |
"xiaomi": ["Redmi Note 12", "Poco X5", "Mi 11 Lite"],
|
|
|
362 |
|
363 |
barra.progress(100)
|
364 |
|
|
|
365 |
video_final_permanente = os.path.join(tempfile.gettempdir(), "video_final_final.mp4")
|
366 |
shutil.copy(video_final_meta, video_final_permanente)
|
367 |
st.session_state.video_final = video_final_permanente
|
368 |
# --- PLAYER DOS VÍDEOS E BOTÕES ---
|
|
|
369 |
if st.session_state.video_final:
|
370 |
|
371 |
st.success("✅ Vídeo criado com sucesso!")
|
372 |
|
|
|
373 |
load_players = st.empty()
|
374 |
load_players.markdown(
|
375 |
"""
|
|
|
415 |
|
416 |
load_players.empty()
|
417 |
|
|
|
418 |
with open(st.session_state.video_final, "rb") as f:
|
419 |
st.download_button("⬇️ Baixar vídeo", f, file_name="video_novo.mp4")
|
420 |
|
|
|
421 |
if st.button("🔄 Criar novo"):
|
422 |
for key in list(st.session_state.keys()):
|
423 |
del st.session_state[key]
|
424 |
st.experimental_rerun()
|
425 |
|
|
|
426 |
if st.button("🔄 Gerar novamente com os mesmos ajustes"):
|
427 |
st.session_state.processando = True
|
428 |
st.session_state.video_final = None
|