Commit Graph
4 Commits
Author SHA1 Message Date
Miguel Ángel 35eab94e69 fix(core): parent-frame proxy bypass, data-preload-eager opt-out, configurable threshold
- 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.
2026-05-11 19:37:06 -07:00
Miguel Ángel a96d99680f fix(core): address staff review — diagnostics, comments, test coverage
- 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
2026-05-11 19:26:18 -07:00
Miguel Ángel 0c6f438ae7 fix(core): add LRU eviction to media preloader, protect untimed media
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).
2026-05-11 18:44:58 -07:00
Miguel Ángel 773c4261e2 feat(core): lazy media preloading for heavy compositions
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.
2026-05-11 18:41:59 -07:00