Skip to content

feat(core): add liveEdgeStart and targetLiveWindow properties#1445

Merged
luwes merged 3 commits intomainfrom
feat/1391-live-edge
Apr 27, 2026
Merged

feat(core): add liveEdgeStart and targetLiveWindow properties#1445
luwes merged 3 commits intomainfrom
feat/1391-live-edge

Conversation

@luwes
Copy link
Copy Markdown
Collaborator

@luwes luwes commented Apr 22, 2026

Closes #1391

Summary

Expose liveEdgeStart and targetLiveWindow on the HLS media delegates (and corresponding store features) so apps can tell how far behind the live edge playback is and whether the stream supports DVR. Modeled after the media-ui-extensions Live Edge proposal and the shape used by @mux/mux-video / Media Chrome.

Changes

  • HlsJsMedia derives both values from LEVEL_LOADED (HOLD-BACK / PART-HOLD-BACK, falling back to per-spec multiples of TARGETDURATION / PART-TARGET). liveEdgeStart is read-derived from seekable.end each access; targetLiveWindow emits targetlivewindowchange on change.
  • NativeHlsMedia fetches the m3u8 on loadstart and parses #EXT-X-PLAYLIST-TYPE, #EXT-X-ENDLIST, #EXT-X-TARGETDURATION, #EXT-X-PART-INF to derive the same values (manifest info isn't exposed via the platform API). Falls back to buffered when Chrome leaves seekable empty.
  • HlsMedia bridges both getters and the targetlivewindowchange event from whichever delegate is active.
  • Store exposes a live feature (liveEdgeStart, targetLiveWindow) wired into the live presets, plus isMediaLiveCapable predicate.
  • Resets to NaN on engine recreation, MANIFEST_LOADING / DESTROYING (hls.js), and emptied (native).
Implementation notes
  • targetLiveWindow semantics: 0 = standard live sliding window, Infinity = DVR (EVENT playlist), NaN = on-demand or unknown.
  • liveEdgeStart is intentionally derived on every read (no cached value / no change event) so it tracks the current seekable.end, matching the media-ui-extensions spec guidance to re-read when seekable, targetLiveWindow, or streamType change.
  • Native m3u8 parsing lives in native-hls/m3u8-utils.ts and follows the first #EXT-X-STREAM-INF when handed a multivariant playlist — mirroring the approach in muxinc/elements/playback-core.

Testing

  • pnpm -F @videojs/core test — covers derivation for standard live / DVR / VOD / low-latency, seekable-based liveEdgeStart, multivariant resolution, fetch failures, and reset behavior.

Made with Cursor


Note

Medium Risk
Adds new live-edge surface area and events across HLS implementations, including native-HLS playlist fetching/parsing and additional event-driven store updates, which could affect playback behavior and introduce network/CORS edge cases.

Overview
Adds a new live-edge capability (liveEdgeStart, targetLiveWindow) and targetlivewindowchange event to core media types/state, and wires it into HlsMedia so apps can detect “at live edge” and DVR vs standard live.

Implements derivation for hls.js via a new HlsJsMediaLiveMixin (driven by manifest/level events) and for native HLS via a new NativeHlsMediaLiveMixin that fetches/parses .m3u8 playlists (including following the first variant of master playlists) and resets state on teardown/source changes.

Extends the DOM store with a new liveFeature (added to live audio/video presets) plus selectLive, and adds tests/docs covering derivation, reset behavior, and parsing utilities.

Reviewed by Cursor Bugbot for commit fe95d8f. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Apr 22, 2026 10:07pm

