@mux/mux-player 0.1.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/LICENSE +9 -0
  3. package/README.md +231 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +161 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/coverage/lcov-report/src/dialog.ts.html +247 -0
  13. package/coverage/lcov-report/src/errors.ts.html +574 -0
  14. package/coverage/lcov-report/src/helpers.ts.html +478 -0
  15. package/coverage/lcov-report/src/html.ts.html +580 -0
  16. package/coverage/lcov-report/src/index.html +251 -0
  17. package/coverage/lcov-report/src/index.ts.html +2941 -0
  18. package/coverage/lcov-report/src/logger.ts.html +163 -0
  19. package/coverage/lcov-report/src/media-chrome/dialog.ts.html +661 -0
  20. package/coverage/lcov-report/src/media-chrome/index.html +131 -0
  21. package/coverage/lcov-report/src/media-chrome/time-display.ts.html +295 -0
  22. package/coverage/lcov-report/src/media-theme-mux/icons/airplay.svg.html +109 -0
  23. package/coverage/lcov-report/src/media-theme-mux/icons/captions-off.svg.html +100 -0
  24. package/coverage/lcov-report/src/media-theme-mux/icons/captions-on.svg.html +100 -0
  25. package/coverage/lcov-report/src/media-theme-mux/icons/fullscreen-enter.svg.html +100 -0
  26. package/coverage/lcov-report/src/media-theme-mux/icons/fullscreen-exit.svg.html +100 -0
  27. package/coverage/lcov-report/src/media-theme-mux/icons/index.html +326 -0
  28. package/coverage/lcov-report/src/media-theme-mux/icons/pause.svg.html +100 -0
  29. package/coverage/lcov-report/src/media-theme-mux/icons/pip-enter.svg.html +100 -0
  30. package/coverage/lcov-report/src/media-theme-mux/icons/pip-exit.svg.html +100 -0
  31. package/coverage/lcov-report/src/media-theme-mux/icons/play.svg.html +100 -0
  32. package/coverage/lcov-report/src/media-theme-mux/icons/seek-backward.svg.html +124 -0
  33. package/coverage/lcov-report/src/media-theme-mux/icons/seek-forward.svg.html +124 -0
  34. package/coverage/lcov-report/src/media-theme-mux/icons/volume-high.svg.html +103 -0
  35. package/coverage/lcov-report/src/media-theme-mux/icons/volume-low.svg.html +103 -0
  36. package/coverage/lcov-report/src/media-theme-mux/icons/volume-medium.svg.html +103 -0
  37. package/coverage/lcov-report/src/media-theme-mux/icons/volume-off.svg.html +103 -0
  38. package/coverage/lcov-report/src/media-theme-mux/icons.ts.html +184 -0
  39. package/coverage/lcov-report/src/media-theme-mux/index.html +146 -0
  40. package/coverage/lcov-report/src/media-theme-mux/media-theme-mux.ts.html +1279 -0
  41. package/coverage/lcov-report/src/media-theme-mux/styles.css.html +586 -0
  42. package/coverage/lcov-report/src/styles.css.html +211 -0
  43. package/coverage/lcov-report/src/template.ts.html +463 -0
  44. package/coverage/lcov-report/src/utils.ts.html +385 -0
  45. package/coverage/lcov-report/src/video-api.ts.html +979 -0
  46. package/coverage/lcov.info +4058 -0
  47. package/dist/index.cjs.js +1432 -0
  48. package/dist/index.mjs +709 -0
  49. package/dist/mux-player.js +1478 -0
  50. package/dist/mux-player.mjs +1478 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -0
  52. package/dist/types/dialog.d.ts +6 -0
  53. package/dist/types/errors.d.ts +6 -0
  54. package/dist/types/helpers.d.ts +26 -0
  55. package/dist/types/html.d.ts +18 -0
  56. package/dist/types/index.d.ts +199 -0
  57. package/dist/types/logger.d.ts +5 -0
  58. package/dist/types/media-chrome/dialog.d.ts +12 -0
  59. package/dist/types/media-chrome/time-display.d.ts +9 -0
  60. package/dist/types/media-theme-mux/icons.d.ts +15 -0
  61. package/dist/types/media-theme-mux/media-theme-mux.d.ts +29 -0
  62. package/dist/types/template.d.ts +5 -0
  63. package/dist/types/utils.d.ts +10 -0
  64. package/dist/types/video-api.d.ts +64 -0
  65. package/dist/types-ts3.4/dialog.d.ts +6 -0
  66. package/dist/types-ts3.4/errors.d.ts +6 -0
  67. package/dist/types-ts3.4/helpers.d.ts +26 -0
  68. package/dist/types-ts3.4/html.d.ts +18 -0
  69. package/dist/types-ts3.4/index.d.ts +180 -0
  70. package/dist/types-ts3.4/logger.d.ts +5 -0
  71. package/dist/types-ts3.4/media-chrome/dialog.d.ts +12 -0
  72. package/dist/types-ts3.4/media-chrome/time-display.d.ts +9 -0
  73. package/dist/types-ts3.4/media-theme-mux/icons.d.ts +15 -0
  74. package/dist/types-ts3.4/media-theme-mux/media-theme-mux.d.ts +29 -0
  75. package/dist/types-ts3.4/template.d.ts +5 -0
  76. package/dist/types-ts3.4/utils.d.ts +10 -0
  77. package/dist/types-ts3.4/video-api.d.ts +53 -0
  78. package/lang/en.json +32 -0
  79. package/lang/nl.json +31 -0
  80. package/package.json +107 -0
  81. package/src/dialog.ts +54 -0
  82. package/src/errors.ts +163 -0
  83. package/src/helpers.ts +131 -0
  84. package/src/html.ts +165 -0
  85. package/src/index.ts +952 -0
  86. package/src/logger.ts +26 -0
  87. package/src/media-chrome/dialog.ts +192 -0
  88. package/src/media-chrome/time-display.ts +70 -0
  89. package/src/media-theme-mux/icons/airplay.svg +8 -0
  90. package/src/media-theme-mux/icons/captions-off.svg +5 -0
  91. package/src/media-theme-mux/icons/captions-on.svg +5 -0
  92. package/src/media-theme-mux/icons/fullscreen-enter.svg +5 -0
  93. package/src/media-theme-mux/icons/fullscreen-exit.svg +5 -0
  94. package/src/media-theme-mux/icons/pause.svg +5 -0
  95. package/src/media-theme-mux/icons/pip-enter.svg +5 -0
  96. package/src/media-theme-mux/icons/pip-exit.svg +5 -0
  97. package/src/media-theme-mux/icons/play.svg +5 -0
  98. package/src/media-theme-mux/icons/seek-backward.svg +13 -0
  99. package/src/media-theme-mux/icons/seek-forward.svg +13 -0
  100. package/src/media-theme-mux/icons/volume-high.svg +6 -0
  101. package/src/media-theme-mux/icons/volume-low.svg +6 -0
  102. package/src/media-theme-mux/icons/volume-medium.svg +6 -0
  103. package/src/media-theme-mux/icons/volume-off.svg +6 -0
  104. package/src/media-theme-mux/icons.ts +33 -0
  105. package/src/media-theme-mux/media-theme-mux.ts +398 -0
  106. package/src/media-theme-mux/styles.css +167 -0
  107. package/src/styles.css +42 -0
  108. package/src/template.ts +126 -0
  109. package/src/types.d.ts +52 -0
  110. package/src/utils.ts +100 -0
  111. package/src/video-api.ts +298 -0
  112. package/test/errors.test.js +169 -0
  113. package/test/helpers.test.js +78 -0
  114. package/test/player.test.js +696 -0
  115. package/test/template.test.js +70 -0
  116. package/test/utils.test.js +21 -0
  117. package/test/web-test-runner.config.mjs +29 -0
  118. package/tsconfig.json +21 -0
