""" BrainGPT — Hugging Face Space (Gradio) build. Web UI wrapper around pipeline_core. Runs on GPU automatically (A10G). API keys come from Space Secrets, so end users don't need their own. """ import os, queue, threading, tempfile import gradio as gr import pipeline_core as pc # Keys live as Space Secrets (Settings → Variables and secrets) KEYS = { "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY", ""), "MINIMAX_API_KEY": os.getenv("MINIMAX_API_KEY", ""), "MINIMAX_GROUP_ID": os.getenv("MINIMAX_GROUP_ID", ""), } DEFAULT_VOICE = os.getenv("MINIMAX_VOICE_ID", "") def _format_report(rep: dict) -> str: lines = [ f"**Segments found:** {rep.get('total_segments', 0)}", f"**Corrected by AI:** {rep.get('corrected_ok', 0)}", f"**Voiced:** {rep.get('voiced_segments', 0)}", ] st = rep.get("stages", {}) if st: lines.append(f"**Transcribe:** {st.get('transcribe', 0)}s · " f"**Render:** {st.get('render', 0)}s") skipped = rep.get("skipped", []) if skipped: lines.append("\n**Skipped / notes:**") lines += [f"- {s}" for s in skipped] else: lines.append("\n_Nothing skipped — full run ✓_") return "\n".join(lines) def process(video_path, voice_id, whisper_model, speed): if not video_path: raise gr.Error("Please upload a video first.") missing = [k for k, v in KEYS.items() if not v] if missing: raise gr.Error("Server is missing API keys: " + ", ".join(missing) + ". (Owner: add them in Settings → Secrets.)") if not voice_id.strip(): raise gr.Error("Enter a MiniMax voice ID.") voice = {"voice_id": voice_id.strip(), "model": "speech-02-hd", "speed": float(speed)} settings = {"whisper_model": whisper_model} out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name q: queue.Queue = queue.Queue() result = {} def run(): try: rep = pc.process_video(video_path, out_path, KEYS, voice, settings, progress=lambda s, d: q.put(("p", s, d)), logfile=None) result["report"] = rep q.put(("done", None, None)) except Exception as e: result["error"] = str(e) q.put(("err", str(e), None)) threading.Thread(target=run, daemon=True).start() yield None, "⏳ Starting…" while True: kind, a, b = q.get() if kind == "p": yield None, f"⏳ {a}: {b}" elif kind == "done": yield out_path, "✅ Done!\n\n" + _format_report(result["report"]) return elif kind == "err": raise gr.Error(a) with gr.Blocks(title="BrainGPT", theme=gr.themes.Soft(primary_hue="violet")) as demo: gr.Markdown("# 🎙 BrainGPT\nReplace your screen-recording voice with a clean AI voice, " "synced to your video.") with gr.Row(): with gr.Column(scale=1): video_in = gr.Video(label="Screen recording", sources=["upload"]) voice_in = gr.Textbox(label="MiniMax Voice ID", value=DEFAULT_VOICE, placeholder="your-voice-id") model_in = gr.Dropdown(label="Transcription model", choices=["base", "small", "large-v3-turbo"], value="large-v3-turbo") speed_in = gr.Slider(label="Voice speed", minimum=0.5, maximum=2.0, value=1.0, step=0.05) go = gr.Button("Generate", variant="primary") with gr.Column(scale=1): status = gr.Markdown("Upload a video and click Generate.") video_out = gr.Video(label="Result (with AI voice)") go.click(process, [video_in, voice_in, model_in, speed_in], [video_out, status]) if __name__ == "__main__": demo.queue().launch()