- Configurado proyecto Nuxt 4 con PWA - Integrado OpenAI Whisper API para transcripción de audio - Implementada captura de audio desde navegador - Creada UI con grabación y visualización de transcripciones - Configurado Authentik Proxy para autenticación - Setup de Docker y Gitea Actions para despliegue
138 lines
3.7 KiB
TypeScript
138 lines
3.7 KiB
TypeScript
export const useWhisper = () => {
|
|
const isRecording = useState<boolean>('whisper_isRecording', () => false)
|
|
const isTranscribing = useState<boolean>('whisper_isTranscribing', () => false)
|
|
const transcription = useState<string>('whisper_transcription', () => '')
|
|
const error = useState<string | null>('whisper_error', () => null)
|
|
|
|
let mediaRecorder: MediaRecorder | null = null
|
|
let audioChunks: Blob[] = []
|
|
|
|
const startRecording = async () => {
|
|
try {
|
|
error.value = null
|
|
|
|
// Solicitar permisos de micrófono
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
audio: {
|
|
echoCancellation: true,
|
|
noiseSuppression: true,
|
|
sampleRate: 16000
|
|
}
|
|
})
|
|
|
|
// Crear MediaRecorder
|
|
const mimeType = MediaRecorder.isTypeSupported('audio/webm')
|
|
? 'audio/webm'
|
|
: 'audio/mp4'
|
|
|
|
mediaRecorder = new MediaRecorder(stream, {
|
|
mimeType
|
|
})
|
|
|
|
audioChunks = []
|
|
|
|
mediaRecorder.ondataavailable = (event) => {
|
|
if (event.data.size > 0) {
|
|
audioChunks.push(event.data)
|
|
}
|
|
}
|
|
|
|
mediaRecorder.onstop = async () => {
|
|
// Detener todos los tracks del stream
|
|
stream.getTracks().forEach(track => track.stop())
|
|
|
|
// Procesar la transcripción
|
|
await processTranscription()
|
|
}
|
|
|
|
mediaRecorder.start()
|
|
isRecording.value = true
|
|
|
|
console.log('[Whisper] Grabación iniciada')
|
|
} catch (err: any) {
|
|
console.error('[Whisper] Error al iniciar grabación:', err)
|
|
error.value = err.message || 'Error al acceder al micrófono'
|
|
}
|
|
}
|
|
|
|
const stopRecording = () => {
|
|
if (mediaRecorder && isRecording.value) {
|
|
mediaRecorder.stop()
|
|
isRecording.value = false
|
|
console.log('[Whisper] Grabación detenida')
|
|
}
|
|
}
|
|
|
|
const processTranscription = async () => {
|
|
try {
|
|
isTranscribing.value = true
|
|
error.value = null
|
|
|
|
// Crear blob del audio
|
|
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' })
|
|
|
|
console.log('[Whisper] Enviando audio para transcripción:', {
|
|
size: audioBlob.size,
|
|
type: audioBlob.type
|
|
})
|
|
|
|
// Crear FormData
|
|
const formData = new FormData()
|
|
formData.append('file', audioBlob, 'recording.webm')
|
|
formData.append('language', 'es')
|
|
|
|
// Enviar al backend
|
|
const response = await $fetch('/api/whisper/transcribe', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
|
|
if (response.success) {
|
|
transcription.value = response.transcription
|
|
console.log('[Whisper] Transcripción exitosa:', response.transcription)
|
|
|
|
// Copiar al clipboard automáticamente
|
|
if (navigator.clipboard) {
|
|
await navigator.clipboard.writeText(response.transcription)
|
|
console.log('[Whisper] Copiado al clipboard')
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
console.error('[Whisper] Error en transcripción:', err)
|
|
error.value = err.message || 'Error al procesar la transcripción'
|
|
} finally {
|
|
isTranscribing.value = false
|
|
audioChunks = []
|
|
}
|
|
}
|
|
|
|
const clearTranscription = () => {
|
|
transcription.value = ''
|
|
error.value = null
|
|
}
|
|
|
|
const copyToClipboard = async () => {
|
|
if (transcription.value && navigator.clipboard) {
|
|
try {
|
|
await navigator.clipboard.writeText(transcription.value)
|
|
return true
|
|
} catch (err) {
|
|
console.error('[Whisper] Error al copiar:', err)
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
return {
|
|
isRecording: readonly(isRecording),
|
|
isTranscribing: readonly(isTranscribing),
|
|
transcription: readonly(transcription),
|
|
error: readonly(error),
|
|
startRecording,
|
|
stopRecording,
|
|
clearTranscription,
|
|
copyToClipboard
|
|
}
|
|
}
|