@@ -0,0 +1,696 @@
1
+ import { fixture, assert, aTimeout, waitUntil } from '@open-wc/testing';
2
+ import '../src/index.ts';
3
+
4
+ describe('<mux-player>', () => {
5
+ it('has a Mux specific API', async function () {
6
+ this.timeout(5000);
7
+
8
+ const player = await fixture(`<mux-player
9
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
10
+ env-key="ilc02s65tkrc2mk69b7q2qdkf"
11
+ start-time="0"
12
+ stream-type="on-demand"
13
+ prefer-mse
14
+ muted
15
+ ></mux-player>`);
16
+
17
+ assert.equal(player.playbackId, 'DS00Spx1CV902MCtPj5WknGlR102V5HFkDe', 'playback-id is reflected');
18
+ assert.equal(player.envKey, 'ilc02s65tkrc2mk69b7q2qdkf', 'env-key is reflected');
19
+ assert.equal(player.startTime, 0, 'startTime is set to 0');
20
+ assert.equal(player.streamType, 'on-demand', 'stream-type is on-demand');
21
+ assert.equal(player.preferMse, true, 'prefer-mse is on');
22
+ assert.equal(player.debug, false, 'debug is off');
23
+ });
24
+
25
+ it('has a video like API', async function () {
26
+ this.timeout(10000);
27
+
28
+ const player = await fixture(`<mux-player
29
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
30
+ stream-type="on-demand"
31
+ muted
32
+ preload="auto"
33
+ ></mux-player>`);
34
+
35
+ assert(player.paused, 'is paused on initialization');
36
+ assert(!player.ended, 'is not ended');
37
+
38
+ assert(!player.loop, 'loop is false by default');
39
+ player.loop = true;
40
+ assert(player.loop, 'loop is true');
41
+
42
+ assert.equal(player.volume, 1, 'is all turned up');
43
+ player.volume = 0.5;
44
+ assert.equal(player.volume, 0.5, 'is half volume');
45
+
46
+ player.muted = true;
47
+ assert(player.muted, 'is muted');
48
+
49
+ try {
50
+ await player.play();
51
+ } catch (error) {
52
+ console.warn(error);
53
+ }
54
+
55
+ assert(!player.paused, 'is playing after player.play()');
56
+ assert.equal(Math.round(player.duration), 134, `is 134s long`);
57
+
58
+ await aTimeout(1000);
59
+
60
+ assert.equal(String(Math.round(player.currentTime)), 1, 'is about 1s in');
61
+
62
+ player.playbackRate = 2;
63
+ await aTimeout(1000);
64
+
65
+ assert.equal(String(Math.round(player.currentTime)), 3, 'is about 3s in');
66
+ });
67
+
68
+ it('playbackId is forwarded to the media element', async function () {
69
+ const player = await fixture(`<mux-player
70
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
71
+ stream-type="on-demand"
72
+ muted
73
+ ></mux-player>`);
74
+
75
+ assert.equal(player.playbackId, 'DS00Spx1CV902MCtPj5WknGlR102V5HFkDe');
76
+ });
77
+
78
+ it('autoplay is forwarded to the media element', async function () {
79
+ const player = await fixture(`<mux-player
80
+ autoplay
81
+ ></mux-player>`);
82
+ const muxVideo = player.media;
83
+
84
+ assert.equal(player.autoplay, true);
85
+ assert.equal(muxVideo.autoplay, true);
86
+
87
+ player.removeAttribute('autoplay');
88
+ assert(!muxVideo.hasAttribute('autoplay'), `has autoplay attr removed`);
89
+
90
+ player.setAttribute('autoplay', '');
91
+ assert.equal(muxVideo.getAttribute('autoplay'), '', `has autoplay attr added`);
92
+ assert.equal(muxVideo.autoplay, true, `has autoplay enabled`);
93
+ });
94
+
95
+ it('muted is forwarded to the media element', async function () {
96
+ const player = await fixture(`<mux-player
97
+ muted
98
+ ></mux-player>`);
99
+ const muxVideo = player.media;
100
+
101
+ assert.equal(player.muted, true);
102
+ assert.equal(muxVideo.muted, true);
103
+
104
+ player.removeAttribute('muted');
105
+ assert(!muxVideo.hasAttribute('muted'), `has muted attr removed`);
106
+
107
+ player.setAttribute('muted', '');
108
+ assert.equal(muxVideo.getAttribute('muted'), '', `has muted attr added`);
109
+ assert.equal(muxVideo.muted, true, `has muted enabled`);
110
+ });
111
+
112
+ // it("playsinline is forwarded to the media element", async function () {
113
+ // const player = await fixture(`<mux-player
114
+ // playsinline
115
+ // ></mux-player>`);
116
+ // const muxVideo = player.media;
117
+
118
+ // assert.equal(player.playsInline, true);
119
+ // assert.equal(muxVideo.playsInline, true);
120
+
121
+ // player.removeAttribute("playsinline");
122
+ // assert(
123
+ // !muxVideo.hasAttribute("playsinline"),
124
+ // `has playsinline attr removed`
125
+ // );
126
+
127
+ // player.setAttribute("playsinline", "");
128
+ // assert.equal(
129
+ // muxVideo.getAttribute("playsinline"),
130
+ // "",
131
+ // `has playsinline attr added`
132
+ // );
133
+ // assert.equal(muxVideo.playsInline, true, `has playsInline enabled`);
134
+ // });
135
+
136
+ it('loop is forwarded to the media element', async function () {
137
+ const player = await fixture(`<mux-player
138
+ loop
139
+ ></mux-player>`);
140
+ const muxVideo = player.media;
141
+
142
+ assert.equal(player.loop, true);
143
+ assert.equal(muxVideo.loop, true);
144
+
145
+ player.removeAttribute('loop');
146
+ assert(!muxVideo.hasAttribute('loop'), `has loop attr removed`);
147
+
148
+ player.setAttribute('loop', '');
149
+ assert.equal(muxVideo.getAttribute('loop'), '', `has loop attr added`);
150
+ assert.equal(muxVideo.loop, true, `has loop enabled`);
151
+ });
152
+
153
+ // it("crossorigin is forwarded to the media element", async function () {
154
+ // const player = await fixture(`<mux-player
155
+ // crossorigin="anonymous"
156
+ // ></mux-player>`);
157
+ // const muxVideo = player.media;
158
+
159
+ // assert.equal(player.crossOrigin, "anonymous");
160
+ // assert.equal(muxVideo.crossOrigin, "anonymous");
161
+
162
+ // player.removeAttribute("crossorigin");
163
+ // assert(
164
+ // !muxVideo.hasAttribute("crossorigin"),
165
+ // `has crossorigin attr removed`
166
+ // );
167
+
168
+ // player.setAttribute("crossorigin", "use-credentials");
169
+ // assert.equal(
170
+ // muxVideo.getAttribute("crossorigin"),
171
+ // "use-credentials",
172
+ // `has crossorigin attr added`
173
+ // );
174
+ // assert.equal(
175
+ // muxVideo.crossOrigin,
176
+ // "use-credentials",
177
+ // `has crossorigin enabled`
178
+ // );
179
+ // });
180
+
181
+ it('preload is forwarded to the media element', async function () {
182
+ const player = await fixture(`<mux-player
183
+ preload="metadata"
184
+ ></mux-player>`);
185
+ const muxVideo = player.media;
186
+
187
+ assert.equal(player.preload, 'metadata');
188
+ assert.equal(muxVideo.preload, 'metadata');
189
+
190
+ player.removeAttribute('preload');
191
+ assert(!muxVideo.hasAttribute('preload'), `has preload attr removed`);
192
+
193
+ player.setAttribute('preload', 'auto');
194
+ assert.equal(muxVideo.getAttribute('preload'), 'auto', `has preload attr added`);
195
+ assert.equal(muxVideo.preload, 'auto', `has preload enabled`);
196
+ });
197
+
198
+ it('poster is forwarded to the media element', async function () {
199
+ const player = await fixture(`<mux-player
200
+ poster="https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=0"
201
+ ></mux-player>`);
202
+ const muxVideo = player.media;
203
+
204
+ assert.equal(
205
+ player.poster,
206
+ 'https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=0'
207
+ );
208
+ assert.equal(
209
+ muxVideo.poster,
210
+ 'https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=0'
211
+ );
212
+
213
+ player.removeAttribute('poster');
214
+ assert(!muxVideo.hasAttribute('poster'), `has poster attr removed`);
215
+
216
+ player.setAttribute(
217
+ 'poster',
218
+ 'https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=1'
219
+ );
220
+ assert.equal(
221
+ muxVideo.getAttribute('poster'),
222
+ 'https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=1',
223
+ `has poster attr added`
224
+ );
225
+ assert.equal(
226
+ muxVideo.poster,
227
+ 'https://image.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE/thumbnail.jpg?time=1',
228
+ `has poster enabled`
229
+ );
230
+ });
231
+
232
+ it('src is forwarded to the media element', async function () {
233
+ const player = await fixture(`<mux-player
234
+ src="https://stream.mux.com/r4rOE02cc95tbe3I00302nlrHfT023Q3IedFJW029w018KxZA.m3u8"
235
+ ></mux-player>`);
236
+ const muxVideo = player.media;
237
+
238
+ assert.equal(player.src, 'https://stream.mux.com/r4rOE02cc95tbe3I00302nlrHfT023Q3IedFJW029w018KxZA.m3u8');
239
+ assert.equal(muxVideo.src, 'https://stream.mux.com/r4rOE02cc95tbe3I00302nlrHfT023Q3IedFJW029w018KxZA.m3u8');
240
+
241
+ player.removeAttribute('src');
242
+ assert(!muxVideo.hasAttribute('src'), `has src attr removed`);
243
+
244
+ player.setAttribute('src', 'https://stream.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE.m3u8');
245
+ assert.equal(
246
+ muxVideo.getAttribute('src'),
247
+ 'https://stream.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE.m3u8',
248
+ `has src attr added`
249
+ );
250
+ assert.equal(
251
+ muxVideo.src,
252
+ 'https://stream.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE.m3u8',
253
+ `has src enabled`
254
+ );
255
+ });
256
+
257
+ it('should forward metadata attributes to the media element', async () => {
258
+ const video_id = 'test-video-id';
259
+ const video_title = 'test-video-title';
260
+ const viewer_user_id = 'test-viewer-user-id';
261
+ const player = await fixture(`<mux-player
262
+ metadata-video-id="${video_id}"
263
+ metadata-video-title="${video_title}"
264
+ metadata-viewer-user-id="${viewer_user_id}"
265
+ ></mux-player>`);
266
+
267
+ const actual = player.media.metadata;
268
+ const expected = { video_id, video_title, viewer_user_id };
269
+ assert.include(actual, expected, 'has expected metadata entries from attrs');
270
+ });
271
+
272
+ it('muted attribute behaves like expected', async function () {
273
+ const player = await fixture(`<mux-player
274
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
275
+ stream-type="on-demand"
276
+ muted
277
+ ></mux-player>`);
278
+
279
+ const muxVideo = player.media;
280
+ const nativeVideo = muxVideo.shadowRoot.querySelector('video');
281
+
282
+ assert(player.muted, 'player.muted is true');
283
+ assert(muxVideo.muted, 'muxVideo.muted is true');
284
+ assert(nativeVideo.muted, 'nativeVideo.muted is true');
285
+
286
+ player.removeAttribute('muted');
287
+
288
+ assert(!player.muted, 'player.muted is false');
289
+ assert(!muxVideo.muted, 'muxVideo.muted is false');
290
+ assert(!nativeVideo.muted, 'nativeVideo.muted is false');
291
+
292
+ player.setAttribute('muted', '');
293
+
294
+ assert(player.muted, 'player.muted is true');
295
+ assert(muxVideo.muted, 'muxVideo.muted is true');
296
+ assert(nativeVideo.muted, 'nativeVideo.muted is true');
297
+ });
298
+
299
+ it('volume attribute behaves like expected', async function () {
300
+ const player = await fixture(`<mux-player
301
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
302
+ stream-type="on-demand"
303
+ volume="0.4"
304
+ ></mux-player>`);
305
+
306
+ assert.equal(player.getAttribute('volume'), '0.4');
307
+
308
+ const muxVideo = player.media;
309
+ const nativeVideo = muxVideo.shadowRoot.querySelector('video');
310
+
311
+ assert.equal(player.volume, 0.4, 'player.volume is 0.4');
312
+ assert.equal(muxVideo.volume, 0.4, 'muxVideo.volume is 0.4');
313
+ assert.equal(nativeVideo.volume, 0.4, 'nativeVideo.volume is 0.4');
314
+
315
+ player.setAttribute('volume', '0.9');
316
+
317
+ assert.equal(player.volume, 0.9, 'player.volume is 0.9');
318
+ assert.equal(muxVideo.volume, 0.9, 'muxVideo.volume is 0.9');
319
+ assert.equal(nativeVideo.volume, 0.9, 'nativeVideo.volume is 0.9');
320
+ });
321
+
322
+ it('playbackrate attribute behaves like expected', async function () {
323
+ const player = await fixture(`<mux-player
324
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
325
+ stream-type="on-demand"
326
+ playbackrate="2"
327
+ ></mux-player>`);
328
+
329
+ assert.equal(player.getAttribute('playbackrate'), '2');
330
+
331
+ const muxVideo = player.media;
332
+ const nativeVideo = muxVideo.shadowRoot.querySelector('video');
333
+
334
+ assert.equal(player.playbackRate, 2, 'player.playbackRate is 2');
335
+ assert.equal(muxVideo.playbackRate, 2, 'muxVideo.playbackRate is 2');
336
+ assert.equal(nativeVideo.playbackRate, 2, 'nativeVideo.playbackRate is 2');
337
+
338
+ player.setAttribute('playbackrate', '0.7');
339
+
340
+ assert.equal(player.playbackRate, 0.7, 'player.playbackRate is 0.7');
341
+ assert.equal(muxVideo.playbackRate, 0.7, 'muxVideo.playbackRate is 0.7');
342
+ assert.equal(nativeVideo.playbackRate, 0.7, 'nativeVideo.playbackRate is 0.7');
343
+ });
344
+
345
+ it("signing tokens generate correct asset URL's", async function () {
346
+ // tokens expire in 10 years
347
+ const player = await fixture(`<mux-player
348
+ stream-type="on-demand"
349
+ playback-id="bos2bPV3qbFgpVPaQ900Xd5UcdM6WXTmz02WZSz01nJ00tY"
350
+ playback-token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE2MjgsImF1ZCI6InYiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.OUegJAmrlvD9BhzUhogrup_mYRBYNG2ocqmJZK2lKPLFmP1jLKi99Lj_9ZQqIXgmoYeXo2jKr3WFMO8nbGwtZFKU2_szq1EWlj4mBgdWXfAP5amC92qkm87nIuNFM2WVANGlBksmj8uOmYNIuPh1Ctti1qiJEYkf-JthWFFpaR_2TlQJ7g0bmRPzk3nOPDtqZnJBfTVm3n4Kp7Cr27a_VBA6zpoW6DwjJ6_uPkm6TAxXjw7VWNd3YVLs7S_jgs8q3t9DPpAN57q94syVQtEUkRh4tlDX-gdIrJDi9nFB1fIBh45pD01PvrAWzZXKKE9YSW7dnktqSUy81kcu2F_gXA"
351
+ thumbnail-token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE3MzYsImF1ZCI6InQiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.gDe_efqmRB5E3e4ag6in8MfMK-Vn3c_3B4M-BiWw6lg2aaf2BOTv7ltxhn2cvg4G0iFi-esRjhDlHbMRTxwTGavsx8TRLFtJ8vyBzToaFQbQMrn9OZztq_XrCEwqkD8bUAVtdOT1YB606OZyy6XO-CxdMRrKMUsM-cGrfv0TxvzJjThJBY4SzFv_whtYRxqAypZojROU7IiTbqcsk_cSrRMjB7WyAOAvyPNKnr6RkVEuMJtlCtaf_e4DIJHebZUZb3JmVTG4jIWrD1QkN7uLUwCPPRvGhXwhet9JaJPyC5lmkcb9YmH-15V6GOpwSg7sDMGC3YS4aIb_RtVkan0t-w"
352
+ storyboard-token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE3NzcsImF1ZCI6InMiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.aVd0dsOJUVeQko3BWd9YEhL41Eytf_ZfaBeNzHSSUqU_gREa_jJEVTlRfuiE4g71cKJLSiVTKP7f-F7Txh6DlL8E2SkonfIPB2H0f_3DQxYLso2E8qI4zuJkyxKORbQFLAEB_vSE-2lMbrHXfdpQhv6SrVyu6di9ku0LpFpoyz-_7fVJICr8nhlsqOGt66AYcaa99TXoZ582FWzBaePmWw-WWKYsLvtNjLS9UoxbdVaBRwNylohvhh-i1Y9dNilyNooJ7O8Cj4GuMjeh1pCj0BOrGagxrWrswm3HjUVNUqFq5JCWnJCxgjjwiV4RLZg_4z7gkBXyX7H2-i1dKA3Cpw"
353
+ ></mux-player>`);
354
+
355
+ const muxVideo = player.media;
356
+ const storyboardTrack = muxVideo.shadowRoot.querySelector("track[label='thumbnails']");
357
+
358
+ assert.equal(
359
+ muxVideo.getAttribute('src'),
360
+ 'https://stream.mux.com/bos2bPV3qbFgpVPaQ900Xd5UcdM6WXTmz02WZSz01nJ00tY.m3u8?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE2MjgsImF1ZCI6InYiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.OUegJAmrlvD9BhzUhogrup_mYRBYNG2ocqmJZK2lKPLFmP1jLKi99Lj_9ZQqIXgmoYeXo2jKr3WFMO8nbGwtZFKU2_szq1EWlj4mBgdWXfAP5amC92qkm87nIuNFM2WVANGlBksmj8uOmYNIuPh1Ctti1qiJEYkf-JthWFFpaR_2TlQJ7g0bmRPzk3nOPDtqZnJBfTVm3n4Kp7Cr27a_VBA6zpoW6DwjJ6_uPkm6TAxXjw7VWNd3YVLs7S_jgs8q3t9DPpAN57q94syVQtEUkRh4tlDX-gdIrJDi9nFB1fIBh45pD01PvrAWzZXKKE9YSW7dnktqSUy81kcu2F_gXA'
361
+ );
362
+
363
+ assert.equal(
364
+ muxVideo.getAttribute('poster'),
365
+ 'https://image.mux.com/bos2bPV3qbFgpVPaQ900Xd5UcdM6WXTmz02WZSz01nJ00tY/thumbnail.jpg?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE3MzYsImF1ZCI6InQiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.gDe_efqmRB5E3e4ag6in8MfMK-Vn3c_3B4M-BiWw6lg2aaf2BOTv7ltxhn2cvg4G0iFi-esRjhDlHbMRTxwTGavsx8TRLFtJ8vyBzToaFQbQMrn9OZztq_XrCEwqkD8bUAVtdOT1YB606OZyy6XO-CxdMRrKMUsM-cGrfv0TxvzJjThJBY4SzFv_whtYRxqAypZojROU7IiTbqcsk_cSrRMjB7WyAOAvyPNKnr6RkVEuMJtlCtaf_e4DIJHebZUZb3JmVTG4jIWrD1QkN7uLUwCPPRvGhXwhet9JaJPyC5lmkcb9YmH-15V6GOpwSg7sDMGC3YS4aIb_RtVkan0t-w'
366
+ );
367
+
368
+ assert.equal(
369
+ storyboardTrack.getAttribute('src'),
370
+ 'https://image.mux.com/bos2bPV3qbFgpVPaQ900Xd5UcdM6WXTmz02WZSz01nJ00tY/storyboard.vtt?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik96VU90ek1nUWhPbkk2MDJ6SlFQbU52THR4MDBnSjJqTlBxN0tTTzAxQlozelEifQ.eyJleHAiOjE5NjE2MDE3NzcsImF1ZCI6InMiLCJzdWIiOiJib3MyYlBWM3FiRmdwVlBhUTkwMFhkNVVjZE02V1hUbXowMldaU3owMW5KMDB0WSJ9.aVd0dsOJUVeQko3BWd9YEhL41Eytf_ZfaBeNzHSSUqU_gREa_jJEVTlRfuiE4g71cKJLSiVTKP7f-F7Txh6DlL8E2SkonfIPB2H0f_3DQxYLso2E8qI4zuJkyxKORbQFLAEB_vSE-2lMbrHXfdpQhv6SrVyu6di9ku0LpFpoyz-_7fVJICr8nhlsqOGt66AYcaa99TXoZ582FWzBaePmWw-WWKYsLvtNjLS9UoxbdVaBRwNylohvhh-i1Y9dNilyNooJ7O8Cj4GuMjeh1pCj0BOrGagxrWrswm3HjUVNUqFq5JCWnJCxgjjwiV4RLZg_4z7gkBXyX7H2-i1dKA3Cpw'
371
+ );
372
+ });
373
+ });
374
+
375
+ describe('<mux-player> playbackId transitions', () => {
376
+ it('loads the new playbackId', async function () {
377
+ const player = await fixture(`<mux-player
378
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
379
+ stream-type="on-demand"
380
+ muted
381
+ ></mux-player>`);
382
+
383
+ assert.equal(player.playbackId, 'DS00Spx1CV902MCtPj5WknGlR102V5HFkDe');
384
+
385
+ player.playbackId = 'xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE';
386
+
387
+ assert.equal(
388
+ player.src,
389
+ 'https://stream.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE.m3u8?redundant_streams=true'
390
+ );
391
+ });
392
+
393
+ it('loads the new playbackId and clears dialog state', async function () {
394
+ const player = await fixture(`<mux-player
395
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
396
+ stream-type="on-demand"
397
+ muted
398
+ ></mux-player>`);
399
+
400
+ assert.equal(player.playbackId, 'DS00Spx1CV902MCtPj5WknGlR102V5HFkDe');
401
+
402
+ player.dispatchEvent(
403
+ new CustomEvent('error', {
404
+ detail: { code: MediaError.MEDIA_ERR_NETWORK },
405
+ })
406
+ );
407
+
408
+ assert.equal(player.shadowRoot.querySelector('mxp-dialog h3').textContent, 'Network Error');
409
+
410
+ player.playbackId = 'xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE';
411
+
412
+ assert.equal(player.shadowRoot.querySelector('mxp-dialog h3'), null);
413
+ });
414
+
415
+ it('loads the new src and clears dialog state', async function () {
416
+ const player = await fixture(`<mux-player
417
+ src="https://stream.mux.com/DS00Spx1CV902MCtPj5WknGlR102V5HFkDe.m3u8"
418
+ stream-type="on-demand"
419
+ muted
420
+ ></mux-player>`);
421
+
422
+ assert.equal(player.src, 'https://stream.mux.com/DS00Spx1CV902MCtPj5WknGlR102V5HFkDe.m3u8');
423
+
424
+ player.dispatchEvent(
425
+ new CustomEvent('error', {
426
+ detail: { code: MediaError.MEDIA_ERR_NETWORK },
427
+ })
428
+ );
429
+
430
+ assert.equal(player.shadowRoot.querySelector('mxp-dialog h3').textContent, 'Network Error');
431
+
432
+ player.src = 'https://stream.mux.com/xLGf7y8cRquv7QXoDB02zEe6centwKfVmUOiPSY02JhCE.m3u8';
433
+
434
+ assert.equal(player.shadowRoot.querySelector('mxp-dialog h3'), null);
435
+ });
436
+ });
437
+
438
+ describe('seek to live behaviors', function () {
439
+ this.timeout(12000);
440
+
441
+ it('should not have a seek to live button if the stream-type is not live/ll-live', async function () {
442
+ const playerEl = await fixture(`<mux-player
443
+ playback-id="DS00Spx1CV902MCtPj5WknGlR102V5HFkDe"
444
+ stream-type="on-demand"
445
+ preload="auto"
446
+ ></mux-player>`);
447
+
448
+ const mediaControllerEl = playerEl.mediaController;
449
+ const seekToLiveEl = playerEl.shadowRoot.querySelector('slot[name="seek-to-live-button"]');
450
+ assert.exists(mediaControllerEl);
451
+ assert.notExists(seekToLiveEl);
452
+ });
453
+
454
+ it('should have a seek to live button if the stream-type is live', async function () {
455
+ const playerEl = await fixture(`<mux-player
456
+ playback-id="v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM"
457
+ stream-type="live"
458
+ preload="auto"
459
+ ></mux-player>`);
460
+
461
+ const mediaControllerEl = playerEl.mediaController;
462
+ const seekToLiveEl = playerEl.theme.shadowRoot.querySelector('slot[name="seek-to-live-button"]');
463
+ assert.exists(mediaControllerEl);
464
+ assert.exists(seekToLiveEl);
465
+ });
466
+
467
+ it('should have a seek to live button if the stream-type is ll-live', async function () {
468
+ const playerEl = await fixture(`<mux-player
469
+ playback-id="v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM"
470
+ stream-type="ll-live"
471
+ preload="auto"
472
+ ></mux-player>`);
473
+
474
+ const mediaControllerEl = playerEl.mediaController;
475
+ const seekToLiveEl = playerEl.theme.shadowRoot.querySelector('slot[name="seek-to-live-button"]');
476
+ assert.exists(mediaControllerEl);
477
+ assert.exists(seekToLiveEl);
478
+ });
479
+
480
+ it('should seek to live when seek to live button pressed', async function () {
481
+ const playerEl = await fixture(`<mux-player
482
+ playback-id="v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM"
483
+ muted
484
+ stream-type="ll-live"
485
+ preload="auto"
486
+ ></mux-player>`);
487
+
488
+ const seekToLiveEl = playerEl.shadowRoot.querySelector('.mxp-seek-to-live-button');
489
+ // NOTE: Need try catch due to bug in play+autoplay behavior (CJP)
490
+ try {
491
+ await playerEl.play();
492
+ } catch (_e) {}
493
+ await waitUntil(() => !playerEl.paused, 'play() failed');
494
+ await waitUntil(() => playerEl.inLiveWindow, 'playback did not start inLiveWindow');
495
+ playerEl.pause();
496
+ await waitUntil(() => !playerEl.inLiveWindow, 'still inLiveWindow after long pause', { timeout: 7500 });
497
+ seekToLiveEl.click();
498
+ await waitUntil(() => playerEl.inLiveWindow, 'clicking seek to live did not seek to live window');
499
+ });
500
+
501
+ it('should seek to live when play button is pressed', async function () {
502
+ const playerEl = await fixture(`<mux-player
503
+ playback-id="v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM"
504
+ muted
505
+ stream-type="ll-live"
506
+ preload="auto"
507
+ ></mux-player>`);
508
+
509
+ await playerEl.play();
510
+
511
+ await waitUntil(() => !playerEl.paused, 'play() failed');
512
+ await waitUntil(() => playerEl.inLiveWindow, 'playback did not start inLiveWindow');
513
+ playerEl.pause();
514
+ await waitUntil(() => !playerEl.inLiveWindow, 'still inLiveWindow after long pause', { timeout: 7500 });
515
+
516
+ const mcPlayEl = playerEl.theme.shadowRoot.querySelector('media-play-button');
517
+ mcPlayEl.click();
518
+ await waitUntil(() => playerEl.inLiveWindow, 'clicking play did not seek to live window');
519
+ });
520
+ });
521
+
522
+ describe('<mux-player> should move cues up', () => {
523
+ it('when user the user active', async function () {
524
+ let done;
525
+ const promise = new Promise((resolve) => {
526
+ done = resolve;
527
+ });
528
+ const player = await fixture(`<mux-player
529
+ playback-id="qP5Eb2cj7MrNnoxBGz012pbZkMHqpIcrKMzd7ykGr01gM"
530
+ stream-type="on-demand"
531
+ muted
532
+ preload="auto"
533
+ ></mux-player>`);
534
+
535
+ const mc = player.mediaController;
536
+ const media = mc.media;
537
+
538
+ media.textTracks.addEventListener('addtrack', (e) => {
539
+ // wait till subtitles have loaded
540
+ if (e.track.kind === 'subtitles') {
541
+ // pool until cues have loaded
542
+ const poolInterval = setInterval(() => {
543
+ if (e.track.cues?.length) {
544
+ clearInterval(poolInterval);
545
+ } else {
546
+ return;
547
+ }
548
+
549
+ const firstCue = e.track.cues[0];
550
+ assert.equal(firstCue.line, 'auto', "the first cue's line is set to auto");
551
+
552
+ e.track.addEventListener(
553
+ 'cuechange',
554
+ () => {
555
+ const activeCue = e.track.activeCues[0];
556
+ assert.equal(activeCue.line, 'auto', "the active cue's line is set to auto");
557
+ mc.addEventListener(
558
+ 'userinactivechange',
559
+ () => {
560
+ assert.equal(activeCue.line, -4, 'the line is now set to -4');
561
+
562
+ mc.addEventListener(
563
+ 'userinactivechange',
564
+ () => {
565
+ setTimeout(() => {
566
+ assert.equal(
567
+ activeCue.line,
568
+ 'auto',
569
+ 'the line prop was reset to original value after the 500ms wait'
570
+ );
571
+ done();
572
+ }, 500);
573
+ },
574
+ { once: true }
575
+ );
576
+ mc.setAttribute('user-inactive', '');
577
+ mc.dispatchEvent(new Event('userinactivechange'));
578
+ },
579
+ { once: true }
580
+ );
581
+ mc.removeAttribute('user-inactive');
582
+ mc.dispatchEvent(new Event('userinactivechange'));
583
+ },
584
+ { once: true }
585
+ );
586
+
587
+ media.currentTime = firstCue.startTime + 0.1;
588
+ }, 10);
589
+ }
590
+ });
591
+
592
+ player.play().catch(() => {});
593
+ return promise;
594
+ });
595
+
596
+ it('when the player is paused even if user is inactive', async function () {
597
+ let done;
598
+ const promise = new Promise((resolve) => {
599
+ done = resolve;
600
+ });
601
+ const player = await fixture(`<mux-player
602
+ playback-id="qP5Eb2cj7MrNnoxBGz012pbZkMHqpIcrKMzd7ykGr01gM"
603
+ stream-type="on-demand"
604
+ muted
605
+ preload="auto"
606
+ ></mux-player>`);
607
+
608
+ const mc = player.mediaController;
609
+ const media = mc.media;
610
+
611
+ media.textTracks.addEventListener('addtrack', (e) => {
612
+ // wait till subtitles have loaded
613
+ if (e.track.kind === 'subtitles') {
614
+ // pool until cues have loaded
615
+ const poolInterval = setInterval(() => {
616
+ if (e.track.cues?.length) {
617
+ clearInterval(poolInterval);
618
+ } else {
619
+ return;
620
+ }
621
+
622
+ const firstCue = e.track.cues[0];
623
+ assert.equal(firstCue.line, 'auto', "the first cue's line is set to auto");
624
+
625
+ assert.isTrue(player.paused, 'player is paused');
626
+
627
+ e.track.addEventListener(
628
+ 'cuechange',
629
+ () => {
630
+ const activeCue = e.track.activeCues[0];
631
+ assert.equal(activeCue.line, -4, "the active cue's line is set to -4");
632
+ done();
633
+ },
634
+ { once: true }
635
+ );
636
+
637
+ media.currentTime = firstCue.startTime + 0.1;
638
+ }, 10);
639
+ }
640
+ });
641
+
642
+ return promise;
643
+ });
644
+
645
+ it('unless the cues should be ignored', async function () {
646
+ let done;
647
+ const promise = new Promise((resolve) => {
648
+ done = resolve;
649
+ });
650
+ const player = await fixture(`<mux-player
651
+ playback-id="qP5Eb2cj7MrNnoxBGz012pbZkMHqpIcrKMzd7ykGr01gM"
652
+ stream-type="on-demand"
653
+ muted
654
+ preload="auto"
655
+ ></mux-player>`);
656
+
657
+ const mc = player.mediaController;
658
+ const media = mc.media;
659
+
660
+ media.textTracks.addEventListener('addtrack', (e) => {
661
+ // wait till subtitles have loaded
662
+ if (e.track.kind === 'subtitles') {
663
+ // pool until cues have loaded
664
+ const poolInterval = setInterval(() => {
665
+ if (e.track.cues?.length) {
666
+ clearInterval(poolInterval);
667
+ } else {
668
+ return;
669
+ }
670
+
671
+ const firstCue = e.track.cues[0];
672
+
673
+ // position first cue at the top of the displayed area
674
+ // this should currently be ignored
675
+ firstCue.line = 0;
676
+
677
+ assert.isTrue(player.paused, 'player is paused');
678
+
679
+ e.track.addEventListener(
680
+ 'cuechange',
681
+ () => {
682
+ const activeCue = e.track.activeCues[0];
683
+ assert.equal(activeCue.line, 0, "the active cue's line was not updated");
684
+ done();
685
+ },
686
+ { once: true }
687
+ );
688
+
689
+ media.currentTime = firstCue.startTime + 0.1;
690
+ }, 10);
691
+ }
692
+ });
693
+
694
+ return promise;
695
+ });
696
+ });