mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(core): add color grading schema and lut parsing
This commit is contained in:
@@ -17,6 +17,29 @@ function makeTempProject(files: Record<string, string>): string {
|
||||
return dir;
|
||||
}
|
||||
|
||||
function makeColorGradingProject(lutSrc: string, files: Record<string, string> = {}): string {
|
||||
return makeTempProject({
|
||||
"index.html": `<!doctype html>
|
||||
<html><body>
|
||||
<div data-composition-id="root" data-width="320" data-height="180">
|
||||
<video
|
||||
id="clip"
|
||||
src="clip.mp4"
|
||||
data-color-grading='{"lut":{"src":"${lutSrc}","intensity":0.5}}'></video>
|
||||
</div>
|
||||
<script>window.__timelines = window.__timelines || {}; window.__timelines.root = {}</script>
|
||||
</body></html>`,
|
||||
...files,
|
||||
});
|
||||
}
|
||||
|
||||
function readBundledColorGradingLutSrc(bundled: string): string | undefined {
|
||||
const { document } = parseHTML(bundled);
|
||||
const rawLook = document.getElementById("clip")?.getAttribute("data-color-grading") ?? "";
|
||||
const parsed = JSON.parse(rawLook) as { lut?: { src?: string } };
|
||||
return parsed.lut?.src;
|
||||
}
|
||||
|
||||
// Mirror the repo convention (preview.test.ts): skip symlink cases on
|
||||
// non-symlink-privileged Windows runners rather than crash the suite.
|
||||
function tryCreateSymlink(target: string, path: string, type: "dir" | "file"): boolean {
|
||||
@@ -921,6 +944,26 @@ describe("bundleToSingleHtml", () => {
|
||||
expect(bundled).toContain("margin: 0");
|
||||
});
|
||||
|
||||
it("inlines cube LUT files referenced from data-color-grading", async () => {
|
||||
const dir = makeColorGradingProject("assets/luts/identity.cube", {
|
||||
"assets/luts/identity.cube": `LUT_3D_SIZE 2
|
||||
0 0 0
|
||||
1 0 0
|
||||
0 1 0
|
||||
1 1 0
|
||||
0 0 1
|
||||
1 0 1
|
||||
0 1 1
|
||||
1 1 1`,
|
||||
});
|
||||
|
||||
const bundled = await bundleToSingleHtml(dir);
|
||||
const lutSrc = readBundledColorGradingLutSrc(bundled);
|
||||
|
||||
expect(lutSrc).toMatch(/^data:text\/plain;base64,/);
|
||||
expect(lutSrc).not.toContain("assets/luts/identity.cube");
|
||||
});
|
||||
|
||||
it("resolves nested CSS @import chains", async () => {
|
||||
const dir = makeTempProject({
|
||||
"index.html": `<!doctype html>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { getHyperframeRuntimeScript } from "../generated/runtime-inline";
|
||||
import { readDeclaredDefaults } from "../runtime/getVariables";
|
||||
import { inlineSubCompositions } from "./inlineSubCompositions";
|
||||
import { isSafePath, resolveWithinProject } from "../safePath.js";
|
||||
import { HF_COLOR_GRADING_ATTR } from "../colorGrading";
|
||||
|
||||
const DEFAULT_RUNTIME_SCRIPT_URL = "";
|
||||
|
||||
@@ -201,6 +202,7 @@ const INLINE_MIME: Record<string, string> = {
|
||||
".svg": "image/svg+xml",
|
||||
".json": "application/json",
|
||||
".txt": "text/plain",
|
||||
".cube": "text/plain",
|
||||
".xml": "application/xml",
|
||||
};
|
||||
|
||||
@@ -219,6 +221,35 @@ function maybeInlineRelativeAssetUrl(urlValue: string, projectDir: string): stri
|
||||
return appendSuffixToUrl(dataUrl, suffix);
|
||||
}
|
||||
|
||||
function isJsonRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
// fallow-ignore-next-line complexity
|
||||
function rewriteLookLutWithInlinedAssets(value: string, projectDir: string): string {
|
||||
if (!value.trim().startsWith("{")) return value;
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
if (!isJsonRecord(parsed)) return value;
|
||||
|
||||
const lut = parsed.lut;
|
||||
if (typeof lut === "string") {
|
||||
const inlined = maybeInlineRelativeAssetUrl(lut, projectDir);
|
||||
if (!inlined) return value;
|
||||
parsed.lut = inlined;
|
||||
return JSON.stringify(parsed);
|
||||
}
|
||||
if (!isJsonRecord(lut) || typeof lut.src !== "string") return value;
|
||||
const inlined = maybeInlineRelativeAssetUrl(lut.src, projectDir);
|
||||
if (!inlined) return value;
|
||||
lut.src = inlined;
|
||||
return JSON.stringify(parsed);
|
||||
}
|
||||
|
||||
function rewriteSrcsetWithInlinedAssets(srcsetValue: string, projectDir: string): string {
|
||||
if (!srcsetValue) return srcsetValue;
|
||||
return srcsetValue
|
||||
@@ -947,6 +978,12 @@ export async function bundleToSingleHtml(
|
||||
rewriteCssUrlsWithInlinedAssets(el.getAttribute("style") || "", projectDir),
|
||||
);
|
||||
}
|
||||
for (const el of [...document.querySelectorAll(`[${HF_COLOR_GRADING_ATTR}]`)]) {
|
||||
const value = el.getAttribute(HF_COLOR_GRADING_ATTR);
|
||||
if (value) {
|
||||
el.setAttribute(HF_COLOR_GRADING_ATTR, rewriteLookLutWithInlinedAssets(value, projectDir));
|
||||
}
|
||||
}
|
||||
|
||||
return document.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user