import os import subprocess import shutil import speech_recognition as sr import traceback import wave import contextlib import gradio as gr import tempfile from pathlib import Path import asyncio # ================== Core Functions ================== def find_ffmpeg(): """Finds FFmpeg executable in system PATH.""" ffmpeg_path = shutil.which('ffmpeg') if ffmpeg_path: return ffmpeg_path raise FileNotFoundError("FFmpeg não encontrado. O Space precisa ter FFmpeg instalado.") def get_audio_duration(wav_path): """Get the duration of a WAV file in seconds""" try: with contextlib.closing(wave.open(wav_path, 'r')) as f: return f.getnframes() / float(f.getframerate()) except Exception as e: raise RuntimeError(f"Erro ao obter duração do áudio: {str(e)}") def convert_audio(input_path, ffmpeg_path): """Converts any audio file to WAV format for processing.""" try: if not os.path.exists(input_path): raise FileNotFoundError(f"Arquivo de áudio não encontrado: {input_path}") output_path = input_path + "_converted.wav" command = [ ffmpeg_path, "-y", "-i", input_path, "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", "-hide_banner", "-loglevel", "error", output_path ] result = subprocess.run(command, capture_output=True, text=True, timeout=300) if result.returncode != 0: raise RuntimeError(f"Falha na conversão: {result.stderr}") if not os.path.exists(output_path): raise FileNotFoundError("Arquivo convertido não foi criado") return output_path except Exception as e: raise RuntimeError(f"Erro ao converter áudio: {str(e)}") def transcribe_chunk(chunk_path, language, recognizer, attempt=0): """Transcribes a single audio chunk with retry logic""" try: with sr.AudioFile(chunk_path) as source: audio_data = recognizer.record(source) return recognizer.recognize_google(audio_data, language=language) except sr.UnknownValueError: if attempt < 2: return transcribe_chunk(chunk_path, language, recognizer, attempt + 1) return "[Áudio não compreendido]" except sr.RequestError as e: if attempt < 2: return transcribe_chunk(chunk_path, language, recognizer, attempt + 1) return f"[Erro na API: {str(e)}]" def transcribe_audio(audio_path, language='pt-BR', progress_callback=None): """Transcribes audio file with progress updates""" recognizer = sr.Recognizer() recognizer.energy_threshold = 300 recognizer.dynamic_energy_threshold = True temp_chunks = [] full_transcript = [] try: duration = get_audio_duration(audio_path) chunk_size = 30 # seconds total_chunks = int(duration // chunk_size) + 1 for i in range(total_chunks): chunk_path = f"temp_chunk_{i}.wav" temp_chunks.append(chunk_path) # Extract chunk using ffmpeg result = subprocess.run([ "ffmpeg", "-y", "-ss", str(i * chunk_size), "-t", str(chunk_size), "-i", audio_path, "-ac", "1", "-ar", "16000", "-loglevel", "error", chunk_path ], capture_output=True, text=True) if result.returncode != 0: raise RuntimeError(f"Erro ao extrair chunk: {result.stderr}") # Transcribe chunk transcript = transcribe_chunk(chunk_path, language, recognizer) full_transcript.append(transcript) # Update progress if progress_callback: progress = (i + 1) / total_chunks progress_callback(progress, f"Transcrevendo parte {i+1}/{total_chunks}") return " ".join(full_transcript) except Exception as e: raise RuntimeError(f"Erro durante transcrição: {str(e)}") finally: # Clean up temp files for chunk in temp_chunks: if os.path.exists(chunk): try: os.remove(chunk) except: pass async def process_audio(audio_file, language, progress=gr.Progress()): """Main processing function for Gradio interface""" try: # Validate inputs if audio_file is None: return "Por favor, selecione um arquivo de áudio.", None progress(0, desc="🔍 Verificando FFmpeg...") # Find FFmpeg try: ffmpeg_path = find_ffmpeg() except FileNotFoundError as e: return f"❌ Erro: {str(e)}", None # Create temporary directory for processing with tempfile.TemporaryDirectory() as temp_dir: temp_audio_path = os.path.join(temp_dir, "audio_input") shutil.copy(audio_file, temp_audio_path) progress(0.1, desc="🔄 Convertendo áudio...") # Convert if needed audio_ext = Path(audio_file).suffix.lower() if audio_ext not in ['.wav']: progress(0.2, desc="🎵 Convertendo para WAV...") converted_path = convert_audio(temp_audio_path, ffmpeg_path) audio_to_transcribe = converted_path else: audio_to_transcribe = temp_audio_path progress(0.3, desc="📝 Preparando transcrição...") # Transcribe with progress updates def update_progress(pct, msg): progress(0.3 + (pct * 0.7), desc=msg) transcript = transcribe_audio( audio_to_transcribe, language, progress_callback=update_progress ) progress(1.0, desc="✅ Transcrição concluída!") # Prepare output filename original_name = Path(audio_file).stem output_filename = f"{original_name}_transcricao.txt" # Save to temporary file for download temp_output = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') temp_output.write(transcript) temp_output.close() # Return results return transcript, temp_output.name except Exception as e: error_msg = f"❌ Erro durante o processamento: {str(e)}" print(traceback.format_exc()) return error_msg, None # ================== Gradio Interface ================== def create_interface(): """Creates the Gradio interface""" with gr.Blocks( title="Transcrição de Áudio", theme=gr.themes.Soft(), css=""" .gradio-container { max-width: 900px !important; } .success-box { border: 1px solid #4CAF50; background-color: #E8F5E8; padding: 10px; border-radius: 5px; margin: 10px 0; } .warning { color: #ff9800; font-weight: bold; } .header { text-align: center; margin-bottom: 20px; } """ ) as interface: gr.Markdown( """

