Skip to content

Nested compositions that reuse a video id with runtime-assigned src collapse onto one scene in the export #3512

Description

@valeriangalliat

Describe the bug

Follow-up to #3340 / #3342. Duplicate media ids across nested compositions with a compile-time src now get a document-unique data-hf-render-id and both clips render.

The residual: <video id="clip" src=""> plus el.src = url in the scene script. Legal per file, check clean. The static parse skips empty src, so those clips exist only in the browser snapshot. That snapshot still keys by author id. After inline, last write wins:

  • Scene A (0–4s, data-media-start="10") gets scene B’s window — footage from ~40s.
  • Scene B (4–8s, data-media-start="40") gets nothing injected — blank panel.

Scene chrome still paints. Preview is correct (each scene’s script is scoped). Export disagrees.

#3342 stamps render ids only when a playable source is already on the element, which is why this path never got one. Native url(#…) / <use> is a different hole (#3490).

Link to reproduction

https://github.com/ArcadeHQ/hyperframes-repros/tree/patch/runtime-src-duplicate-ids

Steps to reproduce

  1. Clone https://github.com/ArcadeHQ/hyperframes-repros and check out patch/runtime-src-duplicate-ids.
  2. Run npx hyperframes@latest check — 0 errors (duplicate ids across files are allowed).
  3. Preview: both scenes show the burnt-in seconds counter (~12 in scene A, ~46 in scene B).
  4. Run npx hyperframes@latest render . -f 10.
  5. Open the MP4 at t≈2s and t≈6s. snapshots/ has actual vs expected frames.

Control: rename scene B’s video id to clip-b (and its script selector). Both windows render correctly.

Expected behavior

Scene A shows the counter around 10–14. Scene B shows it around 44–48. Same as preview.

Actual behavior

On hyperframes@0.8.15 (latest):

  • t≈2s: counter reads ~42 (scene B’s data-media-start).
  • t≈6s: white panel, no footage.

Environment

hyperframes doctor

  ✓ Version          0.8.15 (latest)
  ✓ Node.js          v24.19.0 (darwin arm64)
  ✓ CPU              10 cores · Apple M1 Pro @ 2400MHz
  ✓ Memory           16.0 GB total · 3.5 GB available
  ✓ Disk             358.9 GB free
  ✓ Frames cache     /var/folders/bl/c4hk6r7n34g1d81txd99gsgr0000gn/T/hyperframes-extract-cache-501 · 358.9 GB free at /var/folders/bl/c4hk6r7n34g1d81txd99gsgr0000gn/T/hyperframes-extract-cache-501 · default
  ✓ Archive extractor unzip
  ✓ Environment      non-TTY
  ✗ whisper-cpp      Not found (optional — needed for transcription)
  ✗ TTS (Kokoro)     Not installed (optional — local voice fallback)
  ✗ BGM (MusicGen)   Not installed (optional — local music fallback)
  ✓ FFmpeg           ffmpeg 8.1.2 at /opt/homebrew/bin/ffmpeg
  ✓ FFprobe          ffprobe 8.1.2 at /opt/homebrew/bin/ffprobe
  ✓ Chrome           cache: /Users/val/.cache/puppeteer/chrome-headless-shell/mac_arm-152.0.7977.42/chrome-headless-shell-mac-arm64/chrome-headless-shell
  ✗ Docker           Not found
  ✗ Docker running   Not running

Additional context

The source is a synthetic ffmpeg testsrc clip with a burnt-in seconds counter, so the wrong window is readable off the frame. Related, not the same: #3340 (static src, fixed by render keys), #3490 (SVG url(#) / <use>).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions