mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(producer): probe variable-bound media sources (#2444)
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { describe, expect, it, mock } from "bun:test";
|
||||
import { hasAutoStartVideos, hasScriptedAudioVolumeAutomation } from "./probeStage.js";
|
||||
import {
|
||||
hasAutoStartVideos,
|
||||
hasScriptedAudioVolumeAutomation,
|
||||
hasVariableBoundMedia,
|
||||
} from "./probeStage.js";
|
||||
|
||||
// ── Mocks for runProbeStage tests ────────────────────────────────────────────
|
||||
// Capture the cfg passed to createCaptureSession so we can assert it carries
|
||||
@@ -244,7 +248,36 @@ describe("hasAutoStartVideos", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasVariableBoundMedia", () => {
|
||||
it("requires a browser probe when an audio src is overridden by render variables", () => {
|
||||
const html = `<audio id="voice" src="fallback.wav" data-var-src="voice_src"></audio>`;
|
||||
|
||||
expect(hasVariableBoundMedia(html, { voice_src: "row-02.wav" })).toBe(true);
|
||||
});
|
||||
|
||||
it("does not probe unrelated overrides or image-only bindings", () => {
|
||||
const audio = `<audio src="fallback.wav" data-var-src="voice_src"></audio>`;
|
||||
const image = `<img src="fallback.png" data-var-src="hero_src" />`;
|
||||
|
||||
expect(hasVariableBoundMedia(audio, { title: "Row 02" })).toBe(false);
|
||||
expect(hasVariableBoundMedia(image, { hero_src: "row-02.png" })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("runProbeStage — forceScreenshot threading", () => {
|
||||
it("launches a probe for a static-duration composition with variable-bound audio", async () => {
|
||||
capturedCfgs.length = 0;
|
||||
const { runProbeStage } = await import("./probeStage.js");
|
||||
const input = makeProbeInput({});
|
||||
input.composition.duration = 5;
|
||||
input.compiled.html = `<audio id="voice" src="fallback.wav" data-var-src="voice_src"></audio>`;
|
||||
input.job.config.variables = { voice_src: "row-02.wav" };
|
||||
|
||||
await runProbeStage(input);
|
||||
|
||||
expect(capturedCfgs.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("passes forceScreenshot:true to createCaptureSession when stage input carries it but cfg does not (low-memory mode fix #1236)", async () => {
|
||||
capturedCfgs.length = 0;
|
||||
|
||||
|
||||
@@ -135,6 +135,25 @@ export function hasAutoStartVideos(html: string): boolean {
|
||||
return document.querySelector("video[data-hf-auto-start]") !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable-bound audio/video sources are resolved by the browser runtime, not
|
||||
* the static compiler. Probe them whenever the current render overrides the
|
||||
* referenced variable so media extraction follows the resolved row value.
|
||||
*/
|
||||
export function hasVariableBoundMedia(
|
||||
html: string,
|
||||
variables: Record<string, unknown> | undefined,
|
||||
): boolean {
|
||||
if (!variables || Object.keys(variables).length === 0) return false;
|
||||
const { document } = parseHTML(html);
|
||||
return Array.from(
|
||||
document.querySelectorAll("audio[data-var-src], video[data-var-src], source[data-var-src]"),
|
||||
).some((element) => {
|
||||
const variableId = element.getAttribute("data-var-src")?.trim();
|
||||
return Boolean(variableId && Object.hasOwn(variables, variableId));
|
||||
});
|
||||
}
|
||||
|
||||
export async function runProbeStage(input: ProbeStageInput): Promise<ProbeStageResult> {
|
||||
const {
|
||||
projectDir,
|
||||
@@ -166,11 +185,13 @@ export async function runProbeStage(input: ProbeStageInput): Promise<ProbeStageR
|
||||
compiled.html,
|
||||
composition.audios.length,
|
||||
);
|
||||
const hasVariableMedia = hasVariableBoundMedia(compiled.html, job.config.variables);
|
||||
const needsBrowser =
|
||||
composition.duration <= 0 ||
|
||||
compiled.unresolvedCompositions.length > 0 ||
|
||||
hasAutoStart ||
|
||||
hasScriptedAudio;
|
||||
hasScriptedAudio ||
|
||||
hasVariableMedia;
|
||||
|
||||
if (needsBrowser) {
|
||||
const reasons = [];
|
||||
@@ -179,6 +200,7 @@ export async function runProbeStage(input: ProbeStageInput): Promise<ProbeStageR
|
||||
reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
|
||||
if (hasAutoStart) reasons.push("auto-start video(s)");
|
||||
if (hasScriptedAudio) reasons.push("scripted audio volume");
|
||||
if (hasVariableMedia) reasons.push("variable-bound media source(s)");
|
||||
|
||||
log.info("Launching browser for composition probe...", {
|
||||
reasons,
|
||||
|
||||
Reference in New Issue
Block a user