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