AppConsulta360 / js /recordingModule.js
aarnal80's picture
Update js/recordingModule.js
f54bdc2 verified
// 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