mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(producer): keep auto renders SDR when a graded canvas is present
This commit is contained in:
@@ -9,6 +9,7 @@ import { loadProducer } from "./producer.js";
|
||||
export interface ProjectMedia {
|
||||
videos: readonly Pick<VideoElement, "src" | "hasAudio" | "graded">[];
|
||||
images: readonly { src: string; graded?: boolean }[];
|
||||
gradedCanvas?: boolean;
|
||||
audios: readonly AudioElement[];
|
||||
/** The bundled document the lists were read from. */
|
||||
html: string;
|
||||
|
||||
@@ -219,6 +219,7 @@ export {
|
||||
// ── Media processing ───────────────────────────────────────────────────────────
|
||||
export {
|
||||
parseVideoElements,
|
||||
hasGradedCanvas,
|
||||
parseImageElements,
|
||||
extractVideoFramesRange,
|
||||
extractAllVideoFrames,
|
||||
|
||||
@@ -16,6 +16,7 @@ import { tmpdir } from "node:os";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import {
|
||||
parseVideoElements,
|
||||
hasGradedCanvas,
|
||||
parseImageElements,
|
||||
extractAllVideoFrames,
|
||||
extractVideoFramesRange,
|
||||
@@ -751,6 +752,10 @@ describe("parseVideoElements", () => {
|
||||
["gi", true],
|
||||
["pi", undefined],
|
||||
]);
|
||||
expect(hasGradedCanvas(html)).toBe(false);
|
||||
expect(
|
||||
hasGradedCanvas(`<canvas data-color-grading='{"preset":"clean-studio"}'></canvas>`),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -105,7 +105,6 @@ export interface VideoElement {
|
||||
loop: boolean;
|
||||
hasAudio: boolean;
|
||||
hidden?: boolean;
|
||||
/** Carries a `data-color-grading` treatment, which grades in SDR. */
|
||||
graded?: boolean;
|
||||
}
|
||||
|
||||
@@ -858,10 +857,16 @@ export interface ImageElement {
|
||||
src: string;
|
||||
start: number;
|
||||
end: number;
|
||||
/** Carries a `data-color-grading` treatment, which grades in SDR. */
|
||||
graded?: boolean;
|
||||
}
|
||||
|
||||
export function hasGradedCanvas(html: string): boolean {
|
||||
return (
|
||||
parseHTML(unwrapTemplate(html)).document.querySelector(`canvas[${HF_COLOR_GRADING_ATTR}]`) !==
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
export function parseImageElements(html: string): ImageElement[] {
|
||||
const images: ImageElement[] = [];
|
||||
const { document } = parseHTML(unwrapTemplate(html));
|
||||
|
||||
@@ -233,5 +233,6 @@ describe("gradingKeepsSdr", () => {
|
||||
expect(gradingKeepsSdr("auto", graded)).toBe(true);
|
||||
expect(gradingKeepsSdr("force-hdr", graded)).toBe(false);
|
||||
expect(gradingKeepsSdr("auto", { videos: [{}], images: [] })).toBe(false);
|
||||
expect(gradingKeepsSdr("auto", { videos: [], images: [], gradedCanvas: true })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -145,11 +145,16 @@ export function findHdrAutoPromotion(
|
||||
/** Grading is SDR-only, so in auto mode a composition with graded media renders SDR, as `--sdr` does. */
|
||||
export function gradingKeepsSdr(
|
||||
hdrMode: "auto" | "force-hdr" | "force-sdr" | undefined,
|
||||
media: { videos: readonly { graded?: boolean }[]; images: readonly { graded?: boolean }[] },
|
||||
media: {
|
||||
videos: readonly { graded?: boolean }[];
|
||||
images: readonly { graded?: boolean }[];
|
||||
gradedCanvas?: boolean;
|
||||
},
|
||||
): boolean {
|
||||
return (
|
||||
(hdrMode ?? "auto") === "auto" &&
|
||||
[...media.videos, ...media.images].some(({ graded }) => graded === true)
|
||||
(media.gradedCanvas === true ||
|
||||
[...media.videos, ...media.images].some(({ graded }) => graded === true))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ export interface CompiledComposition {
|
||||
audios: AudioElement[];
|
||||
images: ImageElement[];
|
||||
droppedMediaIds: Set<string>;
|
||||
gradedCanvas: boolean;
|
||||
masterFxChain?: string;
|
||||
unresolvedCompositions: UnresolvedElement[];
|
||||
/** Assets that resolve outside projectDir. Keys are the path used in HTML, values are absolute filesystem paths. */
|
||||
@@ -2236,7 +2237,7 @@ export async function compileForRender(
|
||||
// within one composition file: two scenes declaring `<video id="clip">` — or
|
||||
// two bare `<video>`s, both auto-numbered `hf-video-0` — collapsed into one
|
||||
// entry and injected frames onto whichever element came first. See #3340.
|
||||
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
||||
const { videos, audios, images, droppedMediaIds, gradedCanvas } = collectRenderMedia(html);
|
||||
const masterFxChain = parseMasterFxChain(html);
|
||||
|
||||
// Advisory video checks (sparse keyframes, VFR). Fire-and-forget — these spawn
|
||||
@@ -2294,6 +2295,7 @@ export async function compileForRender(
|
||||
audios,
|
||||
images,
|
||||
droppedMediaIds,
|
||||
gradedCanvas,
|
||||
...(masterFxChain ? { masterFxChain } : {}),
|
||||
unresolvedCompositions,
|
||||
externalAssets,
|
||||
|
||||
@@ -47,6 +47,7 @@ export interface CompositionMetadata {
|
||||
videos: VideoElement[];
|
||||
audios: AudioElement[];
|
||||
images: ImageElement[];
|
||||
gradedCanvas?: boolean;
|
||||
masterFxChain?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
@@ -298,6 +298,7 @@ export async function runCompileStage(input: CompileStageInput): Promise<Compile
|
||||
videos: compiled.videos,
|
||||
audios: compiled.audios,
|
||||
images: compiled.images,
|
||||
gradedCanvas: compiled.gradedCanvas,
|
||||
masterFxChain: compiled.masterFxChain,
|
||||
width: compiled.width,
|
||||
height: compiled.height,
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from "@hyperframes/core/media-timing";
|
||||
import {
|
||||
parseVideoElements,
|
||||
hasGradedCanvas,
|
||||
parseImageElements,
|
||||
parseAudioElements,
|
||||
isSelfOrAncestorHidden,
|
||||
@@ -211,6 +212,7 @@ export interface RenderMedia {
|
||||
images: ImageElement[];
|
||||
/** Ids of clips in the document that never play; the browser probe must not add them back. */
|
||||
droppedMediaIds: Set<string>;
|
||||
gradedCanvas: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +263,7 @@ export function collectRenderMedia(html: string): RenderMedia {
|
||||
else droppedMediaIds.add(elementId);
|
||||
}
|
||||
|
||||
return { videos, audios, images, droppedMediaIds };
|
||||
return { videos, audios, images, droppedMediaIds, gradedCanvas: hasGradedCanvas(html) };
|
||||
}
|
||||
|
||||
function placeAudio(audio: AudioElement, window: HostWindow): AudioElement | null {
|
||||
|
||||
Reference in New Issue
Block a user