Skip to content

fix(vimeo-video-element): keep paused in sync when pause() is called before load - #268

Merged
spuppo-mux merged 5 commits into
muxinc:mainfrom
spuppo-mux:fix/vimeo-pause-before-load
Nov 9, 2026
Merged

spuppo-mux merged 5 commits into
muxinc:mainfrom
spuppo-mux:fix/vimeo-pause-before-load

Conversation

@spuppo-mux

@spuppo-mux spuppo-mux commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #267.

Calling pause() while play() was still waiting on loadComplete left el.paused === false for good. play() sets the state and dispatches play synchronously, but pause() relied on Vimeo's pause event. Both commands reach Vimeo back to back, playback never starts, so Vimeo never emits it. Consumers that only call play() when paused is true (react-player, media-chrome's play button) then never play the video again.

  • Synchronous pause. pause() now updates paused and dispatches pause before awaiting load, mirroring play().
  • Shared state helpers. #onPlay, #onPlaying, #onPaused and #onEnded handle every paused change, from both the Vimeo listeners and play()/pause(). #onPlay and #onPaused only act on an actual state change, so a regular pause still fires a single pause event.

Behavior change: play() on an element that's already playing no longer fires a redundant play event, matching HTMLMediaElement.

Testing

  • New regression test for play() then pause() before load: fails 4 of 5 assertions on main, passes with the fix.
  • Repaired the local suite, which was broken on main: the test importmap was missing media-played-ranges-mixin, and both fixture videos had been deleted from Vimeo. Fixtures now use Vimeo's 76979871; play promise and the src-change test use 22439234, because 76979871 always fails playback with a PlaybackError.
  • 21/21 pass locally in Chrome with npx wet test ./test/index.html --servedir .. The package's wet run script is a no-op, so CI doesn't run these. Lint passes.

Note

Medium Risk
Changes synchronous media event semantics and play/pause gating in a custom element used by players like react-player; behavior is intentional but affects timing-sensitive integrations.

Overview
Fixes paused getting stuck false when play() then pause() happen before loadComplete: Vimeo may never emit pause, so consumers that gate on paused stop working.

pause() now updates internal state and dispatches pause synchronously (matching play()), then awaits load and calls the Vimeo API. Play/pause/playing/ended transitions go through #onPlay, #onPaused, #onPlaying, and #onEnded, with guards so duplicate events are not fired. play() on an already-playing element no longer emits a redundant play event (closer to HTMLMediaElement).

Tests add regressions for pre-load pause and early play() after src, fix the test importmap for media-played-ranges-mixin, and swap fixture Vimeo IDs/durations after removed or unplayable videos.

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

@spuppo-mux
spuppo-mux requested a review from luwes October 21, 2026 14:53
@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown

@spuppo-mux is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

@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 on lines -264 to -280
#onLoaded = async () => {
this.#readyState = 1; // HTMLMediaElement.HAVE_METADATA
this.dispatchEvent(new Event('loadedmetadata'));

if (this.api) {
this.#muted = await this.api.getMuted();
this.#volume = await this.api.getVolume();
this.dispatchEvent(new Event('volumechange'));

this.#duration = await this.api.getDuration();
this.dispatchEvent(new Event('durationchange'));
}

this.dispatchEvent(new Event('loadcomplete'));
this.loadComplete.resolve();
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

note: this was just moved

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

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 2ae1bb6. Configure here.

@decepulis decepulis 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.

Seems reasonable!

@vercel

vercel Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
media-elements-nextjs Ready Ready Preview Nov 9, 2026 9:44pm UTC

Request Review

@spuppo-mux
spuppo-mux merged commit 72a3a62 into muxinc:main Nov 9, 2026
5 checks passed
@github-actions github-actions Bot mentioned this pull request Nov 9, 2026

This branch was successfully deployed

1 active deployment
Preview — 4b9bdce4 Deployed Nov 9, 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.

vimeo-video-element: paused stays false forever when pause() is called before load completes

2 participants