File size: 3,975 Bytes
6e4bcb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""
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()