Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/vimeo-video-element/test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<script type="importmap">
{
"imports": {
"media-played-ranges-mixin": "https://cdn.jsdelivr.net/npm/media-played-ranges-mixin@0.1.0/+esm",
"zora": "https://cdn.jsdelivr.net/npm/zora@6.0.0/dist/index.js",
"@vimeo/player/": "https://cdn.jsdelivr.net/npm/@vimeo/player/"
}
Expand Down
33 changes: 27 additions & 6 deletions packages/vimeo-video-element/test/test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { test } from 'zora';

function createVideoElement() {
function createVideoElement(src = 'https://vimeo.com/76979871') {
return fixture(`<vimeo-video
src="https://vimeo.com/648359100"
src="${src}"
muted
></vimeo-video>`);
}
Expand Down Expand Up @@ -48,7 +48,7 @@ test('duration', async function (t) {
await promisify(video.addEventListener.bind(video))('durationchange');
}

t.equal(Math.round(video.duration), 115, `is 115s long`);
t.equal(Math.round(video.duration), 62, `is 62s long`);
});

test('load promise', async function (t) {
Expand All @@ -57,7 +57,7 @@ test('load promise', async function (t) {

const loadComplete = video.loadComplete;

video.src = 'https://vimeo.com/638371504';
video.src = 'https://vimeo.com/22439234';
await video.loadComplete;

t.ok(
Expand All @@ -69,11 +69,12 @@ test('load promise', async function (t) {
await promisify(video.addEventListener.bind(video))('durationchange');
}

t.equal(Math.round(video.duration), 90, `is 90s long`);
t.equal(Math.round(video.duration), 185, `is 185s long`);
});

test('play promise', async function (t) {
const video = await createVideoElement();
// 76979871 fails to play with a Vimeo PlaybackError, so use a playable video.
const video = await createVideoElement('https://vimeo.com/22439234');
await video.loadComplete;

video.muted = true;
Expand All @@ -86,6 +87,26 @@ test('play promise', async function (t) {
t.ok(!video.paused, 'is playing after video.play()');
});

test('pause() before load completes keeps paused in sync', async function (t) {
const video = await createVideoElement();

let pauseEvents = 0;
video.addEventListener('pause', () => pauseEvents++);

video.play().catch(() => {});
video.pause();

t.equal(video.paused, true, 'is paused synchronously after pause()');
t.equal(pauseEvents, 1, 'dispatches a pause event');

await video.loadComplete;
await delay(1000);

t.equal(video.paused, true, 'is still paused after load');
t.equal(await video.api.getPaused(), true, 'matches the Vimeo player state');
t.equal(pauseEvents, 1, 'does not dispatch a duplicate pause event');
});
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

test('dispatches an error event when the video does not exist', async function (t) {
const video = await fixture(`<vimeo-video
src="https://vimeo.com/585758234/1ef30fd6da"
Expand Down
97 changes: 54 additions & 43 deletions packages/vimeo-video-element/vimeo-video-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,6 @@ class VimeoVideoElement extends MediaPlayedRangesMixin(globalThis.HTMLElement ??
super.disconnectedCallback?.();
}

#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();
};

Comment on lines -264 to -280

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

#setupApiListeners() {
const textTracksVideo = document.createElement('video');
this.textTracks = textTracksVideo.textTracks;
Expand Down Expand Up @@ -310,24 +293,13 @@ class VimeoVideoElement extends MediaPlayedRangesMixin(globalThis.HTMLElement ??
// Make sure a `play` event is fired before the bufferstart event.
// For example Vimeo's `play` event is delayed decreasing video startup time.
this.api.on('bufferstart', () => {
if (this.#paused) {
this.#paused = false;
this.dispatchEvent(new Event('play'));
}
this.#onPlay();
this.dispatchEvent(new Event('waiting'));
});

this.api.on('play', () => {
if (!this.#paused) return;
this.#paused = false;
this.dispatchEvent(new Event('play'));
});
this.api.on('play', () => this.#onPlay());

this.api.on('playing', () => {
this.#readyState = 3; // HTMLMediaElement.HAVE_FUTURE_DATA
this.#paused = false;
this.dispatchEvent(new Event('playing'));
});
this.api.on('playing', () => this.#onPlaying());

this.api.on('seeking', () => {
this.#seeking = true;
Expand All @@ -341,15 +313,9 @@ class VimeoVideoElement extends MediaPlayedRangesMixin(globalThis.HTMLElement ??
this.dispatchEvent(new Event('seeked'));
});

this.api.on('pause', () => {
this.#paused = true;
this.dispatchEvent(new Event('pause'));
});
this.api.on('pause', () => this.#onPaused());

this.api.on('ended', () => {
this.#paused = true;
this.dispatchEvent(new Event('ended'));
});
this.api.on('ended', () => this.#onEnded());

this.api.on('ratechange', ({ playbackRate }) => {
this.#playbackRate = playbackRate;
Expand Down Expand Up @@ -412,22 +378,27 @@ class VimeoVideoElement extends MediaPlayedRangesMixin(globalThis.HTMLElement ??
}
}

/*
* Update state synchronously. If this is called before
* load completes, Vimeo never starts playback and won't emit the respective event.
*/
async play() {
this.#paused = false;
this.dispatchEvent(new Event('play'));
this.#onPlay();

await this.loadComplete;

try {
await this.api?.play();
} catch (error) {
this.#paused = true;
this.dispatchEvent(new Event('pause'));
this.#onPaused();
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
throw error;
}
}

async pause() {
// Update state synchronously. See play().
this.#onPaused();

await this.loadComplete;
return this.api?.pause();
}
Expand Down Expand Up @@ -582,6 +553,46 @@ class VimeoVideoElement extends MediaPlayedRangesMixin(globalThis.HTMLElement ??
});
}

#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();
};

#onPlay() {
if (!this.#paused) return;
this.#paused = false;
this.dispatchEvent(new Event('play'));
}

#onPlaying() {
this.#readyState = 3; // HTMLMediaElement.HAVE_FUTURE_DATA
this.#paused = false;
this.dispatchEvent(new Event('playing'));
}

#onPaused() {
if (this.#paused) return;
this.#paused = true;
this.dispatchEvent(new Event('pause'));
}

#onEnded() {
this.#paused = true;
this.dispatchEvent(new Event('ended'));
}

// This is a pattern to update property values that are set before
// the custom element is upgraded.
// https://web.dev/custom-elements-best-practices/#make-properties-lazy
Expand Down
Loading