ganhos / app.py
pcdoido2's picture
Update app.py
ee975b1 verified
Raw
History Blame Contribute Delete
10.3 kB
import streamlit as st
import os
import subprocess
import tempfile
import shutil
import base64
import random
import string
from datetime import datetime, timedelta
st.set_page_config(page_title="Turbo Spinner Shorts PRO", layout="centered")
st.title("🎬 Turbo Spinner Shorts PRO (TikTok Safe)")
# Diretório temporário
temp_dir = tempfile.mkdtemp()
uploaded_file = st.file_uploader("📤 Envie seu vídeo", type=["mp4", "mov", "avi", "mkv"])
def run_ffmpeg(cmd, label, output_file):
progress_bar = st.progress(0, text=f"⏳ {label} em andamento...")
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
percent = 0
while process.poll() is None:
if percent < 100:
percent += 2
progress_bar.progress(percent, text=f"⏳ {label} {percent}%")
process.wait()
progress_bar.progress(100, text=f"✅ {label} finalizado!")
return os.path.exists(output_file) and os.path.getsize(output_file) > 0
def make_download_button(file_path, label):
with open(file_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
href = f'<a href="data:file/mp4;base64,{b64}" download="{os.path.basename(file_path)}">⬇️ {label}</a>'
st.markdown(href, unsafe_allow_html=True)
def random_string(n=6):
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=n))
def gerar_nome_saida():
agora = datetime.now()
data = agora.strftime("%Y%m%d")
hora = agora.strftime("%H%M%S")
return f"lv_0_{data}{hora}.mp4"
def get_duration(video_path):
try:
result = subprocess.run(
['ffprobe', '-v', 'error', '-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1', video_path],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
return float(result.stdout)
except Exception:
return 15.0
def gerar_microdrift(dur, base_speed=1.0):
expr = f"{1/base_speed}*PTS"
for _ in range(2):
start = round(random.uniform(0, max(1, dur-2)), 2)
end = round(start + random.uniform(0.5, 2), 2)
if end > dur:
end = dur
factor = random.choice([0.98, 0.99, 1.01, 1.02])
expr = f"if(between(T,{start},{end}),{factor}*{expr},{expr})"
return expr
if uploaded_file is not None:
input_path = os.path.join(temp_dir, uploaded_file.name)
with open(input_path, "wb") as f:
f.write(uploaded_file.getbuffer())
st.success("✅ Vídeo carregado com sucesso!")
dur = get_duration(input_path)
st.subheader("🚀 Funções Avançadas")
opt_semantic_noise = st.checkbox("Injeção de Ruído Semântico")
opt_dynamic_frame = st.checkbox("Molduras Dinâmicas")
st.subheader("✂️ Corte do Vídeo")
cut_start = st.slider(
"Cortar do início (segundos)",
0.0,
float(max(0.0, dur - 1)),
0.0,
0.1
)
cut_end = st.slider(
"Cortar do final (segundos)",
0.0,
float(max(0.0, dur - cut_start - 1)),
0.0,
0.1
)
st.subheader("🎵 Pitch Shift")
enable_pitch = st.checkbox("Ativar Pitch Shift")
pitch_value = st.slider(
"Tom do áudio",
0.8,
1.2,
1.0,
0.01
)
res = st.radio("📱 Resolução final", ["720x1280", "1080x1920"], index=0)
w, h = res.split("x")
preset = st.radio("⚡ Escolha um preset:", ["Low", "Médio", "High"], index=1)
preset_defaults = {
"Low": {
"speed_auto": False, "zoom_auto": False, "tilt": False, "eq": False,
"bw": False, "sepia": False, "sharpen": False, "fade": False, "border": False
},
"Médio": {
"speed_auto": True, "zoom_auto": True, "tilt": False, "eq": True,
"bw": False, "sepia": False, "sharpen": True, "fade": True, "border": False
},
"High": {
"speed_auto": True, "zoom_auto": True, "tilt": True, "eq": True,
"bw": True, "sepia": True, "sharpen": True, "fade": True, "border": True
}
}
defaults = preset_defaults[preset]
st.subheader("🎨 Efeitos ativos")
speed_val = st.slider("⏩ Velocidade base", 0.5, 2.0, 1.0, 0.05)
opt_speed_auto = st.checkbox("Velocidade automática", value=defaults["speed_auto"])
zoom_val = st.slider("🔍 Zoom base", 1.0, 1.5, 1.0, 0.01)
opt_zoom_auto = st.checkbox("Zoom automático", value=defaults["zoom_auto"])
opt_tilt = st.checkbox("Tilt (ângulo leve)", value=defaults["tilt"])
opt_eq = st.checkbox("Brilho / Contraste / Saturação", value=defaults["eq"])
opt_bw = st.checkbox("Filtro de desaturação leve", value=defaults["bw"])
opt_sepia = st.checkbox("Filtro sépia suave", value=defaults["sepia"])
opt_sharpen = st.checkbox("Sharpen (nitidez extra)", value=defaults["sharpen"])
opt_fade = st.checkbox("Fade in/out (máx 1s)", value=defaults["fade"])
opt_border = st.checkbox("Borda preta", value=defaults["border"])
hard_mode = st.checkbox("Ativar randomização HARD (reescrita de metadados)")
if st.button("🎲 Gerar Spin"):
out_name = gerar_nome_saida()
out_path = os.path.join(temp_dir, out_name)
first_pass = os.path.join(temp_dir, "first_pass.mp4")
effects = []
expr = gerar_microdrift(dur, base_speed=speed_val)
effects.append(f"setpts='{expr}'")
if opt_zoom_auto:
zoom_choice = round(random.uniform(1.01, 1.25), 2)
effects.append(f"scale=iw*{zoom_choice}:ih*{zoom_choice},crop={w}:{h}")
elif zoom_val > 1.0:
effects.append(f"scale=iw*{zoom_val}:ih*{zoom_val},crop={w}:{h}")
else:
effects.append(f"scale={w}:{h}:force_original_aspect_ratio=increase,crop={w}:{h}")
if opt_tilt:
tilt = random.choice([1, 2, -1, -2])
effects.append(f"rotate={tilt}*PI/180:ow={w}:oh={h}:c=black")
if opt_eq:
effects.append(
f"eq=brightness={round(random.uniform(-0.1,0.1),2)}:"
f"contrast={round(random.uniform(0.9,1.1),2)}:"
f"saturation={round(random.uniform(0.9,1.1),2)}"
)
if opt_bw:
effects.append("hue=s=0.7")
if opt_sepia:
effects.append("colorchannelmixer=.85:.3:.15:0:.3:.85:.15:0:.2:.3:.7")
if opt_sharpen:
effects.append("unsharp=5:5:1.0:5:5:0.0")
if opt_fade:
fade_in = min(1, dur * 0.1)
fade_out = min(1, dur * 0.1)
effects.append(
f"fade=t=in:st=0:d={fade_in},"
f"fade=t=out:st={max(dur-fade_out,0)}:d={fade_out}"
)
if opt_border:
effects.append(f"pad={w}:{h}:(ow-iw)/2:(oh-ih)/2:black")
effects.append("setsar=1")
# 🔥 Injeção de ruído semântico
if opt_semantic_noise:
effects.append("noise=alls=3:allf=t")
effects.append(
f"eq=brightness={round(random.uniform(-0.02,0.02),3)}:"
f"contrast={round(random.uniform(0.98,1.02),3)}:"
f"saturation={round(random.uniform(0.98,1.02),3)}"
)
crop_w = int(w) - random.randint(0, 3)
crop_h = int(h) - random.randint(0, 3)
effects.append(f"crop={crop_w}:{crop_h}")
# 🎨 Molduras dinâmicas
if opt_dynamic_frame:
emoji = random.choice(["🔥", "⚡", "✨", "👀", "🚀"])
x_pos = random.randint(20, int(w) - 80)
y_pos = random.randint(20, int(h) - 80)
effects.append(
f"drawtext=text='{emoji}':"
f"x={x_pos}:y={y_pos}:"
f"fontsize=40:"
f"fontcolor=white@0.3"
)
effects.append(f"pad={w}:{h}:(ow-iw)/2:(oh-ih)/2:black@0.3")
vf = ",".join(effects)
final_duration = dur - cut_start - cut_end
if final_duration <= 0:
st.error("❌ Corte inválido.")
st.stop()
if enable_pitch and pitch_value != 1.0:
af_filter = f"asetrate=48000*{pitch_value},aresample=48000"
else:
af_filter = "aresample=48000"
cmd1 = [
"ffmpeg",
"-y",
"-ss", str(cut_start),
"-i", input_path,
"-t", str(final_duration),
"-vf", vf,
"-af", af_filter,
"-c:v", "libx264",
"-pix_fmt", "yuv420p",
"-preset", "fast",
"-crf", "23",
"-c:a", "aac",
"-b:a", "128k",
"-shortest",
"-vsync", "vfr",
first_pass
]
success1 = run_ffmpeg(cmd1, "Efeitos", first_pass)
if not success1:
st.error("❌ Erro no processamento inicial.")
else:
if not hard_mode:
shutil.move(first_pass, out_path)
st.success("🎉 Spin gerado com sucesso!")
st.video(out_path)
make_download_button(out_path, "Download Spin")
else:
crf = str(random.randint(18, 26))
encoder_name = random_string(12)
cmd2 = [
"ffmpeg",
"-y",
"-i", first_pass,
"-map_metadata", "-1",
"-c:v", "libx264",
"-preset", "fast",
"-crf", crf,
"-c:a", "aac",
"-movflags", "faststart",
"-metadata", f"title={random_string(10)}",
"-metadata", f"encoder={encoder_name}",
out_path
]
success2 = run_ffmpeg(cmd2, "Finalizando Spin", out_path)
if success2:
st.success("🎉 Spin gerado com randomização HARD!")
st.video(out_path)
make_download_button(out_path, "Download Spin")
else:
st.error("❌ Erro no passo final.")
if st.button("🗑️ Limpar temporários"):
shutil.rmtree(temp_dir)
st.warning("Arquivos temporários foram apagados.")