Repository navigation
feat(infra): customisable embed, player events and a quality menu in Safari - #4190
Merged
Merged
Conversation
?rendition=720p serves a master that lists only that rendition, with its audio, so Safari's own HLS player can be switched to it. ?max=720 keeps renditions no taller than 720. Malformed or unknown values leave the master as it is.
The player reads the renditions from the master playlist once metadata is in, and a pick loads the single-rendition master, keeping the time and play state. Auto goes back to the full master.
Events ready, play, pause, ended, timeupdate (about four a second), qualitychange and error go to the embedding page. Commands play, pause, seek, mute, unmute and setQuality are taken only from that page's window and only in validated shapes. Adds keyboard and full screen switches.
… buttons color, radius, quality, quality_menu, speed_menu, pip, fullscreen, poster and keyboard are read strictly from the embed URL; anything malformed is ignored and never reaches the markup.
Accent colour, controls, autoplay muted, captions and the quality menu update the snippet as they change.
richiemcilroy
force-pushed
the
feat/infra-embed-customisation
branch
from
November 13, 2026 08:02
0ab5570 to
27784a0
Compare
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
richiemcilroy
marked this pull request as ready for review
November 13, 2026 08:03
|
Sandchest reviewed Sandchest reviews every change for vulnerabilities an attacker could use · 6 credits · Review details |
…ight on an uneven ladder A constant quality ladder can give a taller rendition less bandwidth, so the cap, the fallback and the menu order are tested against one.
The console options change only the embed URL. The new docs examples use allow="autoplay" with allowfullscreen.
… feat/infra-embed-customisation
quality_menu, speed_menu, pip and fullscreen are read with flag() like the other switches; the page turns them into the player's hidden controls.
Member
Author
|
@greptileai review |
…ing a quality switch wins
Member
Author
|
@greptileai review |
…omisation # Conflicts: # apps/web/lib/infra-video-view.ts
…eld place and seek
Member
Author
|
@greptileai review |
…only a playing embed
Member
Author
|
@greptileai review |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Built on #4171 and #4187, both now on main; main (including #4189's snippet) is merged in.
What changes
The hosted embed (
/dev/<playbackId>/embed) can now be styled and driven from the page that embeds it, and Safari on iPhone and iPad gets the same quality menu as every other browser.Embed options, read strictly from the embed URL. A malformed value is ignored and never reaches the markup or the CSS.
color=3b82f6#optional)radius=12quality=720quality_menu=0,speed_menu=0,pip=0,fullscreen=0fullscreen=0also turns off double click andf)poster=0keyboard=0Each is one field of
DeveloperVideoPlayback, read indeveloperVideoPlaybackwith the shared helpers.The player shows no title, so there is no
titleparameter.Player events over
postMessage. The embed posts{ type: "cap-player", event, currentTime, duration, paused, muted }to its parent forready,play,pause,ended,timeupdate(at most about four a second),qualitychange(quality,auto) anderror(code). It takesplay,pause,seek(time),mute,unmuteandsetQuality(quality) only from its parent window, and only in validated shapes. Aplay,seekorsetQualitysent onready, before the source, metadata or renditions are ready, is held and applied when they are. Events carry no URLs. An embed with nothing to play (taken down, failed, or a paused app) shows its usual neutral page whatever the parameters and sendserrorwithcode: "unavailable"or"paused".Quality menu on native HLS. iPhone and iPad play HLS natively, which offers no way to pick a rendition. The player now reads the renditions from the master playlist once metadata is in and shows the usual menu. A pick loads
master.m3u8?rendition=720p, a master that lists only that rendition, keeping the time and play state; Auto goes back to the full master. A rendition's own playlist could not be used: the audio is a separate rendition, so it would play silent. The playback route serves the narrowed master (and?max=720for the quality cap); an unknown or malformed value leaves the master as it was. Renditions are capped, picked and ordered by height, never by bandwidth: the ladder is constant quality, so a taller rendition can need less (tested with such a ladder). The cap wins over a pick.Console. The embed code on a video's page has toggles for accent colour, controls, autoplay muted, captions (when the video has them) and the quality menu, and the snippet updates as they change. Only the
srcchanges; the iframe's attributes stay as #4189 sets them.Docs. "Embed Options" and "Player Events" sections next to Embed Parameters in the REST API docs, with an example of each.
What does not change
/s/...) and their embed do not use any of these files.CapPlayeris used only by the Infra dev pages, the Infra console and the Infra landing film; the new props default to the current behaviour.Proof
Two test videos uploaded to a test app (1080p with sound, and a silent one for the iOS Simulator), embedded cross-origin from a local page on a Mac. The embed page was the real
DeveloperVideoViewbundled from this branch, with playback proxied to the live playback URLs through this branch's playlist code. Deleted afterwards.ready, quality menu and cap, taken-down embed; 342x192 touch with captions (44 px targets, caption lift); 280/320/400 px bars, mouse and touchsetQualityfrom a cross-origin pageScreenshots: a customised embed (orange accent, 16 px corners, speed, picture in picture and full screen hidden) in Chrome, Edge, Firefox, WebKit and Safari; the quality menu in Chrome, Firefox, Safari (hls.js) and Safari native HLS; the native menu and 360p on iPhone; a 342 px phone column; a 280 px bar.
Bytes before play. The player component adds about 1.56 KB gzip over main (esbuild, full minify). Measured earlier on real Next builds of
/infra, which loads the same player: the player chunk grew 1,301 B and global CSS 54 B, every other chunk byte-identical.Tests
error.