feat: upload audio, get a private Swedish transcript, discuss it (#33, ADR-033) #39
+29
-14
@@ -275,11 +275,18 @@ func (w *Worker) Process(ctx context.Context, job Job) {
|
||||
abort(domain.UploadErrTranscriptionFailed)
|
||||
return
|
||||
}
|
||||
if kind, bad := w.audioLengthProblem(wav, job.Duration); bad {
|
||||
abort(kind)
|
||||
log.Error("upload: converted audio length rejected", "kind", string(kind))
|
||||
got, tooLong := w.audioLengthProblem(wav)
|
||||
if tooLong {
|
||||
abort(domain.UploadErrTooLong)
|
||||
log.Error("upload: converted audio is over the limit", "audio_s", int(got.Seconds()), "limit_s", int(w.maxDuration.Seconds()))
|
||||
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")
|
||||
|
||||
if !planned {
|
||||
@@ -343,21 +350,29 @@ const (
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (w *Worker) audioLengthProblem(wav string, claimed time.Duration) (domain.UploadErrorKind, bool) {
|
||||
// second after a 44-byte header) against the limit. It returns the real length and whether
|
||||
// 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 {
|
||||
return "", false
|
||||
return 0, false
|
||||
}
|
||||
info, err := os.Stat(wav)
|
||||
if err != nil {
|
||||
return "", false
|
||||
return 0, false
|
||||
}
|
||||
got := time.Duration(float64(max(info.Size()-44, 0)) / 32000 * float64(time.Second))
|
||||
switch {
|
||||
case got > w.maxDuration+tooLongSlack:
|
||||
return domain.UploadErrTooLong, true
|
||||
case float64(got) < incompleteBelow*float64(claimed) && claimed-got > incompleteShortfall:
|
||||
return domain.UploadErrIncomplete, true
|
||||
got = time.Duration(float64(max(info.Size()-44, 0)) / 32000 * float64(time.Second))
|
||||
return got, got > w.maxDuration+tooLongSlack
|
||||
}
|
||||
return "", false
|
||||
|
||||
// shortfallNote is a remark for the audio step when the converted audio is much shorter
|
||||
// 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 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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user