Skip to content

feat(playback-core): Support apple JSON chapters from com.apple.hls.chapters session data - #1360

Merged
javierarams merged 12 commits into
mainfrom
core-json-chapters
Sep 29, 2026
Merged

javierarams merged 12 commits into
mainfrom
core-json-chapters

Conversation

@javierarams

@javierarams javierarams commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1359

Summary

Mux Video now publishes chapters as Apple JSON Chapters Notation, referenced from the multivariant playlist by #EXT-X-SESSION-DATA:DATA-ID="com.apple.hls.chapters". We already fetched that document for the Mux metadata but ignored its chapters. It's now parsed into the existing chapters text track, so the time range chapters UI works with no player-side change, for both native HLS and hls.js playback.

Changes

  • parseAppleJsonChapters turns the document into Chapter[]:
    • chapters end at the next entry's start-time, or at their own duration when they have one
    • the title follows the viewer's language: a lang set on the player or the media element first, then navigator.languages, then und, then the first title.
    • Full tags match before primary subtags, case-insensitive
    • titles without a language are kept
    • untitled entries are dropped, so the [{ "start-time": 0 }] document Mux serves for assets without metadata adds no chapters.
  • The document is fetched once and feeds both the Mux metadata (muxmetadata, unchanged) and the chapters.
  • The fetch and the apply step are aborted on teardown, so a source change never leaves the previous source's chapters or metadata behind.
  • Chapters added through addChapters() always take precedence:
    • stream chapters are skipped when the user already added some
    • a later addChapters() replaces them instead of merging with them or truncating them
    • an addChapters() call made while the chapters track is being created waits for it and still wins.
  • Applying the stream chapters again replaces them instead of duplicating cues.
  • Relative URIs are resolved against the playlist URL after redirects: response.url on native (falling back to src when it is empty), and MANIFEST_LOADED with data.url on hls.js. The URI/VALUE check no longer throws when both are missing.

Behavior changes

  • The open last chapter no longer reports Number.MAX_SAFE_INTEGER. It ends at Infinity until the duration is known and greater than zero. This is a public API change. An endTime that is not finite is treated as missing by addChapters() and addCuePoints().
  • forceHiddenTracks no longer resets src on tracks without a src. Resetting it emptied their cues, and in WebKit it dropped the stream chapters.
  • fetchAndDispatchMuxMetadata is now aborted on teardown, on the mp4 path too.

Not changed on purpose

  • A repeated DATA-ID keeps the last entry, same as hls.js, so native and hls.js pick the same document.
  • MANIFEST_LOADED can fire again after network recovery. It re-fetches the document and replaces the cues without duplicating them.

Testing

  • Parser (parseAppleJsonChapters)
    • chapters end at the next entry's start-time, or at their own duration when present, the last one stays open
    • title language order, full tag and primary subtag matching, und case-insensitive, titles without a language
    • untitled entries are dropped but still end the previous chapter, the untitled [{ "start-time": 0 }] document yields no chapters
    • non-array documents and entries without a numeric start-time are ignored
  • Document URL (toChaptersSessionDataUrl)
    • URI preferred over VALUE, a VALUE that is a URL is used as a fallback
    • relative URIs resolve against the playlist URL, including after a redirect and with an empty response.url
    • missing or non-URL values return undefined instead of throwing
  • Fetch and apply (fetchAndApplyChaptersSessionData)
    • the document is requested once and feeds both muxmetadata and the chapters
    • applying it twice doesn't duplicate cues
    • a teardown while the document is loading, or right after it resolves, applies nothing
    • chapters added through addChapters() before the document arrives, or while the track is being created, are kept intact. Chapters added after it replace the stream chapters
    • an untitled document still applies its metadata
    • network error, non-ok response, invalid JSON and a malformed document apply nothing and don't throw
  • Open last chapter
    • ends at Infinity while the duration is unknown, zero or unbounded, and at the media duration once it is known, including activeChapter and the chapterchange detail
  • Call sites
    • native: updateStreamInfoFromSrc with a multivariant playlist carrying the session data, including a relative URI and a redirect
    • hls.js: MANIFEST_LOADED on a real setupHls instance, and through initialize() to cover the chapters track created on loadstart
  • playback-core, mux-video and mux-player suites green, lint and typecheck clean. chapters.test.js also passes in WebKit.
  • E2E in Chrome and WebKit, native and hls.js, against a staging asset with chapters: correct chapter ends, source changes, addChapters() precedence, and a multi-language document.