🎙️ Transcrição de Áudio

Faça a transcrição de arquivos de áudio para texto usando reconhecimento de fala

""" ) with gr.Row(): with gr.Column(scale=1): # Input section with gr.Group(): gr.Markdown("### 📁 Entrada de Áudio") audio_input = gr.Audio( label="Selecione o arquivo de áudio", type="filepath", sources=["upload"], interactive=True ) with gr.Group(): gr.Markdown("### 🌐 Configurações") language_radio = gr.Radio( choices=[ ("Português (Brasil)", "pt-BR"), ("Inglês (EUA)", "en-US"), ("Espanhol", "es-ES"), ("Francês", "fr-FR"), ("Alemão", "de-DE") ], value="pt-BR", label="Idioma do Áudio", info="Selecione o idioma falado no áudio" ) transcribe_btn = gr.Button( "🎯 Transcrever Áudio", variant="primary", size="lg", scale=1 ) with gr.Column(scale=2): # Output section with gr.Group(): gr.Markdown("### 📝 Resultado da Transcrição") transcript_output = gr.Textbox( label="Texto Transcrito", placeholder="A transcrição aparecerá aqui...", lines=12, max_lines=20, show_copy_button=True, autoscroll=True ) with gr.Group(): gr.Markdown("### 💾 Download") download_output = gr.File( label="Baixar Transcrição", file_count="single", height=100 ) # Examples section with gr.Accordion("📚 Exemplos de Uso", open=False): gr.Markdown(""" **Formatos suportados:** MP3, WAV, M4A, FLAC, AAC, OGG, WMA **Dicas para melhor qualidade:** - Use áudios com boa qualidade e pouco ruído de fundo - Arquivos com voz clara e pausadas têm melhor reconhecimento - Para áudios longos, o sistema divide automaticamente em partes """) # Processing info with gr.Accordion("ℹ️ Informações Técnicas", open=False): gr.Markdown(""" **Como funciona:** 1. O áudio é convertido para formato WAV (se necessário) 2. Arquivos longos são divididos em partes de 30 segundos 3. Cada parte é transcrita usando Google Speech Recognition 4. As transcrições são combinadas no resultado final **Tecnologias utilizadas:** - Gradio 5.49.1 para interface web - SpeechRecognition para reconhecimento de fala - FFmpeg para processamento de áudio - Google Speech Recognition API **Limitações:** - Requer conexão com internet - Qualidade depende da clareza do áudio - Limitações da API gratuita do Google - Timeout para arquivos muito longos """) # Event handlers transcribe_btn.click( fn=process_audio, inputs=[audio_input, language_radio], outputs=[transcript_output, download_output], show_progress="full", api_name="transcribe" ) # Clear outputs when new audio is selected def clear_outputs(): return "", None audio_input.change( fn=clear_outputs, outputs=[transcript_output, download_output] ) return interface # ================== Main Execution ================== def main(): """Main function to launch the Gradio interface""" print("🚀 Iniciando aplicação de transcrição de áudio...") print(f"📦 Versão do Gradio: {gr.__version__}") try: # Test FFmpeg availability ffmpeg_path = find_ffmpeg() print(f"✅ FFmpeg encontrado em: {ffmpeg_path}") # Test speech recognition recognizer = sr.Recognizer() print("✅ SpeechRecognition configurado") except Exception as e: print(f"⚠️ Aviso na inicialização: {str(e)}") # Launch interface interface = create_interface() interface.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True, favicon_path=None, inbrowser=False ) if __name__ == "__main__": main()