mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
feat(cli,producer): add gif output format with two-pass palette encode (#1333)
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
co-authored by
Matt Van Horn
parent
e0ecd4d2d1
commit
e6b8d66c2d
@@ -200,6 +200,20 @@ describe("buildDockerRunArgs", () => {
|
||||
expect(args[formatIdx + 1]).toBe("png-sequence");
|
||||
});
|
||||
|
||||
it("forwards --format gif and --gif-loop to the container", () => {
|
||||
const args = buildDockerRunArgs({
|
||||
...FIXED_INPUT,
|
||||
outputFilename: "demo.gif",
|
||||
options: { ...BASE, format: "gif", gifLoop: 0 },
|
||||
});
|
||||
const formatIdx = args.indexOf("--format");
|
||||
const loopIdx = args.indexOf("--gif-loop");
|
||||
expect(formatIdx).toBeGreaterThanOrEqual(0);
|
||||
expect(args[formatIdx + 1]).toBe("gif");
|
||||
expect(loopIdx).toBeGreaterThanOrEqual(0);
|
||||
expect(args[loopIdx + 1]).toBe("0");
|
||||
});
|
||||
|
||||
it("forwards --video-bitrate to the container when set", () => {
|
||||
const args = buildDockerRunArgs({
|
||||
...FIXED_INPUT,
|
||||
|
||||
@@ -39,7 +39,8 @@ export interface DockerRenderOptions {
|
||||
*/
|
||||
fps: Fps;
|
||||
quality: "draft" | "standard" | "high";
|
||||
format: "mp4" | "webm" | "mov" | "png-sequence";
|
||||
format: "mp4" | "webm" | "mov" | "png-sequence" | "gif";
|
||||
gifLoop?: number;
|
||||
workers?: number;
|
||||
gpu: boolean;
|
||||
browserGpu: boolean;
|
||||
@@ -116,6 +117,7 @@ export function buildDockerRunArgs(input: DockerRunArgsInput): string[] {
|
||||
options.quality,
|
||||
"--format",
|
||||
options.format,
|
||||
...(options.gifLoop != null ? ["--gif-loop", String(options.gifLoop)] : []),
|
||||
...(options.workers != null ? ["--workers", String(options.workers)] : []),
|
||||
...(options.crf != null ? ["--crf", String(options.crf)] : []),
|
||||
...(options.videoBitrate ? ["--video-bitrate", options.videoBitrate] : []),
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
MAX_PAGE_NAVIGATION_TIMEOUT_SECONDS,
|
||||
parseBrowserTimeoutMsArg,
|
||||
parseCompositionEntryArg,
|
||||
parseGifLoopArg,
|
||||
type BrowserTimeoutParseResult,
|
||||
type CompositionEntryParseResult,
|
||||
} from "./renderArgs.js";
|
||||
@@ -171,3 +172,18 @@ describe("parseCompositionEntryArg", () => {
|
||||
expect(err.kind).toBe("outside-project");
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseGifLoopArg", () => {
|
||||
it("accepts absent flag, bounds, and integers", () => {
|
||||
expect(parseGifLoopArg(undefined)).toEqual({ ok: true, value: undefined });
|
||||
expect(parseGifLoopArg("0")).toEqual({ ok: true, value: 0 });
|
||||
expect(parseGifLoopArg("65535")).toEqual({ ok: true, value: 65535 });
|
||||
});
|
||||
|
||||
it("rejects out-of-range, non-integer, and empty inputs", () => {
|
||||
expect(parseGifLoopArg("-1").ok).toBe(false);
|
||||
expect(parseGifLoopArg("65536").ok).toBe(false);
|
||||
expect(parseGifLoopArg("1.5").ok).toBe(false);
|
||||
expect(parseGifLoopArg(" ").ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -219,3 +219,28 @@ export function resolveCompositionEntryArg(
|
||||
}
|
||||
return result.value;
|
||||
}
|
||||
|
||||
export type GifLoopParseResult =
|
||||
| { ok: true; value: number | undefined }
|
||||
| { ok: false; message: string };
|
||||
|
||||
/**
|
||||
* Parse and validate `--gif-loop <count>` (GIF Netscape loop count).
|
||||
* Returns `{ ok: true, value: undefined }` when the flag is absent so the
|
||||
* caller can apply the format-dependent default (0 = infinite for gif).
|
||||
*/
|
||||
export function parseGifLoopArg(raw: string | undefined): GifLoopParseResult {
|
||||
if (raw === undefined) return { ok: true, value: undefined };
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.length === 0) {
|
||||
return { ok: false, message: "GIF loop count must not be empty." };
|
||||
}
|
||||
const parsed = Number(trimmed);
|
||||
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65_535) {
|
||||
return {
|
||||
ok: false,
|
||||
message: `Got "${raw}". GIF loop count must be an integer between 0 and 65535.`,
|
||||
};
|
||||
}
|
||||
return { ok: true, value: parsed };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user