fix(audio): refuse trim windows shorter than one codec frame (#679)

trim-audio stream-copies, and a window below one frame ships a container with zero audio frames as a 200 success. Floor the window at 0.1s in the schema, and refuse after endS clamps to the real duration if the window drops under the floor. Fixes #671.
This commit is contained in:
SnapOtter
2026-07-30 09:34:56 +08:00
committed by GitHub
parent 6aacb4f3a9
commit 192d56e2ca
3 changed files with 34 additions and 1 deletions
+13 -1
View File
@@ -5,12 +5,19 @@ import { audioContentType, formatFfmpegSeconds, runMediaTool } from "../../lib/m
import { InputValidationError } from "../../modality/contract.js";
import { createToolRoute } from "../tool-factory.js";
// Stream copy cannot cut below one codec frame (mp3 ~26ms, flac up to ~93ms);
// smaller windows produce a container with zero audio frames.
const MIN_TRIM_WINDOW_S = 0.1;
const settingsSchema = z
.object({
startS: z.number().finite().min(0).default(0),
endS: z.number().finite().min(0.000001),
})
.refine((s) => s.endS > s.startS, { message: "End must be after start" });
.refine((s) => s.endS > s.startS, { message: "End must be after start" })
.refine((s) => s.endS - s.startS >= MIN_TRIM_WINDOW_S, {
message: `Trim window must be at least ${MIN_TRIM_WINDOW_S} seconds`,
});
export function registerTrimAudio(app: FastifyInstance) {
createToolRoute(app, {
@@ -34,6 +41,11 @@ export function registerTrimAudio(app: FastifyInstance) {
throw new InputValidationError("Start is beyond the end of the audio");
}
const endS = Math.min(settings.endS, info.durationS);
if (endS - settings.startS < MIN_TRIM_WINDOW_S) {
throw new InputValidationError(
`Trim window is shorter than ${MIN_TRIM_WINDOW_S} seconds after clamping to the audio duration`,
);
}
// Fast seek with stream-copy for audio
return [
"-ss",