Request Review

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 22, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit fe95d8f
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69e94673142e240008257f15
😎 Deploy Preview https://deploy-preview-1445--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 22, 2026

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/media/hls-video 133.86 kB 134.56 kB +714 B +0.5% 🔺
/media/mux-audio 160.01 kB 160.68 kB +693 B +0.4% 🔺
/media/mux-video 159.87 kB 160.64 kB +782 B +0.5% 🔺
/media/native-hls-video 3.77 kB 4.53 kB +784 B +20.3% 🔴
/video (default + hls) 161.18 kB 162.12 kB +955 B +0.6% 🔺
/video (minimal + hls) 158.84 kB 159.62 kB +795 B +0.5% 🔺
Presets (7)
Entry Size
/video (default) 28.81 kB
/video (default + hls) 162.12 kB
/video (minimal) 26.29 kB
/video (minimal + hls) 159.62 kB
/audio (default) 26.65 kB
/audio (minimal) 24.28 kB
/background 4.15 kB
Media (8)
Entry Size
/media/background-video 1.04 kB
/media/container 1.73 kB
/media/dash-video 236.54 kB
/media/hls-video 134.56 kB
/media/mux-audio 160.68 kB
/media/mux-video 160.64 kB
/media/native-hls-video 4.53 kB
/media/simple-hls-video 15.80 kB
Players (3)
Entry Size
/video/player 7.05 kB
/audio/player 5.13 kB
/background/player 3.86 kB
Skins (29)
Entry Type Size
/video/minimal-skin.css css 3.50 kB
/video/skin.css css 3.53 kB
/video/minimal-skin js 26.30 kB
/video/minimal-skin.tailwind js 26.52 kB
/video/skin js 28.81 kB
/video/skin.tailwind js 28.89 kB
/audio/minimal-skin.css css 2.54 kB
/audio/skin.css css 2.50 kB
/audio/minimal-skin js 24.25 kB
/audio/minimal-skin.tailwind js 24.42 kB
/audio/skin js 26.65 kB
/audio/skin.tailwind js 26.84 kB
/background/skin.css css 117 B
/background/skin js 1.14 kB
/live-video/minimal-skin.css css 3.50 kB
/live-video/skin.css css 3.53 kB
/live-video/minimal-skin js 26.08 kB
/live-video/minimal-skin.tailwind js 26.13 kB
/live-video/skin js 28.51 kB
/live-video/skin.tailwind js 28.58 kB
/live-audio/minimal-skin.css css 2.54 kB
/live-audio/skin.css css 2.50 kB
/live-audio/minimal-skin js 24.06 kB
/live-audio/minimal-skin.tailwind js 24.02 kB
/live-audio/skin js 26.43 kB
/live-audio/skin.tailwind js 26.45 kB
/base.css css 157 B
/shared.css css 88 B
/skin-element js 1.35 kB
UI Components (25)
Entry Size
/ui/alert-dialog 1.03 kB
/ui/alert-dialog-close 479 B
/ui/alert-dialog-description 395 B
/ui/alert-dialog-title 379 B
/ui/buffering-indicator 1.85 kB
/ui/captions-button 2.58 kB
/ui/cast-button 2.63 kB
/ui/compounds 4.13 kB
/ui/controls 2.05 kB
/ui/error-dialog 3.09 kB
/ui/fullscreen-button 2.63 kB
/ui/hotkey 1.79 kB
/ui/mute-button 2.61 kB
/ui/pip-button 2.57 kB
/ui/play-button 2.59 kB
/ui/playback-rate-button 2.62 kB
/ui/popover 1.82 kB
/ui/poster 1.92 kB
/ui/seek-button 2.62 kB
/ui/slider 1.52 kB
/ui/thumbnail 2.97 kB
/ui/time 2.74 kB
/ui/time-slider 3.98 kB
/ui/tooltip 2.00 kB
/ui/volume-slider 2.70 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