Notes

  • One chapters track for now.
  • For a titled asset without chapters, the document's single entry becomes one chapter titled with the asset title, same as v10.

Note

Medium Risk
Touches async playlist/chapter loading, text-track cue lifecycle, and public chapter read APIs; behavior is well covered by tests but races with teardown and hls.js manifest reloads warrant careful review.

Overview
Enables chapter UI from Mux’s com.apple.hls.chapters session data by parsing the same JSON document already fetched for Mux metadata and populating the existing chapters text track (native HLS and hls.js).

Adds parseAppleJsonChapters with timing from start-time, optional duration, or the next entry, localized titles via getPreferredLanguages (element/shadow lang, then navigator.languages), and drops untitled entries. fetchAndApplyChaptersSessionData resolves the session-data URL against the playlist response URL (redirects / relative URIs), fetches once, dispatches muxmetadata, and applies chapters through setSessionDataChapters. Playlist and chapter fetches honor AbortSignal tied to media teardown so source changes don’t leave stale chapters or metadata. hls.js loads chapters on MANIFEST_LOADED instead of MANIFEST_PARSED.

Chapter track behavior: stream chapters are tagged separately from addChapters() (user chapters always win; re-apply replaces stream cues without duplicating). getChapters / getActiveChapter / chapterchange clamp ends to media duration; open last chapter reports Infinity until duration is known. Fixes forceHiddenTracks so src-less chapter tracks aren’t reset (WebKit cue loss).

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

@javierarams javierarams self-assigned this Sep 24, 2026
@javierarams
javierarams requested a review from a team as a code owner September 24, 2026 14:44
@vercel

vercel Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
elements-demo-astro Ready Ready Preview Sep 29, 2026 3:03pm UTC
elements-demo-nextjs Ready Ready Preview Sep 29, 2026 3:03pm UTC
elements-demo-svelte-kit Ready Ready Preview Sep 29, 2026 3:03pm UTC
elements-demo-vanilla Ready Ready Preview Sep 29, 2026 3:03pm UTC
elements-demo-vue Ready Ready Preview Sep 29, 2026 3:03pm UTC

Request Review

@snyk-io

snyk-io Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@javierarams
javierarams requested a review from luwes September 24, 2026 14:45

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/playback-core/src/index.ts Outdated

@luwes luwes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on this. The session-vs-user chapter precedence (WeakSet), redirect-aware URL resolution on both paths, and single fetch feeding both chapters and muxmetadata all look good. A few things I noticed, roughly in priority order:

1. Open last chapter ends at Number.MAX_SAFE_INTEGER and leaks through the public API

Session chapters go through addCuesToTextTrack, which uses mediaEl.duration for a missing endTime only if it's already finite; otherwise it writes MAX_SAFE_INTEGER, and the cue is never amended. Both call sites (updateStreamInfoFromSrc for native, MANIFEST_LOADED for hls.js) run before metadata, so the sentinel is the common case, not the edge case. getChapters(), getActiveChapter(), and the chapterchange detail then report endTime: 9007199254740991.

