Skip to content

fix(lint): warn when a video's slot outlasts its source - #7696

Open
wonjangcloud9 wants to merge 1 commit into
heygen-com:mainfrom
wonjangcloud9:fix/lint-video-outlasts-source
Open

wonjangcloud9 wants to merge 1 commit into
heygen-com:mainfrom
wonjangcloud9:fix/lint-video-outlasts-source

Conversation

@wonjangcloud9

Copy link
Copy Markdown
Contributor

Context: I've been using HyperFrames every day for the past two weeks to make Korean Shorts and long-form videos, and it's been great to work with. This PR is one of a batch of fixes for things I ran into along the way, so you'll see several PRs from me at once — sorry for the pile-up, and thanks for your patience. Each one is reproduced on current main, independent of the others, and can be reviewed, merged or closed on its own.

What

hyperframes lint / check now report video_slot_outlasts_source (warning) when a <video>'s data-duration is longer than what its file has left to play, so the render holds the last frame for the rest of the slot.

⚠ video_slot_outlasts_source: Video "clip.mp4" runs out after 1.9s of its 9.02s slot (source 1.9s, starting 0s in). It holds its final frame for the last 7.12s.
  fix: Shorten data-duration to 1.9s, add loop, or use a longer source if this final-frame hold is unintended.

Why

A Short had <video data-start="0" data-duration="9.019"> pointing at an mp4 that is only 1.9 s long. The render showed 1.9 s of footage and then a still frame for 7.1 s; check passed with no media finding. Holding the final frame is the documented behavior (#2516, docs/reference/lint-rules.mdx video_media_start_at_or_past_eof), so this is not a render bug. What was missing is the warning: the existing probe-based rule only fires when data-media-start is at or past the end of the file, and it skips every video that has no data-media-start at all, which is the usual way to write a clip that starts at the top of its file.

Related work

How

packages/lint/src/hevcPreviewLint.ts:

  • readFiniteVideoSlot now also keeps slots with no data-media-start (it defaults to 0, as the runtime's readMediaOffsetSeconds does) and records data-duration and data-playback-rate (readPlaybackRate from @hyperframes/parsers/media-duration, the shared reader).
  • For each probed slot, a start at/past EOF keeps its existing finding; otherwise resolveNaturalDurationSeconds(source, mediaStart, rate) (the shared "one rule" for media length) gives the timeline seconds the source covers, and a slot longer than that by more than 0.1 s gets video_slot_outlasts_source. 0.1 s keeps a rounded-up data-duration (e.g. 5 s on a 4.97 s file) quiet.
  • Still skipped: loop, data-var-src, data-playback-start, and a rate lane in data-automation (the static probe cannot follow a ramp). The skip checks moved into hasUnknownSourcePosition to keep readFiniteVideoSlot under fallow's complexity limit.
  • Side effect: a finite video with no data-media-start is now duration-probed too (one ffprobe per unique file, same concurrency cap). The HEVC "probed once per file" test now counts codec probes only.

docs/reference/lint-rules.mdx: new video_slot_outlasts_source entry.

Test plan

Unit (packages/lint/src/project.test.ts, ffprobe mocked like the existing rule's tests):

  • warns for data-start="0" data-duration="9.019" on a 1.9 s source (message, fix hint, element id, file)
  • counts data-media-start (4 s slot from 3 s into a 6 s file) and data-playback-rate (4 s at 2x on a 6 s file); 3 s at 2x on a 6 s file is quiet
  • quiet for a 0.05 s hold, loop, a rate lane, and a start past EOF (which still gets only the old finding)

On main (affa03b6) with the fix stashed: 2 of the 3 new tests fail (× warns when a slot outlasts the source…, × counts data-media-start and data-playback-rate…). With the fix: packages/lint 27 files / 1312 tests pass; tsc --noEmit -p packages/lint clean.

Real CLI on a repro project (index.html with one <video id="clip" src="clip.mp4" data-start="0" data-duration="9.019" muted>, clip.mp4 = 1.9 s testsrc2):

$ hyperframes check --no-cache        # main
  0 error(s), 1 warning(s), 0 info(s)   (the one warning is craft's dead_stretch, which doesn't name the video)
◇  Check passed

$ hyperframes check --no-cache        # this branch
  ⚠ video_slot_outlasts_source: Video "clip.mp4" runs out after 1.9s of its 9.02s slot (source 1.9s, starting 0s in). It holds its final frame for the last 7.12s.
◇  Check passed

The freeze itself, rendered on main (-q draft): the frames at 2.5 s and 8.5 s match (PSNR 41 dB, encoder noise only), while 1.0 s vs 2.5 s is 22.6 dB (moving test pattern).

Gates: oxlint / oxfmt clean, fallow audit --base origin/main no issues, comment ratchet at or under share, lefthook pre-commit (format, fallow, typecheck) passed.

A <video data-start="0" data-duration="9.019"> whose file is 1.9 s long
renders 1.9 s of motion and then holds the last frame for 7.1 s. The
hold is the documented behavior, but check passed with nothing to say.
The existing probe-based rule only fired when data-media-start was at or
past the end of the file, and skipped videos with no data-media-start.

The same duration probe now also reports video_slot_outlasts_source when
the slot is longer than what the source has left to play (from
data-media-start, at data-playback-rate). Holds of 0.1 s or less are
rounding in data-duration and stay quiet; loop, data-var-src,
data-playback-start and rate lanes are skipped as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@mintlify

mintlify Bot commented Nov 14, 2026

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit c71347f.

Approve preview

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant