feat: Auto-save voice recordings for model training

- Add /api/recordings endpoint with full CRUD operations
- Create voice_recordings SQLite table for metadata
- Save audio files to server/recordings/ as .webm
- Store transcription, duration, microphone name, file size
- Auto-save on each Whisper recording completion
This commit is contained in:
2026-02-14 01:56:53 -06:00
parent 5da6179f75
commit 950572046e
14 changed files with 229 additions and 0 deletions

View File

@@ -71,6 +71,7 @@ const showMicSelector = ref(false)
const lastAudioUrl = ref<string>('')
const isPlayingAudio = ref(false)
let audioElement: HTMLAudioElement | null = null
let recordingStartTime = 0
function playLastAudio() {
if (!lastAudioUrl.value) return
@@ -98,6 +99,42 @@ function saveAudioForPlayback(blob: Blob) {
URL.revokeObjectURL(lastAudioUrl.value)
}
lastAudioUrl.value = URL.createObjectURL(blob)
// Also save to backend for training data
saveRecordingToBackend(blob)
}
async function saveRecordingToBackend(blob: Blob) {
try {
const duration_ms = Date.now() - recordingStartTime
const reader = new FileReader()
reader.onloadend = async () => {
const base64 = (reader.result as string).split(',')[1]
const response = await fetch(`http://${window.location.hostname}:4100/api/recordings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
audio: base64,
transcription: transcript.value.trim(),
microphone: currentMicName.value,
duration_ms
})
})
const data = await response.json()
if (data.success) {
console.log(`[Voice] Recording saved: ${data.filename} (${(data.size / 1024).toFixed(1)} KB)`)
} else {
console.error('[Voice] Failed to save recording:', data.error)
}
}
reader.readAsDataURL(blob)
} catch (e) {
console.error('[Voice] Error saving recording:', e)
}
}
const currentMicName = computed(() => {
@@ -438,6 +475,7 @@ async function startWhisperRecording() {
// Start recording
mediaRecorder.start(100) // Collect data every 100ms
isRecording.value = true
recordingStartTime = Date.now()
// Send chunks periodically for progressive transcription
chunkInterval = window.setInterval(() => {