mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 09:05:17 -07:00
fix(captures): clean up audio files when create_capture fails
The create flow wrote raw audio (and a transcoded .wav for non-wav sources) to data/captures before the DB row was committed, so any failure between the write and the commit — a webm that decoded to a 0-length array, a whisper model that errored mid-transcribe, a SQLite contention on the commit — left the audio on disk with nothing pointing at it. Over enough flaky uploads the directory grows without bound. Now every path written before the commit is tracked in a list, and the whole stretch from the first write to db.commit() runs inside a try/except that unlinks each tracked file on raise and re-raises. The transcode branch removes the raw file from the cleanup list only when the unlink actually succeeds, so an OSError on the raw-path delete still hands cleanup the original blob to retry.
This commit is contained in:
@@ -71,7 +71,11 @@ async def create_capture(
|
|||||||
suffix = ".wav"
|
suffix = ".wav"
|
||||||
|
|
||||||
raw_path = config.get_captures_dir() / f"{capture_id}{suffix}"
|
raw_path = config.get_captures_dir() / f"{capture_id}{suffix}"
|
||||||
|
written_files: list[Path] = []
|
||||||
|
|
||||||
|
try:
|
||||||
raw_path.write_bytes(audio_bytes)
|
raw_path.write_bytes(audio_bytes)
|
||||||
|
written_files.append(raw_path)
|
||||||
|
|
||||||
# Decode once with librosa — its audioread fallback handles webm/opus
|
# Decode once with librosa — its audioread fallback handles webm/opus
|
||||||
# via ffmpeg, which miniaudio (used inside mlx-audio's whisper) can't.
|
# via ffmpeg, which miniaudio (used inside mlx-audio's whisper) can't.
|
||||||
@@ -105,10 +109,13 @@ async def create_capture(
|
|||||||
# regardless of what format the client shipped.
|
# regardless of what format the client shipped.
|
||||||
audio_path = config.get_captures_dir() / f"{capture_id}.wav"
|
audio_path = config.get_captures_dir() / f"{capture_id}.wav"
|
||||||
sf.write(str(audio_path), audio, sr, format="WAV")
|
sf.write(str(audio_path), audio, sr, format="WAV")
|
||||||
|
written_files.append(audio_path)
|
||||||
try:
|
try:
|
||||||
raw_path.unlink()
|
raw_path.unlink()
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
written_files.remove(raw_path)
|
||||||
|
|
||||||
whisper = get_whisper_model()
|
whisper = get_whisper_model()
|
||||||
resolved_stt = stt_model or whisper.model_size
|
resolved_stt = stt_model or whisper.model_size
|
||||||
@@ -126,6 +133,16 @@ async def create_capture(
|
|||||||
db.add(row)
|
db.add(row)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(row)
|
db.refresh(row)
|
||||||
|
except Exception:
|
||||||
|
# Anything between the first write and the commit means the audio on
|
||||||
|
# disk has no row pointing at it — clean up so data/captures doesn't
|
||||||
|
# accumulate orphan blobs across failed transcribes.
|
||||||
|
for path in written_files:
|
||||||
|
try:
|
||||||
|
path.unlink()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
|
||||||
return _to_response(row)
|
return _to_response(row)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user