fix(uploads): a file much shorter than it claimed is transcribed and noted, not refused; the tolerances are named (second review finding 1)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 17:20:02 +02:00
co-authored by Claude Sonnet 5.5
parent 4943c80dbd
commit f7284ccdc9
+29 -14
View File
@@ -275,11 +275,18 @@ func (w *Worker) Process(ctx context.Context, job Job) {
abort(domain.UploadErrTranscriptionFailed) abort(domain.UploadErrTranscriptionFailed)
return return
} }
if kind, bad := w.audioLengthProblem(wav, job.Duration); bad { got, tooLong := w.audioLengthProblem(wav)
abort(kind) if tooLong {
log.Error("upload: converted audio length rejected", "kind", string(kind)) abort(domain.UploadErrTooLong)
log.Error("upload: converted audio is over the limit", "audio_s", int(got.Seconds()), "limit_s", int(w.maxDuration.Seconds()))
return return
} }
if w.maxDuration > 0 && job.Duration > 0 {
if note := shortfallNote(got, job.Duration); note != "" {
log.Warn("upload: audio is much shorter than the file claimed", "audio_s", int(got.Seconds()), "claimed_s", int(job.Duration.Seconds()))
tr.Annotate("audio", note)
}
}
tr.Done("audio") tr.Done("audio")
if !planned { if !planned {
@@ -343,21 +350,29 @@ const (
) )
// audioLengthProblem measures the converted WAV (16 kHz mono 16-bit PCM is 32,000 bytes a // audioLengthProblem measures the converted WAV (16 kHz mono 16-bit PCM is 32,000 bytes a
// second after a 44-byte header) against the limit and against what the file claimed. // second after a 44-byte header) against the limit. It returns the real length and whether
func (w *Worker) audioLengthProblem(wav string, claimed time.Duration) (domain.UploadErrorKind, bool) { // that is over the limit by more than the tolerance.
func (w *Worker) audioLengthProblem(wav string) (got time.Duration, tooLong bool) {
if w.maxDuration <= 0 { if w.maxDuration <= 0 {
return "", false return 0, false
} }
info, err := os.Stat(wav) info, err := os.Stat(wav)
if err != nil { if err != nil {
return "", false return 0, false
} }
got := time.Duration(float64(max(info.Size()-44, 0)) / 32000 * float64(time.Second)) got = time.Duration(float64(max(info.Size()-44, 0)) / 32000 * float64(time.Second))
switch { return got, got > w.maxDuration+tooLongSlack
case got > w.maxDuration+tooLongSlack: }
return domain.UploadErrTooLong, true
case float64(got) < incompleteBelow*float64(claimed) && claimed-got > incompleteShortfall: // shortfallNote is a remark for the audio step when the converted audio is much shorter
return domain.UploadErrIncomplete, true // than the file claimed, or "" when it is not. It is a NOTE and never a failure: a
// container header can over-estimate a complete file (a variable-bitrate MP3 without a Xing
// header claimed 79 minutes for 20 real ones), so refusing would turn away real
// recordings, while saying nothing about a truncated one would hide a partial transcript.
func shortfallNote(got, claimed time.Duration) string {
if float64(got) >= incompleteBelow*float64(claimed) || claimed-got <= incompleteShortfall {
return ""
} }
return "", false return fmt.Sprintf("The audio is %s long; the file claimed %s. If the recording was cut off, upload the complete one.",
FormatDuration(got), FormatDuration(claimed))
} }