fix(A.2 #19): only report audio_path when the WAV writer finalises cleanly

Review feedback: reported_audio_path was cached at writer-open time
and survived a mid-session writer drop. If an append error cleared
wav_writer (or hound's Drop ran instead of finalize), the Summary
still reported the path — risking StopLiveTranscriptionResponse
pointing at a file whose header did not reflect its data chunk.

Moves the decision to end-of-session. wav_writer.take() is inspected:
- writer present + finalize Ok → report the path,
- writer present + finalize Err → None, emit a Warning,
- writer absent (mid-session drop, or never opened) → None.

StopLiveTranscriptionResponse.audio_path now means "the recording is
known-good" rather than "a recording was attempted". Users can still
recover partial files via filesystem if needed; the warning toasts
already emitted by append_resampled_audio on write failure surface
that path implicitly.
This commit is contained in:
2026-04-22 04:54:15 +01:00
parent e4adcc1832
commit 6f4adae56c

View File

@@ -402,15 +402,12 @@ fn run_live_session(
}, },
None => None, None => None,
}; };
// Resolve the reported audio_path only if the writer actually // `reported_audio_path` is decided at end-of-session based on
// opened; otherwise the returned Summary must not claim a path. // whether the writer finalised successfully, not at open time.
let reported_audio_path: Option<String> = if wav_writer.is_some() { // This way a writer that dies mid-session (append error clearing
audio_path // wav_writer, or finalise returning Err) does not leak a stale
.as_ref() // path back to the frontend that might point to a file whose
.map(|p| p.to_string_lossy().to_string()) // header is out of sync with its data chunk.
} else {
None
};
let mut buffer_start_sample: u64 = 0; let mut buffer_start_sample: u64 = 0;
let mut dropped_audio_ms: u64 = 0; let mut dropped_audio_ms: u64 = 0;
@@ -552,23 +549,32 @@ fn run_live_session(
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
} }
// Finalise the progressive WAV writer so the terminal header // Finalise the progressive WAV writer and decide whether to
// reflects every written sample. A failure here leaves the last // report a path to the frontend. Only a clean finalise produces a
// flushed header state on disk — still a playable WAV — so we // reported path: a writer that died mid-session (wav_writer was
// report a warning but don't fail the whole session. // already None) or a finalise that itself errored both yield
if let Some(writer) = wav_writer.take() { // `None`, so `StopLiveTranscriptionResponse.audio_path` reflects
if let Err(e) = writer.finalize() { // "recording is known-good" rather than "recording was attempted".
let _ = status_channel.send(LiveStatusMessage::Warning { let audio_path = match wav_writer.take() {
session_id, Some(writer) => match writer.finalize() {
message: format!("WAV finalise failed: {e}; partial recording is still playable."), Ok(()) => audio_path.as_ref().map(|p| p.to_string_lossy().to_string()),
}); Err(e) => {
} let _ = status_channel.send(LiveStatusMessage::Warning {
} session_id,
message: format!(
"WAV finalise failed: {e}. A partial recording may still be on disk from earlier flushes."
),
});
None
}
},
None => None,
};
Ok(LiveSessionSummary { Ok(LiveSessionSummary {
session_id, session_id,
dropped_audio_ms, dropped_audio_ms,
audio_path: reported_audio_path, audio_path,
}) })
} }