feat: upload audio, get a private Swedish transcript, discuss it (#33, ADR-033) #39

Merged
mathias merged 299 commits from feat/33-audio-upload-transcript into main 2026-10-03 14:13:37 +00:00
Showing only changes of commit 12576aa129 - Show all commits
+16 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
) )
@@ -17,8 +18,17 @@ import (
type FFmpeg struct { type FFmpeg struct {
// Bin is the ffmpeg executable ("ffmpeg" resolves via PATH). // Bin is the ffmpeg executable ("ffmpeg" resolves via PATH).
Bin string Bin string
// MaxSeconds caps the converted length when positive. The duration limit is a claim
// in the file's header and can be lied about (a 2 h file claiming 10 s converted to a
// 120-minute WAV); this bounds what a lie can cost. A minute of slack is added so a
// file at the limit is never cut, and the worker measures the result.
MaxSeconds int
} }
// lengthSlackSeconds is added to MaxSeconds so ffmpeg's rounding never clips a file that
// is really at the limit.
const lengthSlackSeconds = 60
// ToWAV writes src, converted, to dst. Video streams are dropped (-vn): by now the // ToWAV writes src, converted, to dst. Video streams are dropped (-vn): by now the
// file has already been probed as audio-only, but a cover-art stream must not end // file has already been probed as audio-only, but a cover-art stream must not end
// up in the WAV. // up in the WAV.
@@ -29,7 +39,12 @@ func (f *FFmpeg) ToWAV(ctx context.Context, src, dst string) error {
} }
// Input restrictions go BEFORE -i: placed after it they would apply to the output. // Input restrictions go BEFORE -i: placed after it they would apply to the output.
args := append([]string{"-y", "-v", "error"}, inputRestrictions...) args := append([]string{"-y", "-v", "error"}, inputRestrictions...)
args = append(args, "-i", src, "-vn", "-ac", "1", "-ar", "16000", "-c:a", "pcm_s16le", "-f", "wav", dst) args = append(args, "-i", src, "-vn", "-ac", "1", "-ar", "16000", "-c:a", "pcm_s16le")
if f.MaxSeconds > 0 {
// After -i: an OUTPUT cap. Before it, it would limit what is read, not written.
args = append(args, "-t", strconv.Itoa(f.MaxSeconds+lengthSlackSeconds))
}
args = append(args, "-f", "wav", dst)
cmd := exec.CommandContext(ctx, bin, args...) cmd := exec.CommandContext(ctx, bin, args...)
cmd.WaitDelay = killGrace cmd.WaitDelay = killGrace
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {