Spaces:
Sleeping
Sleeping
| """ | |
| 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() | |