Skip to content

feat(core,html,react): add YouTube provider, refactor IframeMediaHost and fix stale TextTrackList for iframe providers#1573

Closed
ronald-urbina wants to merge 2 commits into
videojs:feat/vimeo-providerfrom
ronald-urbina:feat/youtube-provider
Closed

feat(core,html,react): add YouTube provider, refactor IframeMediaHost and fix stale TextTrackList for iframe providers#1573
ronald-urbina wants to merge 2 commits into
videojs:feat/vimeo-providerfrom
ronald-urbina:feat/youtube-provider

Conversation

@ronald-urbina

Copy link
Copy Markdown
Collaborator

Summary

Adds a full YouTube embed provider — YouTubeMedia (core), YouTubeVideo custom element (html), and YoutubeVideo React component (react) — and extracts a shared IframeMediaHost base class that both YouTube and Vimeo now extend.

Also fixes a bug where iframe providers created a new TextTrackList on every src change, leaving stale listeners on the old list.


What's included

New: YouTube provider

  • packages/core/src/dom/media/youtube/index.tsYouTubeMedia: full MediaEngineHost implementation over the YouTube IFrame Player API
    • Parses video IDs, watch URLs, Shorts URLs, playlist URLs, and live stream URLs
    • CSS crop trick to hide YouTube title/branding bars when native controls are disabled
    • Polling-based timeupdate (250 ms) — the YouTube SDK doesn't emit these natively
    • Seek synthesis with 600 ms fallback for buffered positions
    • Synthetic caption tracks via getOption('captions', 'tracklist')
    • YouTube error code → MediaError mapping
    • Supported props: src, autoplay, controls, loop, muted, playsinline, nocookie, start
  • packages/core/src/dom/media/youtube/api.ts — lightweight IFrame API loader with module-level caching and multi-player callback chaining
  • packages/html/src/media/youtube-video/YouTubeVideo custom element (Shadow DOM, observed attributes)
  • packages/react/src/media/youtube-video/YoutubeVideo React component (useMediaInstance, useSyncProps, useComposedRefs)
  • packages/core/src/dom/media/youtube/tests/youtube-media.test.ts — ~690-line test suite

Refactor: shared IframeMediaHost

  • packages/core/src/dom/media/iframe-host.ts — abstract base class extracted from the Vimeo provider
    • Manages playback state cache (paused, ended, duration, currentTime, buffered, readyState, volume, muted, playbackRate, seeking, pip, error)
    • Overlay div pattern to prevent iframe pointer events from blocking controls
    • PiP support via requestPictureInPicture postMessage for Safari
    • Synthetic TextTrackList management
    • Lifecycle: attach / detach / destroy
  • packages/core/src/dom/media/vimeo/index.ts — simplified ~320 lines by extending IframeMediaHost
  • packages/html/src/media/vimeo-video/ — new HTML custom element for Vimeo (parallel to YouTube)

Bug fix: stale TextTrackList for iframe providers

  • packages/core/src/dom/store/features/text-track.ts — re-subscribes to track list events (addtrack, removetrack, change) on every loadstart, using an AbortController to clean up the previous subscription before attaching to the new list

Sandbox / demos

  • html-youtube-video and react-youtube-video sandbox templates
  • html-vimeo-video and react-vimeo-video sandbox templates (Vimeo was missing React)
  • Navbar and source picker updated to include YouTube entries

Architecture notes

  • Embed-only props (autoplay, controls, loop, nocookie, start) require an iframe remount on change; runtime props (muted, volume, playbackRate) delegate to the SDK directly
  • YouTube's loop param requires playlist to be set to the same video ID — handled automatically
  • nocookie defaults to false (opt-in) so URLs stay consistent with what users provide
  • The CSS crop technique offsets and clips the iframe to hide YouTube's title bar and bottom branding without stretching

@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

@ronald-urbina is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment May 20, 2026 9:49pm

Request Review

@sampotts sampotts left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I realized it's draft after diving in so just a few minor comments. Cheers!

'simple-hls-video': 'Simple HLS Video',
'dash-video': 'DASH Video',
'vimeo-video': 'Vimeo Video',
'youtube-video': 'Youtube Video',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: YouTube Video

},
// YouTube embed element
{
'[&_.media-youtube]:block [&_.media-youtube]:w-full [&_.media-youtube]:h-full [&_.media-youtube]:overflow-hidden [&_.media-youtube]:rounded-(--media-video-border-radius)':

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we not apply the classNames directly onto the component (it looks like you're passing className down to the rendered <div> where .media-youtube is) and share it with the Vimeo media? Maybe an embeds.ts - I'll leave naming up to you! It just feels a little odd to use class names like .media-youtube in the Tailwind styles and target them this way.

},
// YouTube embed element
{
'[&_.media-youtube]:block [&_.media-youtube]:w-full [&_.media-youtube]:h-full [&_.media-youtube]:overflow-hidden [&_.media-youtube]:rounded-(--media-video-border-radius)':

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As above.

iframe.style.border = 'none';

if (!this.#controls) {
// CSS crop trick — hides the YouTube title bar (top) and branding bar (bottom)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are we sure we're not breaching YouTube terms of service by doing this? We'll want to check we're not causing issues for users with this. I got a cease and desist from YouTube for hiding their chrome. I appreciate it doesn't look as nice though.

@sampotts

Copy link
Copy Markdown
Collaborator

I also built the sandbox and noticed a few things:

  • The styling doesn't seem to work - I think we're missing import '@app/styles.css'; in the main.tsx/main.ts for the sandboxes. I had to hack in some styles to get it working.
  • PIP doesn't work (tested on Safari 26.5)
  • Toggling captions shows a message in the frame related to settings.
  • There's a bit of a clash between our input feedback and the YouTube central controls. Perhaps we need to hide our play/pause feedback?
  • Related to the above but hotkeys seem to flow to YouTube but gestures use ours so you end up with a UI mismatch. Should we disable the YouTube hotkeys and rely on ours only?

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.

4 participants