diff --git a/src/handlers/FFmpeg.ts b/src/handlers/FFmpeg.ts index 33a62c2a..176391f9 100644 --- a/src/handlers/FFmpeg.ts +++ b/src/handlers/FFmpeg.ts @@ -7,6 +7,40 @@ import mime from "mime"; import normalizeMimeType from "../normalizeMimeType.ts"; import CommonFormats from "src/CommonFormats.ts"; +export function getSpecialAudioFormats(): FileFormat[] { + return [ + { + name: "Ogg Opus Audio", + format: "opus", + extension: "ogg", + mime: CommonFormats.OGG.mime, + from: true, + to: false, + internal: "ogg", + category: "audio", + lossless: false + }, + { + name: "Ogg Vorbis Audio", + format: "vorbis", + extension: "ogg", + mime: CommonFormats.OGG.mime, + from: false, + to: true, + internal: "ogg", + category: "audio", + lossless: false + } + ]; +} + +export function getCodecArgsForFormat(outputFormat: FileFormat): string[] { + if (outputFormat.internal === "ogg" && outputFormat.format === "vorbis") { + return ["-c:a", "libvorbis"]; + } + return []; +} + class FFmpegHandler implements FormatHandler { static formatNames: Map = new Map([ @@ -258,6 +292,7 @@ class FFmpegHandler implements FormatHandler { // Add PNG input explicitly - FFmpeg otherwise treats both PNG and // APNG as the same thing. this.supportedFormats.push(CommonFormats.PNG.builder("png").allowFrom()); + this.supportedFormats.push(...getSpecialAudioFormats()); this.#ffmpeg.terminate(); @@ -302,6 +337,7 @@ class FFmpegHandler implements FormatHandler { } else if (outputFormat.internal === "asf") { command.push("-b:v", "15M", "-b:a", "192k"); } + command.push(...getCodecArgsForFormat(outputFormat)); if (args) command.push(...args); command.push("output"); diff --git a/test/handlers/ffmpeg.test.ts b/test/handlers/ffmpeg.test.ts new file mode 100644 index 00000000..b4266c73 --- /dev/null +++ b/test/handlers/ffmpeg.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from "bun:test"; +import CommonFormats from "../../src/CommonFormats.ts"; +import { getCodecArgsForFormat, getSpecialAudioFormats } from "../../src/handlers/FFmpeg.ts"; + +test("ffmpeg exposes distinct ogg opus and ogg vorbis aliases", () => { + const formats = getSpecialAudioFormats(); + + expect(formats).toEqual([ + { + name: "Ogg Opus Audio", + format: "opus", + extension: "ogg", + mime: CommonFormats.OGG.mime, + from: true, + to: false, + internal: "ogg", + category: "audio", + lossless: false + }, + { + name: "Ogg Vorbis Audio", + format: "vorbis", + extension: "ogg", + mime: CommonFormats.OGG.mime, + from: false, + to: true, + internal: "ogg", + category: "audio", + lossless: false + } + ]); +}); + +test("ffmpeg forces libvorbis for ogg vorbis output", () => { + const vorbisFormat = getSpecialAudioFormats()[1]; + expect(getCodecArgsForFormat(vorbisFormat)).toEqual(["-c:a", "libvorbis"]); + + const plainOgg = CommonFormats.OGG.builder("ogg").allowTo(); + expect(getCodecArgsForFormat(plainOgg)).toEqual([]); +});