User chapters had the same behavior, but only if addChapters was called before metadata. Now it happens by default whenever a stream ships chapters. Options: clamp to a finite duration in vttCueToChapter, or update the session cue's end on durationchange. For reference, Video.js 10 (videojs/v10#2737) keeps the sentinel on the track but clamps to the media duration on read and re-syncs on durationchange. It might also be worth double-checking that media-chrome's time range clamps cue ends to the duration (I haven't verified that).

2. Possible race with forceHiddenTracks on the hls.js path (unverified)

With initialize(), setupChapters creates an empty label="chapters" track on loadstart. On MANIFEST_LOADED, forceHiddenTracks sees a chapters track with no cues and, one tick later, does setAttribute('src', ''). Per the HTML spec, setting src empties the cue list and restarts the track's load. The chapters fetch kicks off on the same MANIFEST_LOADED, so a fast (e.g. cached) response could add cues before that reset, or before the restarted empty load fails. In v10 we measured Chromium and WebKit dropping cues added before a srcless track's load settles.

The MANIFEST_LOADED tests trigger the event directly without initialize(), so no chapters track exists yet and this path isn't covered. Probably rare in practice, but cheap to guard: e.g. skip tracks without a src in forceHiddenTracks, or wait for the track to settle (load/error) before adding cues.

3. Small teardown gap after the fetch resolves

fetchJsonUntilTeardown stops watching for teardown once the JSON resolves, but setSessionDataChapters → addCuesToTextTrack can still await a setTimeout(0) when it creates the track. A teardown or source change in that tick would add the old source's chapters to a track on the new source. Re-checking signal.aborted after the await (or threading the signal through) would close it.

Minor

  • playlistUrl ?? src: if response.url is '' (opaque or mocked responses), ?? won't fall back to src, and relative URIs silently resolve to undefined. || would handle it.
  • Only one title is used (und, else the first), so a document that lists es first shows Spanish chapters to everyone. Might be worth preferring the player's lang / navigator.language before falling back.
  • The native parser (getMultivariantPlaylistSessionData) keeps the last entry for a repeated DATA-ID, while picking the first entry with a URI would be more predictable for per-LANGUAGE playlists. Edge case.
  • MANIFEST_LOADED can fire again after network recovery, which re-fetches the JSON and re-dispatches muxmetadata. Harmless (cues are replaced, not duplicated), and the old MANIFEST_PARSED handler behaved the same way. Just flagging it.

Load session-data chapters once, per load, without overriding user chapters

Fetch the com.apple.hls.chapters document a single time for both the Mux metadata and the chapters, abort it on teardown, resolve relative URIs, skip untitled entries, and keep chapters added through addChapters() from being merged with or truncated by the stream's.
Native playback resolved a relative chapters URI against the original
src instead of the multivariant playlist's response URL, so it missed
redirected documents. Media playlists already used response.url for
this; chapters now do the same, and the hls.js call site listens on
MANIFEST_LOADED (which carries both sessionData and the response URL)
instead of MANIFEST_PARSED + hls.url, which never reflected redirects.
The open last chapter keeps MAX_SAFE_INTEGER on the track, but getChapters(),
getActiveChapter() and chapterchange now report the media duration once it is
finite and omit endTime until then.
Resetting src on the srcless chapters track emptied the cue list, so in WebKit
session chapters that resolved right after MANIFEST_LOADED were dropped.
…urrent

Keep watching teardown until the chapters are on the track, not just until the
JSON resolves. Callers that find a track still settling now wait for it, and the
user-chapters precedence check runs right before the cues are added, so
addChapters() during that tick is neither dropped nor merged.
…s empty

Synthetic responses have an empty url, which broke resolving relative chapters
and media playlist URIs on the native path.
Prefer the browser languages, then a lang set on the media element or its
shadow hosts (never one inherited from the page), matching the full tag before
the primary subtag, before falling back to und and then the first title.
Omitting endTime for an open chapter changed the public type of chapters and
activeChapter, which broke TypeScript consumers such as the Next.js demo. The
open chapter now ends at Infinity until the media duration is known and greater
than zero.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit af32d90. Configure here.

Comment thread packages/playback-core/src/index.ts
Comment thread packages/playback-core/src/text-tracks.ts
@javierarams
javierarams requested a review from luwes September 28, 2026 19:58

@luwes luwes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working through all of these. Every point from my first review is addressed, most with tests: the clamp when chapters are read, leaving srcless tracks alone in forceHiddenTracks, the signal carried through track creation (plus getOrCreateTextTrack stopping two callers from creating the track at once), the || fallback, and title language selection. Leaving "last entry wins" for a repeated DATA-ID as is seems fine to me.

A couple of things remain after the latest push:

1. Stale chapters after a source change on native playback (Bugbot's high-severity finding, confirmed)

updateStreamInfoFromSrc waits for the playlist fetch without watching for teardown, and untilTeardown only starts once fetchAndApplyChaptersSessionData runs. If the source changes while the playlist is still loading:

  1. emptied removes the old chapters track.
  2. The new source's loadstart creates a fresh one.
  3. The old playlist resolves and fetches the old chapters document, and nothing aborts that fetch.
  4. The old chapters land on the new track. They're marked as session chapters, so they can overwrite the new source's chapters, or be overwritten by them, depending on which fetch finishes first.

The hls.js path is fine, since the old instance is destroyed and sends no more events. A fix is to set up the teardown abort when updateStreamInfoFromSrc starts and pass its signal to both the playlist fetch and the chapters fetch. That would also fix an older bug on the same path: this function already sends a stale streamtypechange and writes liveEdgeStartOffset for the old source.

2. Infinity can't round-trip through addChapters() (Bugbot rated this low, I'd call it medium)

When the duration is unknown, zero, or infinite (live), getChapters(), getActiveChapter(), and the chapterchange detail now return endTime: Infinity. But addCuesToTrack passes any endTime straight to new VTTCue(...), which throws for a value that isn't finite. So addChapters(el, getChapters(el)) fails on live streams or before metadata loads, and Infinity becomes null if someone serializes the event detail to JSON.

Smallest fix: treat an endTime that isn't finite as missing in addCuesToTrack (Number.isFinite(cuePoint.endTime)), so the existing next-cue/duration fallback applies. Returning Infinity where these used to return MAX_SAFE_INTEGER is also a public API change, so it's worth a note in the changeset.

3. Question: language order

getPreferredLanguages puts navigator.languages ahead of the player's lang, and a test pins that order. A lang on <mux-player> / <mux-video> is an explicit author choice, though, and since every browser reports at least one language, it will usually lose. I'd expect the element's lang to come first. Is this order intentional?

Nit

With a preference of en-US and titles [en-GB, en], the primary-subtag fallback returns en-GB because it comes first. Preferring the bare primary tag (en) over a sibling region would be the better match.

Unverified

I haven't checked whether media-chrome's time range clamps cue ends to the duration. Its chapters state reads the track's cues directly, so on a live stream with session chapters the UI may still see the MAX_SAFE_INTEGER end.

I'd fix 1 before merging; 2 is a one-line guard.

updateStreamInfoFromSrc now watches teardown from the start and passes its
signal to the playlist and chapters fetches, so a source change while the
playlist loads no longer applies the old source's chapters, stream type or live
edge offset to the new one.
…s missing

getChapters() reports Infinity for an open chapter while the duration is not
finite, and WebKit's VTTCue rejects it (every engine rejects NaN), so feeding
those chapters back to addChapters() threw. Such an endTime now falls back to
the next cue or the media duration.
…guages

A lang set on the media element or its shadow hosts is an explicit author
choice, so it now comes before navigator.languages. A lang inherited from light
DOM ancestors or the document is still ignored.
For en-US with titles [en-GB, en], pick en: exact tag first, then the bare
primary tag, then any title with the same primary subtag.
@javierarams

Copy link
Copy Markdown
Contributor Author

Thanks for working through all of these. Every point from my first review is addressed, most with tests: the clamp when chapters are read, leaving srcless tracks alone in forceHiddenTracks, the signal carried through track creation (plus getOrCreateTextTrack stopping two callers from creating the track at once), the || fallback, and title language selection. Leaving "last entry wins" for a repeated DATA-ID as is seems fine to me.

A couple of things remain after the latest push:

1. Stale chapters after a source change on native playback (Bugbot's high-severity finding, confirmed)

updateStreamInfoFromSrc waits for the playlist fetch without watching for teardown, and untilTeardown only starts once fetchAndApplyChaptersSessionData runs. If the source changes while the playlist is still loading:

  1. emptied removes the old chapters track.
  2. The new source's loadstart creates a fresh one.
  3. The old playlist resolves and fetches the old chapters document, and nothing aborts that fetch.
  4. The old chapters land on the new track. They're marked as session chapters, so they can overwrite the new source's chapters, or be overwritten by them, depending on which fetch finishes first.

The hls.js path is fine, since the old instance is destroyed and sends no more events. A fix is to set up the teardown abort when updateStreamInfoFromSrc starts and pass its signal to both the playlist fetch and the chapters fetch. That would also fix an older bug on the same path: this function already sends a stale streamtypechange and writes liveEdgeStartOffset for the old source.

2. Infinity can't round-trip through addChapters() (Bugbot rated this low, I'd call it medium)

When the duration is unknown, zero, or infinite (live), getChapters(), getActiveChapter(), and the chapterchange detail now return endTime: Infinity. But addCuesToTrack passes any endTime straight to new VTTCue(...), which throws for a value that isn't finite. So addChapters(el, getChapters(el)) fails on live streams or before metadata loads, and Infinity becomes null if someone serializes the event detail to JSON.

Smallest fix: treat an endTime that isn't finite as missing in addCuesToTrack (Number.isFinite(cuePoint.endTime)), so the existing next-cue/duration fallback applies. Returning Infinity where these used to return MAX_SAFE_INTEGER is also a public API change, so it's worth a note in the changeset.

3. Question: language order

getPreferredLanguages puts navigator.languages ahead of the player's lang, and a test pins that order. A lang on <mux-player> / <mux-video> is an explicit author choice, though, and since every browser reports at least one language, it will usually lose. I'd expect the element's lang to come first. Is this order intentional?

Nit

With a preference of en-US and titles [en-GB, en], the primary-subtag fallback returns en-GB because it comes first. Preferring the bare primary tag (en) over a sibling region would be the better match.

Unverified

I haven't checked whether media-chrome's time range clamps cue ends to the duration. Its chapters state reads the track's cues directly, so on a live stream with session chapters the UI may still see the MAX_SAFE_INTEGER end.

I'd fix 1 before merging; 2 is a one-line guard.

Thanks again. All points are fixed in the latest push.

  1. Stale chapters on native. updateStreamInfoFromSrc now listens for teardown from the start and passes the signal to the multivariant, media playlist and chapters fetches. After a teardown it no longer writes liveEdgeStartOffset, targetLiveWindow or the stream type, and it does not start the seekable poll. So the old streamtypechange bug is fixed too. There are regression tests for a teardown during the playlist load and during the chapters load.
  2. Infinity round trip. addCuesToTrack now treats an endTime that is not finite as missing, so the next cue or the duration is used. addChapters(el, getChapters(el)) works on live and before metadata. WebKit throws for Infinity in VTTCue and both engines throw for NaN, so there is a test for that too.
  3. Language order. You are right. The player's own lang now comes first, then navigator.languages, then und, then the first title. A lang inherited from the page is still ignored.
  4. Primary tag. With en-US and titles [en-GB, en] it now picks en.

About media-chrome: its time range reads the cues from the track, but calcRangeValueFromTime clamps the value to the end of the bar and runs again when the duration changes. So the last segment does not go past the end.

Comment thread packages/playback-core/src/index.ts

@luwes luwes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@javierarams
javierarams merged commit d1de13a into main Sep 29, 2026
16 checks passed
@javierarams
javierarams deleted the core-json-chapters branch September 29, 2026 19:29
@github-actions github-actions Bot mentioned this pull request Sep 29, 2026
spuppo-mux pushed a commit that referenced this pull request Oct 5, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>@mux/mux-audio: 0.16.5</summary>

##
[0.16.5](https://github.com/muxinc/elements/compare/@mux/mux-audio@0.16.4...@mux/mux-audio@0.16.5)
(2026-10-02)


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-audio-react: 0.16.5</summary>

##
[0.16.5](https://github.com/muxinc/elements/compare/@mux/mux-audio-react@0.16.4...@mux/mux-audio-react@0.16.5)
(2026-10-02)


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/playback-core: 0.36.0</summary>

##
[0.36.0](https://github.com/muxinc/elements/compare/@mux/playback-core@0.35.4...@mux/playback-core@0.36.0)
(2026-10-02)


### Features

* **playback-core:** Support apple JSON chapters from
com.apple.hls.chapters session data
([#1360](#1360))
([d1de13a](d1de13a))


### Bug Fixes

* bump mux-embed from 5.16.1 to 5.18.1
([#1323](#1323))
([a440f55](a440f55))
</details>

<details><summary>@mux/mux-player: 3.14.0</summary>

##
[3.14.0](https://github.com/muxinc/elements/compare/@mux/mux-player@3.13.4...@mux/mux-player@3.14.0)
(2026-10-02)


### Bug Fixes

* bump the prod-dependencies group across 1 directory with 2 updates
([#1336](#1336))
([9176ea2](9176ea2))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/mux-video bumped from 0.31.4 to 0.31.5
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-player-astro: 3.14.0</summary>

##
[3.14.0](https://github.com/muxinc/elements/compare/@mux/mux-player-astro@3.13.4...@mux/mux-player-astro@3.14.0)
(2026-10-02)


### Features

* **mux-player-astro, mux-uploader-astro:** support astro v7
([#1361](#1361))
([9002d41](9002d41))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/mux-player bumped from 3.13.4 to 3.14.0
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-player-react: 3.14.0</summary>

##
[3.14.0](https://github.com/muxinc/elements/compare/@mux/mux-player-react@3.13.4...@mux/mux-player-react@3.14.0)
(2026-10-02)


### Miscellaneous Chores

* **@mux/mux-player-react:** Synchronize player versions


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/mux-player bumped from 3.13.4 to 3.14.0
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-video: 0.31.5</summary>

##
[0.31.5](https://github.com/muxinc/elements/compare/@mux/mux-video@0.31.4...@mux/mux-video@0.31.5)
(2026-10-02)


### Bug Fixes

* bump @mux/mux-data-google-ima from 0.3.4 to 0.3.17
([#1324](#1324))
([5c96dac](5c96dac))
* bump the prod-dependencies group across 1 directory with 2 updates
([#1336](#1336))
([9176ea2](9176ea2))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-video-react: 0.31.5</summary>

##
[0.31.5](https://github.com/muxinc/elements/compare/@mux/mux-video-react@0.31.4...@mux/mux-video-react@0.31.5)
(2026-10-02)


### Miscellaneous Chores

* **@mux/mux-video-react:** Synchronize video versions


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/playback-core bumped from 0.35.4 to 0.36.0
</details>

<details><summary>@mux/mux-uploader: 1.6.0</summary>

##
[1.6.0](https://github.com/muxinc/elements/compare/@mux/mux-uploader@1.5.0...@mux/mux-uploader@1.6.0)
(2026-10-02)


### Miscellaneous Chores

* **@mux/mux-uploader:** Synchronize uploader versions
</details>

<details><summary>@mux/mux-uploader-astro: 1.6.0</summary>

##
[1.6.0](https://github.com/muxinc/elements/compare/@mux/mux-uploader-astro@1.5.0...@mux/mux-uploader-astro@1.6.0)
(2026-10-02)


### Features

* **mux-player-astro, mux-uploader-astro:** support astro v7
([#1361](#1361))
([9002d41](9002d41))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/mux-uploader bumped from 1.5.0 to 1.6.0
</details>

<details><summary>@mux/mux-uploader-react: 1.6.0</summary>

##
[1.6.0](https://github.com/muxinc/elements/compare/@mux/mux-uploader-react@1.5.0...@mux/mux-uploader-react@1.6.0)
(2026-10-02)


### Miscellaneous Chores

* **@mux/mux-uploader-react:** Synchronize uploader versions


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @mux/mux-uploader bumped from 1.5.0 to 1.6.0
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Releases playback-core chapter handling and mux-embed/IMA dependency
updates across all consumer packages; Astro peer range expansion may
affect install resolution for Astro apps.
> 
> **Overview**
> This is a **Release Please** cut that bumps published versions across
the monorepo and aligns workspace dependencies (notably
**`@mux/playback-core` → 0.36.0**), plus updates the release manifest,
`package-lock.json`, and per-package changelogs.
> 
> **`@mux/playback-core` 0.36.0** is the main functional release: Apple
JSON chapters from HLS session data (`com.apple.hls.chapters`) and an
updated **mux-embed** dependency. Player, video, and audio packages pick
that up via version bumps (**`@mux/mux-player` 3.14.0**,
**`@mux/mux-video` 0.31.5**, **`@mux/mux-audio` 0.16.5**, and matching
React wrappers).
> 
> **`@mux/mux-player-astro` 3.14.0** and **`@mux/mux-uploader-astro`
1.6.0** document **Astro v7** peer support. **`@mux/mux-video` 0.31.5**
also notes dependency bumps (including **`@mux/mux-data-google-ima`**).
Uploader packages move to **1.6.0** as a synchronized release.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6371102. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

This branch was successfully deployed

5 active deployments
Preview – elements-demo-svelte-kit — e67a3df8 Deployed Sep 29, 2026 by vercel[bot]
Preview – elements-demo-vanilla — e67a3df8 Deployed Sep 29, 2026 by vercel[bot]
Preview – elements-demo-vue — e67a3df8 Deployed Sep 29, 2026 by vercel[bot]
Preview – elements-demo-nextjs — e67a3df8 Deployed Sep 29, 2026 by vercel[bot]
Preview – elements-demo-astro — e67a3df8 Deployed Sep 29, 2026 by vercel[bot]
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 Request: Support apple json chapters from ext-x-session-data.

3 participants