From cc215195ee2f4e9eed8b4773e86c03e3c942b321 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Wed, 2 Sep 2026 18:05:11 -0500 Subject: [PATCH] Build a command for the audio the user turned off (#198) audioArgs' Drop arm -- `AudioPlan.Drop -> listOf("-an")` -- was ci == 0. The suite's only -an assertion lives in "gif generates a palette to avoid banding and drops audio", and that one comes from the image path at FFmpegCommandBuilder.kt:79/:90, which emits -an directly and never reaches audioArgs. Two sites, one string, one tested. It is a live path rather than defensive code. AdvancedPicker renders all of AudioCodec.entries including NONE, ContainerCapabilities.validate permits audio-off whenever the input has video, and MKV routes the job to FFmpeg -- so "convert this and drop the soundtrack" is something a user can do today and nothing had built the command for. Both halves are asserted, and the second is not padding: -an alone still passes if the arm falls through to the else and emits an AAC encoder beside the flag, which is a file that is silent because the flag won while carrying an encoder nobody asked for. Three mutations, all run and restored. The third is the one that justifies the second assertion, since the first two break -an as a side effect and so cannot show it: Drop -> emptyList() red Drop -> the else arm's aac encoder red (loses -an as well) Drop -> listOf("-an", "-c:a", "aac") red -- -an intact, caught by assertFalse Co-Authored-By: Claude Opus 5 (1M context) --- .../ffmpeg/FFmpegCommandBuilderTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/src/test/java/org/libremediaconverter/ffmpeg/FFmpegCommandBuilderTest.kt b/app/src/test/java/org/libremediaconverter/ffmpeg/FFmpegCommandBuilderTest.kt index 9f9624d..1274d11 100644 --- a/app/src/test/java/org/libremediaconverter/ffmpeg/FFmpegCommandBuilderTest.kt +++ b/app/src/test/java/org/libremediaconverter/ffmpeg/FFmpegCommandBuilderTest.kt @@ -190,6 +190,30 @@ class FFmpegCommandBuilderTest { assertPair(cmd(OutputFormat.M4A_AAC), "-b:a", "192k") } + /** + * Turning audio off, which the Advanced picker offers and nothing had ever built a command for. + * + * `audioArgs`' `Drop` arm was `ci == 0`. The suite's only `-an` assertion is in + * `gif generates a palette to avoid banding and drops audio`, and that one comes from the image + * path (`FFmpegCommandBuilder.kt:79`/`:90`), which emits `-an` directly and never reaches + * `audioArgs`. Two sites, one string, one tested. + * + * It is a live path rather than defensive code: `AdvancedPicker` renders all of + * `AudioCodec.entries` including `NONE`, `ContainerCapabilities.validate` permits audio-off + * whenever the input has video, and MKV routes the job to FFmpeg. + * + * Both halves are asserted. `-an` alone would still pass if the arm fell through to the `else` + * and emitted an AAC encoder beside it -- a file that is silent because the flag won, carrying + * an encoder nobody asked for. + */ + @Test + fun `turning audio off drops the track instead of encoding one`() { + val args = cmd(OutputSpec(Container.MKV, VideoCodec.H264, AudioCodec.NONE)) + + assertTrue("audio turned off must emit -an, got $args", args.contains("-an")) + assertFalse("a dropped track must not also carry an encoder, got $args", args.contains("-c:a")) + } + @Test fun `audio only formats never carry a video encoder`() { listOf(OutputFormat.MP3, OutputFormat.FLAC, OutputFormat.WAV, OutputFormat.OPUS) -- 2.47.3