Skip to content

feat(core): add vimeo media layer and html/react components#1620

Closed
luwes wants to merge 7 commits into
refactor/media-layer-architecturefrom
feat/vimeo-media
Closed

feat(core): add vimeo media layer and html/react components#1620
luwes wants to merge 7 commits into
refactor/media-layer-architecturefrom
feat/vimeo-media

Conversation

@luwes

@luwes luwes commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Fix #1435
Related #1538

Summary

Adds Vimeo playback on the media-layer architecture: a VimeoMedia host in core, HTML and React VimeoVideo components, shared mediaPlayedRanges for buffered/played ranges on custom media, and sandbox demos.

Changes

  • VimeoMedia wraps @vimeo/player with an HTMLMediaElement-like surface (src parsing, load, PiP, fullscreen, embed config)
  • mediaPlayedRanges helper for custom elements that lack native TimeRanges
  • CustomMediaElement gains iframe template support for Vimeo embeds
  • VimeoVideo web component and React wrapper with sandbox HTML/React templates

Testing

  • pnpm -F @videojs/core test src/dom/media/vimeo
  • pnpm -F @videojs/core test src/dom/media/media-played-ranges
  • Sandbox: open HTML and React Vimeo video demos

Made with Cursor


Note

Medium Risk
Touches shared CustomMediaElement lifecycle (reconnect, iframe path) used by other custom media; new third-party @vimeo/player dependency and large embed surface area, mitigated by unit tests.

Overview
Adds Vimeo as a first-class media source on the v10 media-layer stack, with sandbox demos and shared infrastructure for iframe embeds.

Core: New VimeoMedia host wraps @vimeo/player, maps Vimeo events to standard media APIs (play/pause, time, volume, PiP, fullscreen, text tracks), parses ids/URLs (including live events and unlisted h hashes), and builds embed URLs via buildVimeoIframeSrc. Adds mediaPlayedRanges so iframe hosts expose a played TimeRanges-like surface. CustomMediaElement is extended for iframe embeds: lazy/recreatable media host, property upgrade before define, iframe config hydration from data-config, and no track/source sync on iframe.

Packages: HTML vimeo-video web component and React VimeoVideo attach VimeoMedia to an iframe; escapeHtml secures iframe src in templates. @vimeo/player is a new @videojs/core dependency; build entries include vimeo and media-played-ranges.

Sandbox: New vimeo-video preset with fixed VIMEO_VIDEO_SRC, HTML/React templates, routing, and UI rules (no Tailwind skin, source picker disabled).

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

@vercel

vercel Bot commented May 28, 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 Jun 3, 2026 6:27pm

Request Review

Comment thread packages/core/src/dom/media/vimeo/index.ts
Comment thread packages/core/src/dom/media/vimeo/index.ts
await this.#loadComplete;
await this.#player?.exitFullscreen?.();
this.#isFullscreen = false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fullscreen state set unconditionally after optional chain

Low Severity

In requestFullscreen and exitFullscreen, #isFullscreen is unconditionally set after awaiting #loadComplete. If #player becomes null between #loadComplete resolving and execution reaching the assignment (e.g., concurrent destroy() call), the optional chain this.#player?.requestFullscreen?.() evaluates to undefined, the await resolves immediately, and the state flag is incorrectly toggled without an actual fullscreen transition. The PiP methods use try/catch which partially mitigates this, but the fullscreen methods lack any guard.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f6b941. Configure here.

Comment thread packages/core/src/dom/media/vimeo/index.ts

player.on('fullscreenchange', ({ fullscreen }) => {
this.#isFullscreen = fullscreen;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing fullscreenchange event dispatch in Vimeo handler

Medium Severity

The fullscreenchange handler from the Vimeo player updates #isFullscreen but never dispatches a fullscreenchange event on the VimeoMedia EventTarget. This is inconsistent with the enterpictureinpicture and leavepictureinpicture handlers on lines 589–597, which both call this.dispatchEvent(...). Without the event dispatch, the player UI and any external listeners won't be notified when fullscreen state changes via Vimeo's own controls.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3cd8326. Configure here.

Comment thread packages/core/src/dom/media/vimeo/index.ts
Comment thread packages/core/src/dom/media/media-played-ranges/index.ts
@luwes luwes force-pushed the refactor/media-layer-architecture branch from 6a4244c to 4ed3564 Compare June 3, 2026 18:10
luwes and others added 7 commits June 3, 2026 11:16

@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 and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 02ceb05. Configure here.

if (!loadOptions) return;

// Vimeo dispatches an `error` event separately on failure.
await this.#player.loadVideo(loadOptions).catch(() => {});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

loadComplete never settles on failure

High Severity

VimeoMedia gates play(), fullscreen, and PiP on await this.#loadComplete, but that promise is only resolved in #onLoaded. A player error, a failed loadVideo, or load() after resetting the promise when toLoadVideoOptions returns null leaves #loadComplete pending forever, so callers can hang indefinitely.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 02ceb05. Configure here.

@luwes luwes closed this Jun 4, 2026
@videojs videojs deleted a comment from tk7128762-coder Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant