| import os |
| import time |
| import threading |
| import logging |
| from .models import TranscriptionProviderConfig |
|
|
| log = logging.getLogger(__name__) |
|
|
| |
| |
| _whisper_model = None |
| _whisper_lock = threading.Lock() |
|
|
| _nemotron_model = None |
| _nemotron_lock = threading.Lock() |
|
|
| def get_whisper_model(model_size="base"): |
| global _whisper_model |
| if _whisper_model is None: |
| try: |
| import whisper |
| with _whisper_lock: |
| if _whisper_model is None: |
| log.info(f"Cargando modelo Whisper ({model_size}) en memoria...") |
| _whisper_model = whisper.load_model(model_size) |
| except ImportError: |
| log.error("La librer铆a 'whisper' no est谩 instalada. Ejecuta: pip install openai-whisper") |
| return None |
| return _whisper_model |
|
|
| def get_nemotron_model(model_name="nvidia/nemotron-3.5-asr-streaming-0.6b"): |
| global _nemotron_model |
| if _nemotron_model is None: |
| try: |
| import torch |
| |
| _orig_load = torch.load |
| def patched_load(*args, **kwargs): |
| kwargs['weights_only'] = False |
| return _orig_load(*args, **kwargs) |
| torch.load = patched_load |
|
|
| import nemo.collections.asr as nemo_asr |
| with _nemotron_lock: |
| if _nemotron_model is None: |
| log.info(f"Cargando modelo Nemotron ({model_name}) en memoria...") |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
| _nemotron_model = nemo_asr.models.ASRModel.from_pretrained(model_name, map_location=device) |
| |
| |
| torch.load = _orig_load |
| except ImportError: |
| log.error("La librer铆a 'nemo_toolkit' no est谩 instalada.") |
| return None |
| except Exception as e: |
| log.error(f"Error cargando Nemotron: {e}") |
| if 'torch' in locals() and hasattr(torch, 'load') and '_orig_load' in locals(): |
| torch.load = _orig_load |
| return None |
| return _nemotron_model |
|
|
| def transcribe_audio_file(audio_path, provider_type="whisper_local"): |
| """ |
| Toma la ruta de un archivo de audio y el tipo de proveedor de transcripcion. |
| Retorna (texto_transcrito, tiempo_de_ejecucion_ms, error_msg) |
| """ |
| start_time = time.time() |
| transcribed_text = "" |
| error_msg = None |
|
|
| try: |
| config = TranscriptionProviderConfig.objects.filter(provider_type=provider_type, is_active=True).first() |
| |
| if provider_type == "whisper_local": |
| model_size = config.model if config and config.model else "base" |
| model = get_whisper_model(model_size) |
| if not model: |
| raise RuntimeError("Motor Whisper Local no disponible (faltan dependencias o fall贸 la carga).") |
| |
| with _whisper_lock: |
| result = model.transcribe(audio_path, language="es", fp16=False) |
| transcribed_text = result["text"] |
|
|
| elif provider_type == "whisper_api": |
| try: |
| from openai import OpenAI |
| except ImportError: |
| raise RuntimeError("La librer铆a 'openai' no est谩 instalada.") |
| |
| if not config or not config.api_key: |
| raise RuntimeError("API Key no configurada para Whisper API.") |
| |
| client = OpenAI(api_key=config.api_key) |
| with open(audio_path, "rb") as audio_file: |
| transcription = client.audio.transcriptions.create( |
| model="whisper-1", |
| file=audio_file, |
| language="es" |
| ) |
| transcribed_text = transcription.text |
|
|
| elif provider_type == "nemotron": |
| model_name = config.model if config and config.model else "nvidia/nemotron-3.5-asr-streaming-0.6b" |
| model = get_nemotron_model(model_name) |
| if not model: |
| log.warning("Nemotron fall贸. Haciendo fallback autom谩tico a whisper_local.") |
| return transcribe_audio_file(audio_path, provider_type="whisper_local") |
| |
| with _nemotron_lock: |
| import json |
| import tempfile |
| |
| import json |
| import tempfile |
|
|
| |
| try: |
| import librosa |
| duration = float(librosa.get_duration(path=audio_path)) |
| except Exception: |
| try: |
| import soundfile as sf |
| duration = float(sf.info(audio_path).duration) |
| except Exception: |
| duration = 10000.0 |
|
|
| |
| with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f: |
| manifest_data = { |
| "audio_filepath": audio_path, |
| "duration": duration, |
| "target_lang": "es", |
| "lang": "es", |
| "text": "" |
| } |
| f.write(json.dumps(manifest_data) + '\n') |
| manifest_path = f.name |
|
|
| try: |
| |
| results = model.transcribe([manifest_path]) |
| if isinstance(results, tuple): |
| results = results[0] |
| if isinstance(results, list) and len(results) > 0: |
| if isinstance(results[0], tuple): |
| transcribed_text = results[0][0] |
| else: |
| transcribed_text = results[0] |
| |
| |
| if hasattr(transcribed_text, 'text'): |
| transcribed_text = transcribed_text.text |
| elif not isinstance(transcribed_text, str): |
| transcribed_text = str(transcribed_text) |
| except Exception as e: |
| raise e |
| finally: |
| import os |
| if os.path.exists(manifest_path): |
| os.remove(manifest_path) |
|
|
| else: |
| raise ValueError(f"Proveedor de transcripci贸n '{provider_type}' no soportado en el backend.") |
|
|
| except Exception as e: |
| log.exception(f"Error en transcribe_audio_file ({provider_type})") |
| error_msg = str(e) |
|
|
| end_time = time.time() |
| execution_time_ms = int((end_time - start_time) * 1000) |
|
|
| return transcribed_text, execution_time_ms, error_msg |
|
|