- Player: _adoptIframeMedia now skips media with preload="metadata" or
"none", preventing parent-frame proxies from bypassing the preloader.
MutationObserver extended to watch preload attribute changes so proxies
are created just-in-time when the preloader promotes a clip.
- init.ts: lazy-mode demotion loop skips elements with data-preload-eager,
letting power users keep specific clips eagerly buffered.
- mediaPreloader: reads window.__HF_LAZY_PRELOAD_THRESHOLD as an override,
falling back to the default 6.
- Add onActivation callback to MediaPreloadManager; wired to
postRuntimeDiagnosticOnce in init.ts for observability
- Document LAZY_THRESHOLD rationale (why 6) and MAX_PROMOTED
defense-in-depth semantics
- Add render-mode bypass contract test (isLazy with exactly 6 clips)
- Add onActivation tests: fires once on lazy activation, skips below
threshold, deduplicates across refreshes
Three root-cause fixes for the lazy media preloading feature:
1. Untimed media orphaned at preload="metadata": the else branch in
bindMediaMetadataListeners demoted ALL media elements, but the
mediaPreloader only manages timed clips (data-start). Untimed media
(background audio, ambient loops) got stuck at metadata forever.
Now only timed elements are demoted.
2. Monotonic promotion with no eviction: once promoted, clips stayed
at preload="auto" forever. Scrubbing through the full timeline
promoted everything, bringing back the OOM crash. Added LRU eviction
with MAX_PROMOTED=5 — when clips leave the preload window, their src
is cleared and load() called to release buffered data per MDN. On
re-entry, the original src is restored.
3. Metadata preload without load(): setting preload="metadata" alone
doesn't guarantee the metadata fetch in Chrome Lite mode or Firefox
with media.preload.default=0. Now load() is called after demotion
to ensure el.duration is populated for timeline computation.
Also adds exact-boundary tests for LAZY_THRESHOLD=6 and eviction
coverage (evict on scrub, src restoration, MAX_PROMOTED cap, load()
called on eviction).
Compositions with many large video files (e.g., 6GB across 20 clips) crash
the browser because the runtime eagerly sets preload="auto" + .load() on
every media element at startup. All files buffer simultaneously, exhausting
memory.
Add a MediaPreloadManager that gates preloading based on playhead position:
- Activates when a composition has ≥6 timed media elements
- Only preloads clips within a 10-second lookahead window (or next 2 clips)
- Far-away clips stay at preload="metadata" (resolves duration without
downloading data)
- Advances the window on each transport tick and immediately on seek
- Render mode (window.__HF_EXPORT_RENDER_SEEK_CONFIG) keeps eager preload
for deterministic frame capture
- Small compositions (<6 clips) keep eager preload — no behavior change
Studio's hasUnloadedAssets now skips elements with preload!="auto", so
deferred clips don't block the loading overlay.