From 12576aa129f3d9c105f7ee70b24d8f3128a10611 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 2 Oct 2026 16:45:25 +0200 Subject: [PATCH] feat(audio): MaxSeconds caps the converted length, so a header that lies cannot become hours of WAV (review F2) Co-Authored-By: Claude Sonnet 5.5 --- internal/adapters/audio/ffmpeg.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/adapters/audio/ffmpeg.go b/internal/adapters/audio/ffmpeg.go index 609cd50..a78908a 100644 --- a/internal/adapters/audio/ffmpeg.go +++ b/internal/adapters/audio/ffmpeg.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "strconv" "strings" ) @@ -17,8 +18,17 @@ import ( type FFmpeg struct { // Bin is the ffmpeg executable ("ffmpeg" resolves via PATH). 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 // file has already been probed as audio-only, but a cover-art stream must not end // 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. 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.WaitDelay = killGrace if out, err := cmd.CombinedOutput(); err != nil {