This repository has been archived by the owner on May 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jwplayer-video-element.js
135 lines (114 loc) · 3.61 KB
/
jwplayer-video-element.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { SuperVideoElement } from 'super-media-element';
const templateLightDOM = globalThis.document?.createElement('template');
if (templateLightDOM) {
templateLightDOM.innerHTML = /*html*/`
<style class="jw-style">
.jw-no-controls [class*="jw-controls"],
.jw-no-controls .jw-title {
display: none !important;
}
</style>
<div class="jwplayer"></div>
`;
}
const templateShadowDOM = globalThis.document?.createElement('template');
if (templateShadowDOM) {
templateShadowDOM.innerHTML = /*html*/`
<style>
:host {
display: inline-block;
min-width: 300px;
min-height: 150px;
position: relative;
}
::slotted(.jwplayer) {
position: absolute !important;
width: 100%;
height: 100%;
}
</style>
<slot></slot>
`;
}
class JWPlayerVideoElement extends SuperVideoElement {
static template = templateShadowDOM;
static skipAttributes = ['src'];
get nativeEl() {
return this.querySelector('.jw-video');
}
async load() {
// Cleanup the previous video container.
this.querySelector('.jw-style')?.remove();
this.querySelector('.jwplayer')?.remove();
if (!this.src) {
return;
}
this.loadComplete.then(() => {
this.volume = 1;
});
// e.g. https://cdn.jwplayer.com/players/C8YE48zj-IxzuqJ4M.html
const MATCH_SRC = /jwplayer\.com\/players\/(\w+)(?:-(\w+))?/i;
const [, videoId, playerId] = this.src.match(MATCH_SRC);
const mediaUrl = `https://cdn.jwplayer.com/v2/media/${videoId}`;
const media = await (await fetch(mediaUrl)).json();
const scriptUrl = `https://content.jwplatform.com/libraries/${playerId}.js`;
const JW = await loadScript(scriptUrl, 'jwplayer');
// Sadly the JW player setup/render will not work in the shadow DOM.
this.append(templateLightDOM.content.cloneNode(true));
this.api = JW(this.querySelector('.jwplayer')).setup({
width: '100%',
height: '100%',
preload: this.getAttribute('preload') ?? 'metadata',
...media,
});
await promisify(this.api.on, this.api)('ready');
this.api.getContainer().classList.toggle('jw-no-controls', !this.controls);
}
async attributeChangedCallback(attrName, oldValue, newValue) {
if (['controls', 'muted'].includes(attrName)) {
await this.loadComplete;
switch (attrName) {
case 'controls':
this.api
.getContainer()
.classList.toggle('jw-no-controls', !this.controls);
break;
case 'muted':
this.muted = this.hasAttribute('muted');
break;
}
return;
}
super.attributeChangedCallback(attrName, oldValue, newValue);
}
get paused() {
return this.nativeEl?.paused ?? true;
}
}
const loadScriptCache = {};
async function loadScript(src, globalName) {
if (!globalName) return import(src);
if (loadScriptCache[src]) return loadScriptCache[src];
if (self[globalName]) return self[globalName];
return (loadScriptCache[src] = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.defer = true;
script.src = src;
script.onload = () => resolve(self[globalName]);
script.onerror = reject;
document.head.append(script);
}));
}
export function promisify(fn) {
return (...args) =>
new Promise((resolve) => {
fn(...args, (...res) => {
if (res.length > 1) resolve(res);
else resolve(res[0]);
});
});
}
if (globalThis.customElements && !globalThis.customElements.get('jwplayer-video')) {
globalThis.customElements.define('jwplayer-video', JWPlayerVideoElement);
}
export default JWPlayerVideoElement;