Keep long recordings: spool, retry, chunked upload, progress ring

A long clip used to die on a fixed 30 s timeout and the audio went with it.
Now every conditioned clip is written to spool/ before upload and only
removed once its text is on the clipboard; failures keep it, and a tray item
retries spooled clips (copied together, never dispatched). Requests the API
rejects outright (4xx other than 408/429) are not kept.

Timeouts scale with the audio (30 s + 30 s/min, capped at 5 min). Clips over
5 min are cut inside pauses into ~4 min chunks uploaded three at a time and
stitched in order; failed chunks are spooled individually and the partial
transcript is copied with a gap marker. The tray disc fills clockwise as
chunks return.

mic_check.py passes the new timeout tuple.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016rLFNPygDpRkRwPQakdmYh
This commit is contained in:
2026-09-05 19:03:50 -04:00
co-authored by Claude Fable 5.1
parent 6ad8526920
commit 82bc350521
4 changed files with 625 additions and 70 deletions
+20 -1
View File
@@ -56,7 +56,26 @@ filtered out, and the level is normalised towards -20 dBFS. Near-silent clips
are refused instead of being sent, since transcription models answer silence
with confident hallucinations.
Nothing else is cut: the whole recording is uploaded. If a transcript comes
Every clip is written to `spool/` (gitignored) before it is uploaded, and
deleted again as soon as its text reaches the clipboard. If transcription fails —
timeout, connection error, server error, empty response — the WAV stays there
and the notification says so; **Retry failed transcriptions (N)** in the tray
menu re-uploads the spooled clips oldest first, so a network hiccup never costs
you the words. A rejected request (any 4xx other than 408 and 429) is the
exception: the same audio can only be rejected again, so it is not kept, and the
notification says that too. Retried transcripts are copied to the clipboard
together, oldest first, once the whole run is done — and never dispatched to an
agent, since a spooled clip may be one part of a longer recording. The upload
timeout scales with the clip: 30 s plus 30 s per minute of audio, capped at
5 minutes. Clips longer than 5 minutes are split into chunks of about
4 minutes — each cut lands in a pause, not mid-word — and the chunks are
uploaded in parallel and stitched back together in order. The tray icon fills
clockwise as each chunk comes back, so you can see how far along the clip is.
If only some chunks come back, the transcript is copied with ` […] ` where the
gaps are, and just the failed chunks are kept in the spool as `…-partNN.wav`
for the retry.
Nothing else is cut: every second of the recording is uploaded. If a transcript comes
back missing words, check `blurt.log` — each clip logs its length, speech level,
noise floor, and `dead=`, the longest run of digital silence inside it. A
non-zero `dead=` means the input dropped out mid-clip, which is what following