Daniel251's picture
Update app.py
d54e1ed verified
Raw
History Blame Contribute Delete
12.8 kB
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(
"""
<div class="header">
<h1>🎙️ Transcrição de Áudio</h1>
<p>Faça a transcrição de arquivos de áudio para texto usando reconhecimento de fala</p>
</div>
"""
)
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()