File size: 4,125 Bytes
18fd051
f2325ad
18fd051
f2325ad
18fd051
802caeb
18fd051
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46b613a
 
 
 
 
 
 
 
 
 
18fd051
802caeb
f2325ad
802caeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2325ad
802caeb
 
 
 
 
 
 
18fd051
46b613a
29bd05d
 
 
 
 
 
 
 
46b613a
 
29bd05d
 
802caeb
 
18fd051
802caeb
18fd051
802caeb
 
18fd051
 
 
 
 
 
f2325ad
18fd051
 
f2325ad
8985e44
f2325ad
 
8985e44
18fd051
 
802caeb
f2325ad
81392ea
18fd051
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8985e44
f2325ad
18fd051
 
 
 
 
 
 
 
81392ea
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
Transcrição de áudio — ZeroGPU Space

Envie um arquivo de áudio (mp3, wav, m4a, etc.) e transcreva com Whisper.
"""
import tempfile
from pathlib import Path
import gradio as gr

# ZeroGPU: decorator é no-op fora do HF
try:
    import spaces
except ImportError:
    class _Spaces:
        def GPU(self, fn=None, **kwargs):
            def decorator(f):
                return f
            return decorator(fn) if fn else decorator
    spaces = _Spaces()


def _fmt_tempo(segundos: float) -> str:
    """Formata segundos como MM:SS ou HH:MM:SS."""
    h = int(segundos // 3600)
    m = int((segundos % 3600) // 60)
    s = int(segundos % 60)
    if h > 0:
        return f"{h:01d}:{m:02d}:{s:02d}"
    return f"{m:01d}:{s:02d}"


@spaces.GPU(duration=180)
def transcrever(audio, modelo: str, idioma: str) -> str:
    """Transcreve áudio enviado com Whisper."""
    try:
        from faster_whisper import WhisperModel

        path = None
        if isinstance(audio, str) and audio and Path(audio).exists():
            path = str(audio)
        elif hasattr(audio, "name"):
            p = getattr(audio, "name", None)
            if p and Path(str(p)).exists():
                path = str(p)
        elif isinstance(audio, bytes) and audio:
            with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
                f.write(audio)
                path = f.name
        if not path:
            return "❌ Envie um arquivo de áudio."

        model = WhisperModel(modelo, device="cuda", compute_type="float16")
        lang = None if idioma == "Auto" else idioma.lower()

        segments, info = model.transcribe(
            path,
            language=lang,
            beam_size=5,
            vad_filter=True,
        )

        resultado = []
        for seg in segments:
            resultado.append({
                "start": seg.start,
                "end": seg.end,
                "text": seg.text.strip(),
            })

        # Timestamp + parágrafos: [MM:SS] texto, linha em branco quando pausa > 1.5s
        PAUSA_PARAGRAFO = 1.5
        linhas = []
        prev_end = 0
        for s in resultado:
            if not s["text"]:
                continue
            if prev_end > 0 and (s["start"] - prev_end) > PAUSA_PARAGRAFO:
                linhas.append("")  # linha em branco = novo parágrafo
            ts = _fmt_tempo(s["start"])
            linhas.append(f"[{ts}] {s['text']}")
            prev_end = s["end"]
        texto = "\n".join(linhas)
        if not texto:
            return "⚠️ Nenhum texto transcrito (áudio sem fala?)."

        return f"Idioma detectado: {info.language}\n\n{texto}"

    except Exception as e:
        return f"❌ Erro: {type(e).__name__}: {e}"


MODELOS = ["tiny", "base", "small", "medium", "large-v3"]
IDIOMAS = ["Auto", "pt", "en", "es", "fr"]

with gr.Blocks(
    title="Transcrição de áudio",
    theme=gr.themes.Soft(),
) as demo:
    gr.Markdown("# 🎙️ Transcrição de áudio")
    gr.Markdown(
        "Envie um arquivo de áudio (mp3, wav, m4a, webm, etc.) e transcreva com Whisper. "
        "**ZeroGPU** — processamento gratuito na nuvem."
    )

    with gr.Row():
        audio = gr.File(
            label="Áudio",
            file_types=[".mp3", ".wav", ".m4a", ".webm", ".opus", ".ogg", ".flac"],
        )
    with gr.Row():
        modelo = gr.Dropdown(
            label="Modelo Whisper",
            choices=MODELOS,
            value="small",
            info="small = bom equilíbrio; large-v3 = mais preciso (mais lento)",
        )
        idioma = gr.Dropdown(
            label="Idioma",
            choices=IDIOMAS,
            value="Auto",
        )

    btn = gr.Button("Transcrever", variant="primary")

    saida = gr.Textbox(
        label="Transcrição",
        lines=15,
        max_lines=30,
    )

    btn.click(
        fn=transcrever,
        inputs=[audio, modelo, idioma],
        outputs=saida,
    )

    gr.Markdown("---")
    gr.Markdown(
        "A transcrição pode levar 1–2 min para iniciar (fila da GPU)."
    )

demo.launch(show_error=True, share=True)