mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(cli,producer): cross-multiply aspect check, CLI HDR guard, honest banner
This commit is contained in:
@@ -207,6 +207,35 @@ describe("renderLocal browser GPU config", () => {
|
||||
expect(producerState.createdJobs[0]?.entryFile).toBeUndefined();
|
||||
});
|
||||
|
||||
it("forwards outputResolution to createRenderJob when --resolution is set", async () => {
|
||||
await renderLocal("/tmp/project", "/tmp/out.mp4", {
|
||||
fps: 30,
|
||||
quality: "standard",
|
||||
format: "mp4",
|
||||
gpu: false,
|
||||
browserGpuMode: "software",
|
||||
hdrMode: "auto",
|
||||
quiet: true,
|
||||
outputResolution: "landscape-4k",
|
||||
});
|
||||
|
||||
expect(producerState.createdJobs[0]?.outputResolution).toBe("landscape-4k");
|
||||
});
|
||||
|
||||
it("omits outputResolution from createRenderJob by default", async () => {
|
||||
await renderLocal("/tmp/project", "/tmp/out.mp4", {
|
||||
fps: 30,
|
||||
quality: "standard",
|
||||
format: "mp4",
|
||||
gpu: false,
|
||||
browserGpuMode: "software",
|
||||
hdrMode: "auto",
|
||||
quiet: true,
|
||||
});
|
||||
|
||||
expect(producerState.createdJobs[0]?.outputResolution).toBeUndefined();
|
||||
});
|
||||
|
||||
it("can force the CLI process to exit after a successful local render", async () => {
|
||||
vi.useFakeTimers();
|
||||
const exit = vi
|
||||
|
||||
@@ -253,6 +253,18 @@ export default defineCommand({
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
// Reject the --resolution + --hdr combination at the CLI layer so the
|
||||
// user sees the friendly errorBox before any work directories or
|
||||
// ffmpeg processes spin up. The orchestrator also enforces this via
|
||||
// resolveDeviceScaleFactor — defense in depth.
|
||||
if (args.hdr) {
|
||||
errorBox(
|
||||
"Conflicting flags",
|
||||
"--resolution cannot be combined with --hdr. The HDR pipeline composites at composition dimensions and does not yet support supersampling.",
|
||||
"Render in two passes: HDR at composition resolution, then upscale separately with ffmpeg.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Validate workers ──────────────────────────────────────────────────
|
||||
@@ -369,7 +381,11 @@ export default defineCommand({
|
||||
);
|
||||
console.log(c.dim(" " + fps + "fps \u00B7 " + quality + " \u00B7 " + workerLabel));
|
||||
if (outputResolution) {
|
||||
console.log(c.dim(" Output resolution: " + outputResolution + " (supersampled via DPR)"));
|
||||
// Don't claim "supersampled" — when the composition is already at the
|
||||
// target dimensions, the DPR resolves to 1 and no supersampling
|
||||
// happens. We don't have the composition's dims at this point in the
|
||||
// CLI, so describe the intent rather than the mechanism.
|
||||
console.log(c.dim(" Output resolution: " + outputResolution));
|
||||
}
|
||||
if (useGpu || browserGpuMode !== "software") {
|
||||
const gpuModes = [
|
||||
|
||||
@@ -616,9 +616,11 @@ export function resolveDeviceScaleFactor(input: {
|
||||
);
|
||||
}
|
||||
const target = CANVAS_DIMENSIONS[input.outputResolution];
|
||||
const widthRatio = target.width / input.compositionWidth;
|
||||
const heightRatio = target.height / input.compositionHeight;
|
||||
if (widthRatio !== heightRatio) {
|
||||
// Aspect-ratio compare via cross-multiplication so the equality is integer-
|
||||
// safe. Float division (`target.width / compositionWidth`) loses precision
|
||||
// for non-power-of-2 ratios (e.g. cinema 4K 4096×2160 = 1.8963…) and a
|
||||
// future preset could trip a false-mismatch on otherwise valid input.
|
||||
if (target.width * input.compositionHeight !== target.height * input.compositionWidth) {
|
||||
throw new Error(
|
||||
`outputResolution ${input.outputResolution} (${target.width}×${target.height}) ` +
|
||||
`does not match the aspect ratio of the composition ` +
|
||||
@@ -626,6 +628,8 @@ export function resolveDeviceScaleFactor(input: {
|
||||
`Pick a preset whose orientation matches.`,
|
||||
);
|
||||
}
|
||||
// Aspect ratios match → widthRatio === heightRatio. Compute once.
|
||||
const widthRatio = target.width / input.compositionWidth;
|
||||
if (widthRatio < 1) {
|
||||
throw new Error(
|
||||
`outputResolution ${input.outputResolution} (${target.width}×${target.height}) ` +
|
||||
|
||||
Reference in New Issue
Block a user