import streamlit as st import subprocess import os import random import tempfile import shutil import time import base64 # ======================= CONFIG GERAL ======================= st.set_page_config(page_title="Shorts Generator", layout="centered") # Abas laterais (igual ao primeiro app) abas = ["🎬 Gerador", "🔐 Admin"] pagina = st.sidebar.radio("Escolha uma pĂĄgina:", abas) # Categorias e estrutura de pastas CATEGORIAS = ["AVATAR WORLD", "99 NIGHTS", "BRAINROT", "TOCA LIFE", "FC MOBILE"] BASE_ASSETS = "assets" for cat in CATEGORIAS: os.makedirs(os.path.join(BASE_ASSETS, cat, "cortes"), exist_ok=True) # ======================= PÁGINA GERADOR ======================= if pagina == "🎬 Gerador": st.title("đŸŽ„ Shorts Generator - Simples") st.markdown("Envie seus vĂ­deos e/ou use cortes salvos por categoria para gerar conteĂșdo com efeitos e controle de duração!") # Seleção de categoria para usar cortes salvos (opcional) categoria_selecionada = st.selectbox( "Escolha a categoria (opcional, para usar cortes jĂĄ salvos):", ["Selecione..."] + CATEGORIAS ) cortes_salvos_paths = [] if categoria_selecionada != "Selecione...": path_cortes_salvos = os.path.join(BASE_ASSETS, categoria_selecionada, "cortes") if os.path.isdir(path_cortes_salvos): cortes_salvos_paths = [ os.path.join(path_cortes_salvos, f) for f in os.listdir(path_cortes_salvos) if f.lower().endswith(".mp4") ] st.caption(f"Encontrados {len(cortes_salvos_paths)} cortes salvos em: {path_cortes_salvos}") # Upload dos vĂ­deos (MANTIDO) cortes = st.file_uploader("Envie os vĂ­deos de cortes", type=["mp4"], accept_multiple_files=True) # ConfiguraçÔes (MANTIDO) num_videos_finais = st.number_input("Quantos vĂ­deos finais gerar?", min_value=1, max_value=10, value=1) duracao_final = st.number_input("Duração final do vĂ­deo (em segundos)", min_value=10, max_value=300, value=30) # Min e Max duração dos cortes (MANTIDO) duracao_min = st.slider("Duração mĂ­nima de cada corte (s)", 1, 30, 3) duracao_max = st.slider("Duração mĂĄxima de cada corte (s)", 1, 60, 5) if duracao_min > duracao_max: st.warning("⚠ A duração mĂ­nima nĂŁo pode ser maior que a mĂĄxima.") # Corte lateral e zoom (MANTIDO) corte_lateral = st.slider("Corte lateral (%)", 0, 50, 0, 5) zoom = st.slider("Zoom Central (1.0 = normal)", 1.0, 2.0, 1.0, 0.1) # Velocidades e qualidade (MANTIDO) velocidade_cortes = st.slider("Velocidade dos cortes", 0.5, 2.0, 1.0, 0.1) velocidade_final = st.slider("Velocidade final do vĂ­deo", 0.5, 2.0, 1.0, 0.1) crf_value = st.slider("Qualidade CRF (menor = melhor qualidade)", 18, 30, 18) # Outros (MANTIDO) st.write("### Outros") ativar_espelhar = st.checkbox("Espelhar VĂ­deo", value=True) ativar_filtro_cor = st.checkbox("Filtro de Cor (Contraste/Saturação)", value=True) # BotĂŁo principal (MANTIDO, agora combinando uploads + salvos) if st.button("Gerar VĂ­deo(s)"): if not cortes and len(cortes_salvos_paths) == 0: st.error("❌ Envie vĂ­deos OU selecione uma categoria com cortes salvos para continuar.") else: with st.spinner('đŸŽ„ Seu vĂ­deo estĂĄ sendo gerado...'): progresso = st.progress(0) temp_dir = tempfile.mkdtemp() try: # === PREPARAR LISTA FINAL DE ARQUIVOS DE CORTE === cortes_names = [] # 1) Arquivos enviados (MANTIDO) if cortes: for idx, corte in enumerate(cortes): nome = os.path.join(temp_dir, f"corte_{idx}_{random.randint(1000,9999)}.mp4") with open(nome, "wb") as f: f.write(corte.read()) cortes_names.append(nome) # 2) Arquivos salvos na categoria (NOVO – usamos os caminhos diretamente) if len(cortes_salvos_paths) > 0: cortes_names.extend(cortes_salvos_paths) if len(cortes_names) == 0: st.error("❌ NĂŁo hĂĄ cortes disponĂ­veis apĂłs combinar uploads e salvos.") shutil.rmtree(temp_dir) st.stop() progresso.progress(10) for n in range(num_videos_finais): cortes_prontos = [] random.shuffle(cortes_names) progresso.progress(10 + int(20 * (n / num_videos_finais))) tempo_total = 0 while tempo_total < duracao_final: for c in cortes_names: dur_proc = subprocess.run([ "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", c ], stdout=subprocess.PIPE) dur = dur_proc.stdout.decode().strip() try: d = float(dur) dur_aleatoria = random.uniform(duracao_min, duracao_max) if d > dur_aleatoria: ini = random.uniform(0, d - dur_aleatoria) out = os.path.join(temp_dir, f"cut_{n}_{random.randint(1000,9999)}.mp4") subprocess.run([ "ffmpeg", "-ss", str(ini), "-i", c, "-t", str(dur_aleatoria), "-an", "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", out ], check=True, stderr=subprocess.PIPE) cortes_prontos.append(out) tempo_total += dur_aleatoria / velocidade_cortes if tempo_total >= duracao_final: break except: continue 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, "-c:v", "libx264", "-preset", "ultrafast", "-crf", "30", video_raw ], check=True, stderr=subprocess.PIPE) progresso.progress(40 + int(20 * (n / num_videos_finais))) filtros_main = [] if corte_lateral > 0: fator = (100 - corte_lateral) / 100 filtros_main.append(f"crop=iw*{fator}:ih:(iw-iw*{fator})/2:0") if zoom != 1.0: filtros_main.append(f"scale=iw*{zoom}:ih*{zoom},crop=iw/{zoom}:ih/{zoom}") filtros_main.append(f"setpts=PTS/{velocidade_cortes}") if ativar_espelhar: filtros_main.append("hflip") if ativar_filtro_cor: filtros_main.append("eq=contrast=1.1:saturation=1.2") filtros_main.append("pad=ceil(iw/2)*2:ceil(ih/2)*2") filtro_final = ",".join(filtros_main) video_editado = os.path.join(temp_dir, f"video_editado_{n}.mp4") subprocess.run([ "ffmpeg", "-i", video_raw, "-vf", filtro_final, "-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), video_editado ], check=True, stderr=subprocess.PIPE) video_final = f"video_final_{n}_{int(time.time())}.mp4" subprocess.run([ "ffmpeg", "-i", video_editado, "-filter:v", f"setpts=PTS/{velocidade_final}", "-t", str(duracao_final), "-c:v", "libx264", "-preset", "ultrafast", "-crf", str(crf_value), "-map_metadata", "-1", "-movflags", "+faststart", video_final ], check=True, stderr=subprocess.PIPE) progresso.progress(90 + int(10 * (n / num_videos_finais))) st.video(video_final) with open(video_final, "rb") as f: st.download_button(f"📅 Baixar VĂ­deo {n+1}", f, file_name=video_final) progresso.progress(100) st.success("✅ Todos os vĂ­deos foram gerados com sucesso!") except subprocess.CalledProcessError as e: st.error(f"Erro durante processamento: {e.stderr.decode()}") except Exception as e: st.error(f"Erro inesperado: {str(e)}") finally: shutil.rmtree(temp_dir) # ======================= PÁGINA ADMIN ======================= elif pagina == "🔐 Admin": st.title("🔐 Área de Administração (CORTES)") # Mesma senha do seu primeiro cĂłdigo: base64 de "admin123" SENHA_CODIFICADA = "YWRtaW4xMjM=" if "autenticado_admin" not in st.session_state: st.session_state.autenticado_admin = False if not st.session_state.autenticado_admin: senha_input = st.text_input("Senha do Admin:", type="password") if st.button("Entrar"): if base64.b64encode(senha_input.encode()).decode() == SENHA_CODIFICADA: st.session_state.autenticado_admin = True st.success("✅ Acesso liberado!") else: st.error("❌ Senha incorreta.") st.stop() if not st.session_state.autenticado_admin: st.stop() # Seleção da categoria e upload de CORTES cat_admin = st.selectbox("Categoria para salvar os cortes:", CATEGORIAS, key="cat_admin_upload") pasta_admin = os.path.join(BASE_ASSETS, cat_admin, "cortes") st.caption(f"Pasta de destino: `{pasta_admin}`") arquivos_admin = st.file_uploader( "Enviar arquivos de CORTES (.mp4) para esta categoria", type=["mp4"], accept_multiple_files=True, key="admin_uploader" ) if arquivos_admin: with st.spinner("⏳ Processando e padronizando cortes..."): for f in arquivos_admin: ext = os.path.splitext(f.name)[1].lower() nome_aleatorio = f"{random.randint(1000000, 9999999)}{ext}" temp_in = os.path.join(tempfile.gettempdir(), f"adm_input_{nome_aleatorio}") with open(temp_in, "wb") as out: out.write(f.read()) final_out = os.path.join(pasta_admin, nome_aleatorio) # Agora salvando o arquivo diretamente, sem padronizar shutil.move(temp_in, final_out) st.success("✅ Cortes enviados, padronizados e salvos com sucesso!") # Listagem e exclusĂŁo st.write("Arquivos existentes nesta categoria:") try: arquivos_existentes = sorted([f for f in os.listdir(pasta_admin) if f.lower().endswith(".mp4")]) except FileNotFoundError: arquivos_existentes = [] if not arquivos_existentes: st.info("Nenhum arquivo encontrado nesta categoria.") else: for arq in arquivos_existentes: col1, col2 = st.columns([5, 1]) with col1: st.markdown(f"`{arq}`") with col2: if st.button("🗑", key=f"del_{cat_admin}_{arq}"): os.remove(os.path.join(pasta_admin, arq)) st.success(f"❌ Arquivo '{arq}' excluĂ­do.") st.rerun()