Gyrvg / app.py
Merlimhhs's picture
Update app.py
df02095 verified
import os
import shutil
import subprocess
import tempfile
from pathlib import Path
from typing import List, Tuple
import gradio as gr
# =========================================================
# Batch Cinematic Processor
# - Upload many videos
# - Interpolate to 60 fps using FFmpeg minterpolate
# - Upscale to 1440p
# - Return a ZIP with all processed videos
#
# Requirements:
# pip install gradio
# ffmpeg installed and available in PATH
#
# Notes:
# - No reverse is applied here.
# - This is designed for CPU-friendly, stable batch processing.
# =========================================================
def run_ffmpeg(cmd: List[str]) -> None:
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if proc.returncode != 0:
raise RuntimeError(proc.stderr[-5000:])
def safe_stem(path: str) -> str:
return Path(path).stem.replace(" ", "_")
def process_one_video(
input_path: str,
output_dir: Path,
target_width: int,
target_height: int,
target_fps: int,
crf: int,
preset: str,
) -> Path:
input_path = str(Path(input_path).resolve())
name = safe_stem(input_path)
output_path = output_dir / f"{name}_{target_fps}fps_{target_height}p.mp4"
# Minterpolate first, then upscale.
# This tends to preserve motion continuity better than upscaling first.
vf = (
f"minterpolate=fps={target_fps}:mi_mode=mci:mc_mode=aobmc:"
f"me_mode=bidir:me=epzs:vsbmc=1:scd=fdiff,"
f"scale={target_width}:{target_height}:flags=lanczos"
)
cmd = [
"ffmpeg",
"-y",
"-i", input_path,
"-vf", vf,
"-c:v", "libx264",
"-preset", preset,
"-crf", str(crf),
"-pix_fmt", "yuv420p",
"-movflags", "+faststart",
str(output_path),
]
run_ffmpeg(cmd)
return output_path
def batch_process(
videos: List[str],
target_width: int,
target_height: int,
target_fps: int,
crf: int,
preset: str,
progress=gr.Progress(track_tqdm=False),
) -> Tuple[str, str]:
if not videos:
raise gr.Error("Envie pelo menos um vídeo.")
# Normalize values.
target_width = int(target_width)
target_height = int(target_height)
target_fps = int(target_fps)
crf = int(crf)
if target_width % 2 != 0:
target_width += 1
if target_height % 2 != 0:
target_height += 1
workdir = Path(tempfile.mkdtemp(prefix="batch_cinematic_"))
outdir = workdir / "outputs"
outdir.mkdir(parents=True, exist_ok=True)
processed_files: List[Path] = []
total = len(videos)
for idx, video in enumerate(videos, start=1):
progress((idx - 1) / total, desc=f"Processando {idx}/{total}: {Path(video).name}")
processed = process_one_video(
video,
outdir,
target_width=target_width,
target_height=target_height,
target_fps=target_fps,
crf=crf,
preset=preset,
)
processed_files.append(processed)
progress(0.95, desc="Montando ZIP...")
zip_base = workdir / "batch_output"
zip_path = shutil.make_archive(str(zip_base), "zip", root_dir=outdir)
progress(1.0, desc="Concluído")
summary = (
f"{len(processed_files)} vídeo(s) processado(s).\n"
f"Saída: {target_fps} fps | {target_width}x{target_height}\n"
f"Arquivo ZIP pronto."
)
return zip_path, summary
with gr.Blocks(title="Batch Cinematic Processor") as demo:
gr.Markdown(
"# Batch Cinematic Processor\n"
"Envie vários vídeos e receba um ZIP com tudo processado em 60 fps e 1440p.\n"
"Sem reverse automático."
)
with gr.Row():
videos = gr.File(
label="Vídeos de entrada",
file_count="multiple",
type="filepath",
file_types=[".mp4", ".mov", ".mkv", ".webm", ".avi"],
)
with gr.Row():
target_width = gr.Number(label="Largura", value=2560, precision=0)
target_height = gr.Number(label="Altura", value=1440, precision=0)
target_fps = gr.Number(label="FPS de saída", value=60, precision=0)
with gr.Row():
crf = gr.Slider(label="Qualidade (CRF menor = melhor)", minimum=14, maximum=24, value=18, step=1)
preset = gr.Dropdown(
label="Preset do encoder",
choices=["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower"],
value="slow",
)
process_btn = gr.Button("Processar lote")
with gr.Row():
zip_out = gr.File(label="ZIP final")
info = gr.Textbox(label="Status", lines=4)
process_btn.click(
fn=batch_process,
inputs=[videos, target_width, target_height, target_fps, crf, preset],
outputs=[zip_out, info],
)
if __name__ == "__main__":
demo.launch()