Spaces:
Sleeping
Sleeping
import streamlit as st | |
import subprocess | |
import os | |
import random | |
import tempfile | |
import shutil | |
import time | |
st.set_page_config(page_title="TikTok Video Generator - PRO", layout="centered") | |
pagina = st.sidebar.radio("Escolha uma página:", ["🎬 Gerador de Vídeo", "📂 Gerenciador de Arquivos"]) | |
if pagina == "🎬 Gerador de Vídeo": | |
st.title("🎥 TikTok Video Generator - PRO") | |
st.markdown("Envie seus vídeos e gere conteúdo com efeitos, zoom, texto, música e filtros!") | |
# Uploads dos vídeos de cortes (principal) | |
st.markdown("<div style='background-color:#F0F0FF;padding:10px;border-radius:8px'>", unsafe_allow_html=True) | |
st.subheader("🎬 Envie os vídeos principais (cortes)") | |
cortes = st.file_uploader("Vídeos de cortes", type=["mp4"], accept_multiple_files=True) | |
st.markdown("</div>", unsafe_allow_html=True) | |
# Upload de múltiplos vídeos ou imagens de fundo | |
st.markdown("<div style='background-color:#FFF0F0;padding:10px;border-radius:8px;margin-top:15px;'>", unsafe_allow_html=True) | |
st.subheader("🌄 Envie os vídeos ou imagens de fundo (opcional)") | |
videos_fundo = st.file_uploader("Vídeos ou Imagens de Fundo", type=["mp4", "mov", "jpg", "jpeg", "png"], accept_multiple_files=True) | |
st.markdown("</div>", unsafe_allow_html=True) | |
# Uploads adicionais (múltiplos tutoriais agora) | |
videos_tutorial = st.file_uploader("📎 Tutoriais (opcional)", type="mp4", accept_multiple_files=True) | |
musica = st.file_uploader("🎵 Música (opcional, MP3)", type=["mp3"]) | |
num_videos_finais = st.number_input("Quantos vídeos gerar?", 1, 10, 1) | |
duracao_final = st.number_input("Duração final (s)", 10, 300, 30) | |
st.write("## Configuração dos cortes") | |
# BOTÃO ALEATORIZAR CONFIGURAÇÕES | |
aleatorizar = st.checkbox("🎲 Aleatorizar configurações por vídeo") | |
duracao_corte_min = st.slider("Duração mínima de cada corte (s)", 1, 10, 3) | |
duracao_corte_max = st.slider("Duração máxima de cada corte (s)", duracao_corte_min + 1, 20, 5) | |
zoom = st.slider("Zoom no vídeo principal", 1.0, 2.0, 1.0, 0.1) | |
blur_strength = st.slider("Blur no fundo", 1, 50, 10) | |
velocidade_cortes = st.slider("Velocidade dos cortes", 0.5, 2.0, 1.0, 0.1) | |
velocidade_final = st.slider("Velocidade final", 0.5, 2.0, 1.0, 0.1) | |
crf_value = st.slider("Qualidade CRF", 18, 30, 23) | |
st.write("## Texto no Vídeo") | |
ativar_texto = st.checkbox("Ativar texto", value=False) | |
if ativar_texto: | |
texto_personalizado = st.text_input("Texto (emojis permitidos)", "🔥 Siga para mais!") | |
posicao_texto = st.selectbox("Posição do texto", ["Topo", "Centro", "Base"]) | |
duracao_texto = st.radio("Duração do texto", ["Vídeo todo", "Apenas primeiros segundos"]) | |
segundos_texto = 5 | |
if duracao_texto == "Apenas primeiros segundos": | |
segundos_texto = st.slider("Segundos", 1, 30, 5) | |
cor_texto = st.color_picker("Cor do texto", "#FFFF00") | |
cor_sombra = st.color_picker("Cor da sombra", "#000000") | |
tamanho_texto = st.slider("Tamanho da fonte", 20, 100, 60, step=2) | |
st.write("## Filtros no fundo") | |
ativar_blur_fundo = st.checkbox("Ativar blur no fundo", value=True) | |
ativar_sepia = st.checkbox("Sépia", False) | |
ativar_granulado = st.checkbox("Granulado", False) | |
ativar_pb = st.checkbox("Preto e branco", False) | |
ativar_vignette = st.checkbox("Vignette", False) | |
with st.expander("🎨 Filtros Avançados"): | |
ativar_brilho = st.checkbox("Brilho extra", False) | |
ativar_contraste = st.checkbox("Contraste forte (cinemático)", False) | |
ativar_colorboost = st.checkbox("Cores intensas", False) | |
ativar_azul = st.checkbox("Filtro Azul Frio", False) | |
ativar_quente = st.checkbox("Filtro Quente (laranja)", False) | |
ativar_desaturar = st.checkbox("Desaturação parcial", False) | |
ativar_vhs = st.checkbox("Efeito VHS Leve", False) | |
st.write("## Outros efeitos") | |
ativar_espelhar = st.checkbox("Espelhar vídeo", True) | |
ativar_filtro_cor = st.checkbox("Ajuste de cor", True) | |
remover_borda = st.checkbox("Remover borda do vídeo") | |
tamanho_borda = st.slider("Tamanho da borda (px)", 0, 200, 0, 5) | |
ativar_borda_personalizada = st.checkbox("Borda personalizada", False) | |
if ativar_borda_personalizada: | |
cor_borda = st.color_picker("Cor da borda", "#FF0000") | |
animacao_borda = st.selectbox("Animação da borda", ["Nenhuma", "Borda Pulsante", "Cor Animada", "Neon", "Ondulada"]) | |
st.write("## Deseja salvar os vídeos no gerenciador de arquivos?") | |
salvar_no_gerenciador = st.checkbox("Salvar ao invés de oferecer download") | |
CATEGORIAS = ["AVATAR WORLD", "BLOX FRUITS", "TOCA LIFE"] | |
categoria_selecionada = None | |
if salvar_no_gerenciador: | |
categoria_selecionada = st.radio("Escolha a categoria:", CATEGORIAS, index=0) | |
os.makedirs(categoria_selecionada, exist_ok=True) | |
if st.button("Gerar Vídeo(s)"): | |
if not cortes: | |
st.error("❌ Envie os vídeos de cortes.") | |
else: | |
with st.spinner("🎬 Processando..."): | |
progresso = st.progress(0) | |
temp_dir = tempfile.mkdtemp() | |
try: | |
# ---------- SALVAR CORTES ---------- | |
cortes_names = [] | |
for idx, corte in enumerate(cortes): | |
path_in = os.path.join(temp_dir, f"corte_in_{idx}.mp4") | |
path_out = os.path.join(temp_dir, f"corte_padrao_{idx}.mp4") | |
with open(path_in, "wb") as f: | |
f.write(corte.read()) | |
subprocess.run([ | |
"ffmpeg", "-i", path_in, | |
"-vf", "scale=1280:720:force_original_aspect_ratio=decrease,scale=trunc(iw/2)*2:trunc(ih/2)*2", | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", path_out | |
], check=True, stderr=subprocess.PIPE) | |
cortes_names.append(path_out) | |
# ---------- SALVAR TUTORIAIS ---------- | |
tutorials_salvos = [] | |
if videos_tutorial: | |
for idx, tut in enumerate(videos_tutorial): | |
t_path = os.path.join(temp_dir, f"tutorial_salvo_{idx}.mp4") | |
with open(t_path, "wb") as f: | |
f.write(tut.read()) | |
tutorials_salvos.append(t_path) | |
# ---------- SALVAR FUNDOS ---------- | |
fundos_salvos = [] | |
if videos_fundo: | |
for idx, fundo in enumerate(videos_fundo): | |
fundo_ext = os.path.splitext(fundo.name)[-1].lower() | |
fundo_path = os.path.join(temp_dir, f"fundo_salvo_{idx}{fundo_ext}") | |
with open(fundo_path, "wb") as f: | |
f.write(fundo.read()) | |
fundos_salvos.append(fundo_path) | |
# ---------- LOOP PARA GERAR VÍDEOS ---------- | |
for n in range(num_videos_finais): | |
progresso.progress(5 + n * 5) | |
random.shuffle(cortes_names) | |
tempo_total = 0 | |
cortes_prontos = [] | |
cortes_usados = [] | |
# ---------- APLICAR OU SORTEAR CONFIGURAÇÕES ---------- | |
if aleatorizar and num_videos_finais > 1: | |
duracao_corte_min_n = random.randint(1, 5) | |
duracao_corte_max_n = random.randint(duracao_corte_min_n + 1, 8) | |
zoom_n = round(random.uniform(1.0, 1.5), 2) | |
blur_strength_n = random.randint(5, 30) | |
velocidade_cortes_n = round(random.uniform(0.8, 1.5), 2) | |
velocidade_final_n = round(random.uniform(0.8, 1.3), 2) | |
# ----- FILTROS ----- | |
todos_filtros = [ | |
"blur_fundo", "sepia", "granulado", "pb", "vignette", | |
"brilho", "contraste", "colorboost", "azul", "quente", "desaturar", "vhs" | |
] | |
qtd_filtros = random.randint(1, 4) | |
filtros_escolhidos = random.sample(todos_filtros, qtd_filtros) | |
ativar_blur_fundo_n = "blur_fundo" in filtros_escolhidos | |
ativar_sepia_n = "sepia" in filtros_escolhidos | |
ativar_granulado_n = "granulado" in filtros_escolhidos | |
ativar_pb_n = "pb" in filtros_escolhidos | |
ativar_vignette_n = "vignette" in filtros_escolhidos | |
ativar_brilho_n = "brilho" in filtros_escolhidos | |
ativar_contraste_n = "contraste" in filtros_escolhidos | |
ativar_colorboost_n = "colorboost" in filtros_escolhidos | |
ativar_azul_n = "azul" in filtros_escolhidos | |
ativar_quente_n = "quente" in filtros_escolhidos | |
ativar_desaturar_n = "desaturar" in filtros_escolhidos | |
ativar_vhs_n = "vhs" in filtros_escolhidos | |
# ----- OUTROS EFEITOS ----- | |
ativar_espelhar_n = random.choice([True, False]) | |
# Corrigido: só aleatoriza borda se ativada | |
if remover_borda: | |
remover_borda_n = random.choice([True, False]) | |
tamanho_borda_n = random.randint(0, 50) | |
else: | |
remover_borda_n = False | |
tamanho_borda_n = 0 | |
else: | |
duracao_corte_min_n = duracao_corte_min | |
duracao_corte_max_n = duracao_corte_max | |
zoom_n = zoom | |
blur_strength_n = blur_strength | |
velocidade_cortes_n = velocidade_cortes | |
velocidade_final_n = velocidade_final | |
ativar_blur_fundo_n = ativar_blur_fundo | |
ativar_sepia_n = ativar_sepia | |
ativar_granulado_n = ativar_granulado | |
ativar_pb_n = ativar_pb | |
ativar_vignette_n = ativar_vignette | |
ativar_brilho_n = ativar_brilho | |
ativar_contraste_n = ativar_contraste | |
ativar_colorboost_n = ativar_colorboost | |
ativar_azul_n = ativar_azul | |
ativar_quente_n = ativar_quente | |
ativar_desaturar_n = ativar_desaturar | |
ativar_vhs_n = ativar_vhs | |
ativar_espelhar_n = ativar_espelhar | |
remover_borda_n = remover_borda | |
tamanho_borda_n = tamanho_borda | |
# ---------- ESCOLHER FUNDO ALEATÓRIO ---------- | |
if fundos_salvos: | |
fundo_path = random.choice(fundos_salvos) | |
fundo_ext = os.path.splitext(fundo_path)[-1].lower() | |
fundo_convertido = os.path.join(temp_dir, f"fundo_convertido_{n}.mp4") | |
if fundo_ext in [".mp4", ".mov"]: | |
subprocess.run([ | |
"ffmpeg", "-i", fundo_path, "-vf", | |
"scale='if(gt(iw/ih,720/1280),max(720,iw),-2)':'if(gt(iw/ih,720/1280),-2,max(1280,ih))'," | |
"scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280,fps=30", | |
"-t", str(duracao_final), "-preset", "ultrafast", "-crf", "25", | |
fundo_convertido | |
], check=True, stderr=subprocess.PIPE) | |
else: | |
movimento = "scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280," | |
subprocess.run([ | |
"ffmpeg", "-loop", "1", "-i", fundo_path, | |
"-c:v", "libx264", "-t", str(duracao_final), | |
"-pix_fmt", "yuv420p", | |
"-vf", movimento + "fps=30", | |
"-preset", "ultrafast", "-crf", "25", | |
fundo_convertido | |
], check=True, stderr=subprocess.PIPE) | |
else: | |
fundo_convertido = os.path.join(temp_dir, f"fundo_preto_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-f", "lavfi", "-i", "color=black:s=720x1280:d=600", | |
"-t", str(duracao_final), | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "25", | |
fundo_convertido | |
], check=True, stderr=subprocess.PIPE) | |
# ---------- ESCOLHER TUTORIAL ALEATÓRIO ---------- | |
if tutorials_salvos: | |
tutorial_path = random.choice(tutorials_salvos) | |
tutorial_mp4 = os.path.join(temp_dir, f"tutorial_conv_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-i", tutorial_path, "-vf", "scale=720:1280,fps=30", | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), | |
"-y", tutorial_mp4 | |
], check=True, stderr=subprocess.PIPE) | |
else: | |
tutorial_mp4 = None | |
# ---------- GERAR CORTES CURTOS COM DURAÇÃO ALEATÓRIA ---------- | |
tentativas = 0 | |
max_tentativas = 100 | |
while tempo_total < duracao_final and tentativas < max_tentativas: | |
tentativas += 1 | |
random.shuffle(cortes_names) | |
for c in cortes_names: | |
dur = subprocess.run([ | |
"ffprobe", "-v", "error", "-show_entries", "format=duration", | |
"-of", "default=noprint_wrappers=1:nokey=1", c | |
], stdout=subprocess.PIPE).stdout.decode().strip() | |
try: | |
d = float(dur) | |
if d < duracao_corte_min_n + 0.5: | |
continue | |
duracao_aleatoria = random.uniform(duracao_corte_min_n, min(d, duracao_corte_max_n)) | |
ini = random.uniform(0, d - duracao_aleatoria) | |
repetido = False | |
for usado in cortes_usados: | |
mesmo_video = c == usado[0] | |
sobreposicao = abs(ini - usado[1]) < 0.5 | |
if mesmo_video and sobreposicao: | |
repetido = True | |
break | |
if repetido: | |
continue | |
out = os.path.join(temp_dir, f"cut_{random.randint(1000,9999)}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-ss", str(ini), "-i", c, "-t", str(duracao_aleatoria), | |
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2", | |
"-an", "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", out | |
], check=True, stderr=subprocess.PIPE) | |
cortes_prontos.append(out) | |
cortes_usados.append((c, ini, duracao_aleatoria)) | |
tempo_total += duracao_aleatoria / velocidade_cortes_n | |
if tempo_total >= duracao_final: | |
break | |
except: | |
continue | |
if tempo_total >= duracao_final: | |
break | |
lista = os.path.join(temp_dir, f"lista_{n}.txt") | |
with open(lista, "w") as f: | |
for c in cortes_prontos: | |
f.write(f"file '{c}'\n") | |
video_raw = os.path.join(temp_dir, f"video_raw_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-f", "concat", "-safe", "0", "-i", lista, | |
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2", | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw | |
], check=True, stderr=subprocess.PIPE) | |
# ---------- FILTROS E TEXTO ---------- | |
filtros_main = ["scale=720:1280:force_original_aspect_ratio=decrease"] | |
if zoom_n != 1.0: | |
filtros_main.append(f"scale=iw*{zoom_n}:ih*{zoom_n}") | |
filtros_main.append(f"setpts=PTS/{velocidade_cortes_n}") | |
if ativar_espelhar_n: | |
filtros_main.append("hflip") | |
if remover_borda_n and tamanho_borda_n > 0: | |
filtros_main.append(f"crop=in_w-{tamanho_borda_n*2}:in_h-{tamanho_borda_n*2}") | |
if ativar_filtro_cor: | |
filtros_main.append("eq=contrast=1.1:saturation=1.2") | |
filtros_main.append("scale=trunc(iw/2)*2:trunc(ih/2)*2") | |
if ativar_borda_personalizada: | |
cor_ffmpeg = f"0x{cor_borda.lstrip('#')}FF" | |
drawbox = f"drawbox=x=0:y=0:w=iw:h=ih:color={cor_ffmpeg}:t=5" | |
filtros_main.append(drawbox) | |
filtro_complex = ( | |
f"[0:v]scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280" | |
) | |
if ativar_blur_fundo_n: | |
filtro_complex += f",boxblur={blur_strength_n}:1" | |
if ativar_sepia_n: | |
filtro_complex += ",colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131" | |
if ativar_granulado_n: | |
filtro_complex += ",noise=alls=20:allf=t+u" | |
if ativar_pb_n: | |
filtro_complex += ",hue=s=0.3" | |
if ativar_vignette_n: | |
filtro_complex += ",vignette" | |
if ativar_brilho_n: | |
filtro_complex += ",eq=brightness=0.05" | |
if ativar_contraste_n: | |
filtro_complex += ",eq=contrast=1.3" | |
if ativar_colorboost_n: | |
filtro_complex += ",eq=saturation=1.5" | |
if ativar_azul_n: | |
filtro_complex += ",colorbalance=bs=0.5" | |
if ativar_quente_n: | |
filtro_complex += ",colorbalance=rs=0.3" | |
if ativar_desaturar_n: | |
filtro_complex += ",hue=s=0.5" | |
if ativar_vhs_n: | |
filtro_complex += ",noise=alls=10:allf=t+u,format=yuv420p" | |
filtro_complex += "[blur];" | |
filtro_complex += f"[1:v]{','.join(filtros_main)}[zoomed];" | |
filtro_complex += "[blur][zoomed]overlay=(W-w)/2:(H-h)/2[base]" | |
if ativar_texto and texto_personalizado.strip(): | |
y_pos = "100" if posicao_texto == "Topo" else "(h-text_h)/2" if posicao_texto == "Centro" else "h-text_h-100" | |
enable = f":enable='lt(t\\,{segundos_texto})'" if duracao_texto == "Apenas primeiros segundos" else "" | |
texto_clean = texto_personalizado.replace(":", "\\:").replace("'", "\\'") | |
filtro_complex += f";[base]drawtext=text='{texto_clean}':" | |
filtro_complex += ( | |
f"fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:" | |
f"fontcolor={cor_texto}:fontsize={tamanho_texto}:" | |
f"shadowcolor={cor_sombra}:shadowx=3:shadowy=3:" | |
f"x=(w-text_w)/2:y={y_pos}{enable}[final]" | |
) | |
else: | |
filtro_complex += ";[base]null[final]" | |
video_editado = os.path.join(temp_dir, f"video_editado_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-i", fundo_convertido, "-i", video_raw, | |
"-filter_complex", filtro_complex, | |
"-map", "[final]", | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), | |
video_editado | |
], check=True, stderr=subprocess.PIPE) | |
# ---------- ACELERAR VÍDEO ---------- | |
video_acelerado = os.path.join(temp_dir, f"video_acelerado_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-y", "-i", video_editado, "-an", | |
"-filter:v", f"setpts=PTS/{velocidade_final_n}", | |
"-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), | |
video_acelerado | |
], check=True, stderr=subprocess.PIPE) | |
video_final_raw = video_acelerado | |
# ---------- INSERIR TUTORIAL ---------- | |
if tutorial_mp4: | |
dur_proc = subprocess.run([ | |
"ffprobe", "-v", "error", "-show_entries", "format=duration", | |
"-of", "default=noprint_wrappers=1:nokey=1", video_acelerado | |
], stdout=subprocess.PIPE) | |
dur_f = float(dur_proc.stdout.decode().strip()) | |
pt = dur_f / 2 if dur_f < 10 else random.uniform(5, dur_f - 5) | |
part1 = os.path.join(temp_dir, f"part1_{n}.mp4") | |
part2 = os.path.join(temp_dir, f"part2_{n}.mp4") | |
subprocess.run([ | |
"ffmpeg", "-i", video_acelerado, "-ss", "0", "-t", str(pt), | |
"-c:v", "libx264", "-preset", "ultrafast", part1 | |
], check=True, stderr=subprocess.PIPE) | |
subprocess.run([ | |
"ffmpeg", "-i", video_acelerado, "-ss", str(pt), | |
"-c:v", "libx264", "-preset", "ultrafast", part2 | |
], check=True, stderr=subprocess.PIPE) | |
final_txt = os.path.join(temp_dir, f"final_{n}.txt") | |
with open(final_txt, "w") as f: | |
f.write(f"file '{part1}'\nfile '{tutorial_mp4}'\nfile '{part2}'\n") | |
video_final_raw = os.path.join(temp_dir, f"video_final_raw_{n}.mp4") | |
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) | |
# ---------- ADICIONAR MÚSICA ---------- | |
dur_proc = subprocess.run([ | |
"ffprobe", "-v", "error", "-show_entries", "format=duration", | |
"-of", "default=noprint_wrappers=1:nokey=1", video_final_raw | |
], stdout=subprocess.PIPE) | |
dur_video_real = float(dur_proc.stdout.decode().strip()) | |
if musica: | |
musica_path = os.path.join(temp_dir, "musica_original.mp3") | |
with open(musica_path, "wb") as f: | |
f.write(musica.read()) | |
musica_cortada = os.path.join(temp_dir, f"musica_cortada_{n}.aac") | |
subprocess.run([ | |
"ffmpeg", "-i", musica_path, "-ss", "0", "-t", str(dur_video_real), | |
"-vn", "-acodec", "aac", "-y", musica_cortada | |
], check=True, stderr=subprocess.PIPE) | |
final_name = f"video_final_{n}_{int(time.time())}.mp4" | |
subprocess.run([ | |
"ffmpeg", "-i", video_final_raw, "-i", musica_cortada, | |
"-map", "0:v:0", "-map", "1:a:0", | |
"-c:v", "copy", "-c:a", "aac", | |
"-shortest", final_name | |
], check=True, stderr=subprocess.PIPE) | |
else: | |
final_name = f"video_final_{n}_{int(time.time())}.mp4" | |
shutil.copy(video_final_raw, final_name) | |
# ---------- DOWNLOAD OU SALVAR ---------- | |
if salvar_no_gerenciador and categoria_selecionada: | |
destino = os.path.join("uploaded_files", categoria_selecionada, final_name) | |
os.makedirs(os.path.join("uploaded_files", categoria_selecionada), exist_ok=True) | |
shutil.move(final_name, destino) | |
st.success(f"✅ Vídeo {n+1} salvo na categoria '{categoria_selecionada}'.") | |
else: | |
st.video(final_name) | |
with open(final_name, "rb") as f: | |
st.download_button(f"📥 Baixar vídeo {n+1}", f, file_name=final_name) | |
progresso.progress(100) | |
st.success("✅ Todos os vídeos foram gerados com sucesso!") | |
except subprocess.CalledProcessError as e: | |
st.error(f"❌ Erro ao gerar vídeo:\n\n{e.stderr.decode(errors='ignore')}") | |
finally: | |
shutil.rmtree(temp_dir) | |
elif pagina == "📂 Gerenciador de Arquivos": | |
st.header("📂 Vídeos Salvos por Categoria") | |
BASE_FOLDER = "uploaded_files" | |
CATEGORIES = ["AVATAR WORLD", "BLOX FRUITS", "TOCA LIFE"] | |
st.markdown(""" | |
<style> | |
.file-box { | |
border: 1px solid #ccc; | |
padding: 10px; | |
margin-bottom: 10px; | |
border-radius: 5px; | |
background-color: #f9f9f9; | |
} | |
.file-name { | |
font-size: 16px; | |
font-weight: bold; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
for categoria in CATEGORIES: | |
folder = os.path.join(BASE_FOLDER, categoria) | |
os.makedirs(folder, exist_ok=True) | |
arquivos = os.listdir(folder) | |
st.subheader(f"📁 {categoria}") | |
if not arquivos: | |
st.info("Nenhum vídeo salvo nessa categoria.") | |
else: | |
for file in arquivos: | |
file_path = os.path.join(folder, file) | |
st.markdown("<div class='file-box'>", unsafe_allow_html=True) | |
st.markdown(f"<div class='file-name'>{file}</div>", unsafe_allow_html=True) | |
assistir = st.button(f"▶ Assistir {file}", key=f"assistir_{categoria}_{file}") | |
if assistir: | |
st.video(file_path) | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
with open(file_path, "rb") as f_obj: | |
st.download_button("⬇ Download", f_obj, file_name=file, key=f"down_{categoria}_{file}") | |
with col2: | |
if st.button("🗑 Excluir", key=f"delete_{categoria}_{file}"): | |
os.remove(file_path) | |
st.success(f"Arquivo '{file}' excluído.") | |
st.rerun() | |
with col3: | |
with open(file_path, "rb") as f_obj: | |
if st.download_button("⬇ Baixar & Apagar", f_obj, file_name=file, key=f"down_del_{categoria}_{file}"): | |
os.remove(file_path) | |
st.success(f"Arquivo '{file}' baixado e excluído.") | |
st.rerun() | |
st.markdown("</div>", unsafe_allow_html=True) | |