Path Base PR Diff %
/media/hls-video 132.64 kB 133.12 kB +493 B +0.4% 🔺
/media/mux-audio 158.59 kB 159.40 kB +830 B +0.5% 🔺
/media/mux-video 158.53 kB 159.39 kB +883 B +0.5% 🔺
/media/native-hls-video 2.26 kB 3.04 kB +797 B +34.4% 🔴
/video (default + hls) 154.67 kB 155.56 kB +910 B +0.6% 🔺
/video (minimal + hls) 152.60 kB 153.25 kB +665 B +0.4% 🔺
Presets (7)
Entry Size
/video (default) 23.59 kB
/video (default + hls) 155.56 kB
/video (minimal) 21.21 kB
/video (minimal + hls) 153.25 kB
/audio (default) 19.14 kB
/audio (minimal) 17.69 kB
/background 756 B
Media (7)
Entry Size
/media/background-video 575 B
/media/dash-video 235.04 kB
/media/hls-video 133.12 kB
/media/mux-audio 159.40 kB
/media/mux-video 159.39 kB
/media/native-hls-video 3.04 kB
/media/simple-hls-video 14.36 kB
Skins (26)
Entry Type Size
/video/minimal-skin.css css 3.44 kB
/video/skin.css css 3.46 kB
/video/minimal-skin js 21.11 kB
/video/minimal-skin.tailwind js 24.62 kB
/video/skin js 23.51 kB
/video/skin.tailwind js 24.76 kB
/audio/minimal-skin.css css 2.44 kB
/audio/skin.css css 2.39 kB
/audio/minimal-skin js 17.60 kB
/audio/minimal-skin.tailwind js 20.11 kB
/audio/skin js 19.09 kB
/audio/skin.tailwind js 20.09 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 3.44 kB
/live-video/skin.css css 3.46 kB
/live-video/minimal-skin js 17.83 kB
/live-video/minimal-skin.tailwind js 21.24 kB
/live-video/skin js 20.26 kB
/live-video/skin.tailwind js 21.39 kB
/live-audio/minimal-skin.css css 2.44 kB
/live-audio/skin.css css 2.39 kB
/live-audio/minimal-skin js 15.87 kB
/live-audio/minimal-skin.tailwind js 18.08 kB
/live-audio/skin js 17.28 kB
/live-audio/skin.tailwind js 18.21 kB
UI Components (20)
Entry Size
/ui/alert-dialog 1.16 kB
/ui/buffering-indicator 1.87 kB
/ui/captions-button 2.08 kB
/ui/cast-button 2.06 kB
/ui/controls 1.86 kB
/ui/error-dialog 2.33 kB
/ui/fullscreen-button 2.07 kB
/ui/mute-button 2.06 kB
/ui/pip-button 2.02 kB
/ui/play-button 2.03 kB
/ui/playback-rate-button 2.04 kB
/ui/popover 1.93 kB
/ui/poster 1.72 kB
/ui/seek-button 2.09 kB
/ui/slider 3.37 kB
/ui/thumbnail 2.06 kB
/ui/time 2.62 kB
/ui/time-slider 2.99 kB
/ui/tooltip 2.18 kB
/ui/volume-slider 3.73 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core

Path Base PR Diff %
/dom/media/hls 131.89 kB 132.63 kB +757 B +0.6% 🔺
/dom/media/mux 158.01 kB 158.69 kB +699 B +0.4% 🔺
/dom/media/native-hls 1.61 kB 2.40 kB +810 B +49.2% 🔴
Entries (9)
Entry Size
. 4.96 kB
/dom 11.99 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.13 kB
/dom/media/google-cast 4.07 kB
/dom/media/hls 132.63 kB
/dom/media/mux 158.69 kB
/dom/media/native-hls 2.40 kB
/dom/media/simple-hls 13.73 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 999 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 695 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.92 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 40 B
/dom 13.33 kB
/playback-engine 13.24 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

Add JSDoc to `liveFeature` and expand JSDoc on `LiveVideoFeatures` /
`LiveAudioFeatures` types and their preset counterparts to call out the
new `PlayerFeature<MediaLiveState>` / `liveFeature` additions. Add a
`feature-live` reference page to the site mirroring the structure of
the other feature reference pages, and register it in the docs sidebar.

Made-with: Cursor
Inline the trivial `#setOffset` setter, reuse `#reset()` in the non-live
derive branch, drop a redundant `?? null`, and destructure `engine` once
in the constructor. In `m3u8-utils`, flatten `resolveFirstMediaPlaylistUrl`
with `findIndex`/`find`, remove a redundant `.trim()` before `Number()`,
and extract a small `fetchPlaylist` helper to dedupe the fetch + ok +
text pattern across the multivariant and media branches.

Made-with: Cursor
@luwes luwes merged commit e3d4ff9 into main Apr 27, 2026
26 checks passed
@luwes luwes deleted the feat/1391-live-edge branch April 27, 2026 19:40
@luwes luwes mentioned this pull request Apr 27, 2026
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.

Feature: Add liveEdgeStart and targetLiveWindow Properties

1 participant