fix(core): avoid live volume probe during render

This commit is contained in:
James
2026-07-26 22:39:58 +00:00
parent 2dddb4c463
commit 477defc7e7
11 changed files with 219 additions and 1 deletions
+4
View File
@@ -1834,6 +1834,10 @@ export function initSandboxRuntimeModular(): void {
state.capturedTimeline,
getSafeTimelineDurationSeconds(state.capturedTimeline, 0),
volumeKeyframeCache,
{
allowLiveTimelineSeek: !(window as Window & { __HF_RENDER_CAPTURE_MODE?: boolean })
.__HF_RENDER_CAPTURE_MODE,
},
);
};
@@ -3,6 +3,32 @@ import { describe, expect, it } from "vitest";
import { probeAndCacheElementVolume } from "./mediaVolumeEnvelope";
describe("probeAndCacheElementVolume", () => {
it("does not seek or cache when live timeline probing is disabled", () => {
const audio = document.createElement("audio");
audio.dataset.volume = "1";
document.body.append(audio);
let seekCount = 0;
const timeline = {
totalTime(next?: number) {
if (next !== undefined) {
seekCount += 1;
audio.volume = next >= 1 ? 0 : 1;
}
return 0.75;
},
};
const cache = new WeakMap<HTMLMediaElement, { time: number; volume: number }[]>();
probeAndCacheElementVolume(audio, timeline, 1, cache, {
allowLiveTimelineSeek: false,
});
expect(seekCount).toBe(0);
expect(audio.volume).toBe(1);
expect(cache.has(audio)).toBe(false);
});
it("restores the timeline playhead after sampling volume automation", () => {
const audio = document.createElement("audio");
audio.dataset.volume = "1";
@@ -129,6 +129,18 @@ export function probeElementVolumeKeyframes(
export type RuntimeTimelineRef = Partial<Pick<RuntimeTimelineLike, "totalTime" | "seek">>;
export interface VolumeProbeOptions {
/**
* Render/probe pages must not sample the live visual timeline during runtime
* initialization. The producer discovers audio automation in its own
* isolated pass and bakes it before frame capture, so seeking here is both
* redundant and capable of materializing future zero-duration GSAP state.
*
* Preview callers omit this option and retain live automation discovery.
*/
allowLiveTimelineSeek?: boolean;
}
/**
* Probe a media element and, if volume automation is detected, store the
* keyframes in `cache`. Safe to call with a null timeline returns early.
@@ -138,7 +150,9 @@ export function probeAndCacheElementVolume(
timeline: RuntimeTimelineRef | null | undefined,
compositionDuration: number,
cache: WeakMap<HTMLMediaElement, VolumeKeyframe[]>,
options: VolumeProbeOptions = {},
): void {
if (options.allowLiveTimelineSeek === false) return;
if (!timeline) return;
if (!(mediaEl instanceof HTMLAudioElement) && !(mediaEl instanceof HTMLVideoElement)) return;
if (compositionDuration <= 0) return;