Files
BotVPS/audio_handler.py

75 lines
3.1 KiB
Python

import os
import speech_recognition as sr
from pydub import AudioSegment
import uuid
import re
import asyncio
import edge_tts
def transcribe_audio(file_path: str) -> str:
"""Converte áudio (qualquer formato compatível com pydub) para WAV e transcreve com Google Speech."""
recognizer = sr.Recognizer()
# Se não for wav, converte usando pydub (precisa de ffmpeg na VPS)
temp_wav = f"/tmp/{uuid.uuid4()}.wav"
try:
audio = AudioSegment.from_file(file_path)
audio.export(temp_wav, format="wav")
with sr.AudioFile(temp_wav) as source:
audio_data = recognizer.record(source)
text = recognizer.recognize_google(audio_data, language="pt-BR")
return text
finally:
if os.path.exists(temp_wav):
os.remove(temp_wav)
async def text_to_speech_async(text: str) -> str:
"""Sintetiza texto em áudio MP3 usando Edge TTS (Versão ASYNC)."""
if not text or not text.strip():
text = "Processado com sucesso."
# Limpeza para narração: remove tudo o que a voz tenta ler literalmente mas não deve
# Preservamos o texto original se ele já vier "limpo" (ex: vindo do main.py via refined)
texto_limpo = text.replace("🤖", "").replace("🧑‍🏫", "").replace("*", "").replace("`", "")
texto_limpo = texto_limpo.replace("#", "").replace("- ", " ").replace("> ", " ")
# Só remove REFINED se ele estiver presente como tag (proteção dupla)
if "<REFINED>" in texto_limpo:
texto_limpo = re.sub(r'<REFINED>.*?</REFINED>', '', texto_limpo, flags=re.DOTALL)
# Remove URLs e links [texto](url)
texto_limpo = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', texto_limpo)
texto_limpo = re.sub(r'http[s]?://\S+', '', texto_limpo)
# Limpeza final: remove espaços extras e garante que sobrou algo audível
texto_limpo = texto_limpo.strip()
if not texto_limpo:
texto_limpo = "Prompt processado."
filename = f"audio_reply_{uuid.uuid4().hex[:8]}.mp3"
filepath = os.path.join("/tmp", filename)
try:
# Voz Masculina PT-BR: Donato
voice = "pt-BR-DonatoNeural"
communicate = edge_tts.Communicate(texto_limpo, voice, rate="+35%")
await communicate.save(filepath)
return filename
except Exception as e:
# Se falhar o TTS (ex: erro de rede ou "No audio was received"), gera um bip ou áudio simples se possível
# Aqui vamos apenas logar e relançar para o main.py tratar
print(f"[TTS ERROR] Falha ao gerar áudio para: '{texto_limpo}'. Erro: {e}")
raise
def text_to_speech(text: str) -> str:
"""Wrapper síncrono para compatibilidade legada (CUIDADO com loops eventuais)."""
try:
# Se já houver um loop rodando (ex: Telegram), isso vai falhar
return asyncio.run(text_to_speech_async(text))
except RuntimeError:
# Fallback: se houver loop, tenta rodar de forma síncrona ou retorna erro
# No nosso caso, o bot_logic e main.py devem usar a versão ASYNC diretamente
print("[VOICE] Erro: text_to_speech (sync) chamado dentro de um event loop.")
raise