Spaces:
Running
Running
| // js/recordingModule.js | |
| // Modificamos la firma para aceptar el nuevo callback | |
| export function initRecorder({ btnStart, btnStop, transcriptEl, getProvider, clearFieldsCallback }) { | |
| let mediaRecorder, audioChunks = [], transcriptText = ''; | |
| // Listener para el bot贸n Iniciar (MODIFICADO para usar el callback) | |
| btnStart.addEventListener('click', async () => { | |
| console.log('[Recorder] Bot贸n Iniciar pulsado'); | |
| // --- INICIO: Limpieza usando Callback --- | |
| if (typeof clearFieldsCallback === 'function') { | |
| console.log('[Recorder] Llamando a clearFieldsCallback...'); | |
| try { | |
| clearFieldsCallback(); // <-- LLAMADA AL CALLBACK | |
| } catch (error) { | |
| console.error('[Recorder] Error al ejecutar clearFieldsCallback:', error); | |
| } | |
| } else { | |
| console.warn('[Recorder] clearFieldsCallback no fue proporcionado o no es una funci贸n.'); | |
| // Opcionalmente, limpiar al menos la transcripci贸n aqu铆 como fallback | |
| if (transcriptEl) transcriptEl.value = ''; // Fallback si no hay callback | |
| } | |
| // --- FIN: Limpieza usando Callback --- | |
| // Ya no se necesitan las l铆neas de evento y log desactivado | |
| // console.log('[Recorder] Evento newRecordingStarted DESACTIVADO TEMPORALMENTE'); // Eliminado | |
| const aiProviderUrl = getProvider(); | |
| if (!aiProviderUrl) { | |
| alert('Proveedor de Transcripci贸n no configurado o URL inv谩lida. Revisa la Configuraci贸n IA.'); | |
| return; | |
| } | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| console.log('[Recorder] Acceso a micr贸fono concedido'); | |
| mediaRecorder = new MediaRecorder(stream); | |
| mediaRecorder.onstop = onStop; | |
| console.log('[Recorder] MediaRecorder creado:', mediaRecorder); | |
| audioChunks = []; | |
| mediaRecorder.ondataavailable = e => audioChunks.push(e.data); | |
| mediaRecorder.start(); | |
| btnStart.disabled = true; | |
| btnStop.disabled = false; | |
| const status = document.getElementById('recorder-status'); | |
| if (status) { | |
| status.innerHTML = '<i class="fas fa-circle text-red-500 animate-pulse mr-2"></i>Consulta en progreso...'; | |
| } | |
| } catch (err) { | |
| console.error('[Recorder] Error al acceder al micr贸fono:', err); | |
| alert(`No se pudo acceder al micr贸fono: ${err.message}`); | |
| btnStart.disabled = false; | |
| btnStop.disabled = true; | |
| } | |
| }); | |
| // Listener para el bot贸n Detener (CON estado intermedio) | |
| btnStop.addEventListener('click', () => { | |
| console.log('[Recorder] Bot贸n Detener pulsado'); | |
| if (mediaRecorder && mediaRecorder.state === 'recording') { | |
| mediaRecorder.stop(); | |
| btnStop.disabled = true; | |
| const status = document.getElementById('recorder-status'); | |
| if (status) { | |
| status.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Procesando audio...'; | |
| } | |
| } else { | |
| console.warn('[Recorder] MediaRecorder no estaba grabando al intentar detener.'); | |
| btnStart.disabled = false; | |
| btnStop.disabled = true; | |
| } | |
| }); | |
| // --- INICIO: Funci贸n onStop (Sin cambios en su l贸gica interna) --- | |
| async function onStop() { | |
| console.log('[Recorder] Grabaci贸n detenida (onStop), procesando audio...'); | |
| if (audioChunks.length === 0) { | |
| console.warn('[Recorder] No hay audio chunks para procesar.'); | |
| alert('No se grab贸 audio.'); | |
| const status = document.getElementById('recorder-status'); | |
| if (status) status.innerHTML = '<i class="fas fa-circle text-gray-400 mr-2"></i>No hay consulta en progreso'; | |
| btnStart.disabled = false; btnStop.disabled = true; return; | |
| } | |
| const cfg = JSON.parse(localStorage.getItem('iaConfig')); | |
| if (!cfg || !cfg.transcription || !cfg.transcription.provider || !cfg.transcription.apiKeys || !cfg.transcription.models) { | |
| alert('Configuraci贸n de Transcripci贸n incompleta.'); | |
| const status = document.getElementById('recorder-status'); if(status) status.innerHTML = '<i class="fas fa-exclamation-circle text-red-500 mr-2"></i>Error Configuraci贸n.'; | |
| btnStart.disabled = false; btnStop.disabled = true; return; | |
| } | |
| const transProvider = cfg.transcription.provider; | |
| const apiKey = cfg.transcription.apiKeys[transProvider]; | |
| const transModel = cfg.transcription.models[transProvider]; | |
| const providerBaseUrl = getProvider(); // Llama a la funci贸n pasada desde main.js | |
| if (!apiKey) { | |
| alert(`API Key para ${transProvider} no encontrada.`); | |
| const status = document.getElementById('recorder-status'); if(status) status.innerHTML = `<i class="fas fa-exclamation-circle text-red-500 mr-2"></i>Falta API Key (${transProvider}).`; | |
| btnStart.disabled = false; btnStop.disabled = true; return; | |
| } | |
| if (!providerBaseUrl) { | |
| alert(`URL base para ${transProvider} no encontrada.`); | |
| const status = document.getElementById('recorder-status'); if(status) status.innerHTML = `<i class="fas fa-exclamation-circle text-red-500 mr-2"></i>Error URL Proveedor.`; | |
| btnStart.disabled = false; btnStop.disabled = true; return; | |
| } | |
| let blobType = audioChunks[0]?.type || 'audio/webm'; | |
| console.log(`[Recorder] Usando tipo MIME para Blob: ${blobType}`); | |
| const blob = new Blob(audioChunks, { type: blobType }); | |
| console.log('[Recorder] Blob de audio creado:', blob); | |
| audioChunks = []; | |
| let transcript = ''; | |
| try { | |
| if (transProvider === 'deepgram') { | |
| console.log(`[Recorder] Iniciando transcripci贸n con Deepgram (Modelo: ${transModel})`); | |
| const deepgramUrl = `${providerBaseUrl}/v1/listen?language=es&model=${transModel}&smart_format=true`; | |
| console.log('[Recorder] Deepgram URL:', deepgramUrl); | |
| const res = await fetch(deepgramUrl, { method: 'POST', headers: { 'Authorization': 'Token ' + apiKey, 'Content-Type': blob.type }, body: blob }); | |
| if (!res.ok) { const errorText = await res.text().catch(() => `Status ${res.status}`); throw new Error(`Error Deepgram (${res.status}): ${errorText}`); } | |
| const data = await res.json(); | |
| console.log('[Deepgram] Respuesta completa:', data); | |
| transcript = data.results?.channels?.[0]?.alternatives?.[0]?.transcript || ''; | |
| console.log('[Deepgram] Transcript extra铆do:', transcript); | |
| } else if (transProvider === 'openai') { | |
| console.log(`[Recorder] Iniciando transcripci贸n con OpenAI (Modelo: ${transModel})`); | |
| const fd = new FormData(); fd.append('model', transModel); | |
| // --- INICIO: C脫DIGO MODIFICADO PARA FILENAME --- | |
| // Obtener la parte despu茅s de '/' (ej: 'webm;codecs=opus' o 'mp4') o usar 'webm' como fallback | |
| const mimeSubType = (blobType.split('/')[1] || 'webm'); | |
| // Quitar cualquier cosa despu茅s de un punto y coma (ej: ';codecs=opus') | |
| const extension = mimeSubType.split(';')[0]; | |
| // Construir el nombre de archivo solo con la extensi贸n limpia | |
| const filename = `consulta.${extension}`; | |
| // --- FIN: C脫DIGO MODIFICADO PARA FILENAME --- | |
| fd.append('file', blob, filename); // Ahora filename ser谩 algo como 'consulta.webm' | |
| console.log(`[Recorder] Enviando a OpenAI: model=${transModel}, filename=${filename}, size=${blob.size}`); // Log mostrar谩 el filename corregido | |
| const respOA = await fetch(`${providerBaseUrl}/v1/audio/transcriptions`, { method: 'POST', headers: { 'Authorization': 'Bearer ' + apiKey }, body: fd }); | |
| if (!respOA.ok) { const errTxt = await respOA.text(); throw new Error(`Error OpenAI transcripci贸n (${respOA.status}): ${errTxt}`); } | |
| const dataOA = await respOA.json(); | |
| console.log('[OpenAI Whisper] Respuesta completa:', dataOA); | |
| transcript = dataOA.text || ''; | |
| console.log('[OpenAI Whisper] Transcript extra铆do:', transcript); | |
| } else { throw new Error(`Proveedor de transcripci贸n no soportado: ${transProvider}`); } | |
| // Actualizar UI y disparar evento (ESTA L脥NEA SIGUE ACTIVA) | |
| if (transcriptEl) transcriptEl.value = transcript; // Asegurarse que transcriptEl existe | |
| document.dispatchEvent(new CustomEvent('transcriptionReady', { detail: transcript })); | |
| console.log("[Recorder] Evento 'transcriptionReady' DESPACHADO con detalle:", { detail: transcript }); | |
| } catch (e) { | |
| console.error('[Recorder] Error durante la llamada API de transcripci贸n:', e); | |
| alert('Error en la transcripci贸n: ' + e.message); | |
| const status = document.getElementById('recorder-status'); | |
| if (status) status.innerHTML = `<i class="fas fa-times-circle text-red-500 mr-2"></i>Error en transcripci贸n.`; | |
| } finally { | |
| console.log('[Recorder] Finalizando onStop (finally).'); | |
| btnStart.disabled = false; btnStop.disabled = true; | |
| const statusElement = document.getElementById('recorder-status'); | |
| if (statusElement && !statusElement.innerHTML.includes('Error')) { | |
| statusElement.innerHTML = '<i class="fas fa-check-circle text-green-500 mr-2"></i>Transcripci贸n lista.'; | |
| // Actualizar estado despu茅s de un tiempo si sigue en "lista" | |
| setTimeout(() => { | |
| const currentStatusElement = document.getElementById('recorder-status'); | |
| if(currentStatusElement && currentStatusElement.innerHTML.includes('lista')) { | |
| currentStatusElement.innerHTML = '<i class="fas fa-circle text-gray-400 mr-2"></i>No hay consulta en progreso'; | |
| } | |
| }, 4000); | |
| } else if (statusElement && statusElement.innerHTML.includes('Error')) { | |
| // Si hubo error, dejar el mensaje de error por un tiempo y luego resetear | |
| setTimeout(() => { | |
| const currentStatusElement = document.getElementById('recorder-status'); | |
| if(currentStatusElement && currentStatusElement.innerHTML.includes('Error')) { | |
| currentStatusElement.innerHTML = '<i class="fas fa-circle text-gray-400 mr-2"></i>No hay consulta en progreso'; | |
| } | |
| }, 5000); // Un poco m谩s de tiempo para leer el error | |
| } else if (statusElement) { | |
| // Si no hubo error pero tampoco est谩 'lista' (caso raro, ej. no audio chunks), resetear | |
| statusElement.innerHTML = '<i class="fas fa-circle text-gray-400 mr-2"></i>No hay consulta en progreso'; | |
| } | |
| } | |
| } | |
| // --- FIN: Funci贸n onStop --- | |
| } // Fin export function initRecorder |