@eluvio/elv-player-js 2.0.22 → 2.0.23
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.
- package/dist/.vite/manifest.json +17 -17
- package/dist/{Analytics-DMv5C4jR.mjs → Analytics-B72tGTQx.mjs} +1 -1
- package/dist/{Analytics-B5-vVweo.js → Analytics-CDXMirgj.js} +1 -1
- package/dist/{dash.all.min-DdJwXkcr.js → dash.all.min-3hdqLcqd.js} +1 -1
- package/dist/{dash.all.min-D6KUBLpO.mjs → dash.all.min-DlSRaJ_5.mjs} +1 -1
- package/dist/elv-player-js.cjs.js +1 -1
- package/dist/elv-player-js.css +1 -1
- package/dist/elv-player-js.es.js +1 -1
- package/dist/index-Be7Zq2Nh.js +378 -0
- package/dist/{index-CD_eflzW.mjs → index-BrZn3sD8.mjs} +77 -79
- package/dist/{index-BAQHi25X.mjs → index-BxTtC119.mjs} +19589 -19544
- package/dist/{index-M64Z5q4j.js → index-CO76930D.js} +3 -3
- package/lib/player/Cast.js +280 -0
- package/lib/player/Controls.js +100 -4
- package/lib/player/Player.js +74 -12
- package/lib/static/icons/Icons.js +2 -0
- package/lib/static/icons/svgs/airplay.svg +1 -0
- package/lib/static/icons/svgs/cast.svg +1 -0
- package/lib/static/stylesheets/common.module.scss +65 -0
- package/lib/static/stylesheets/controls-tv.module.scss +1 -0
- package/lib/static/stylesheets/controls-web.module.scss +15 -0
- package/lib/static/stylesheets/player.module.scss +16 -0
- package/lib/ui/BuildIcons.cjs +2 -0
- package/lib/ui/Components.jsx +44 -5
- package/lib/ui/Observers.js +28 -18
- package/lib/ui/PlayerUI.jsx +10 -0
- package/lib/ui/TVControls.jsx +25 -11
- package/lib/ui/WebControls.jsx +30 -25
- package/package.json +3 -3
- package/dist/index-Bw8z3Iax.js +0 -375
package/lib/player/Player.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import EluvioPlayerParameters from "./PlayerParameters.js";
|
|
2
2
|
import {InitializeFairPlayStream} from "./FairPlay.js";
|
|
3
|
-
|
|
3
|
+
import Cast from "./Cast";
|
|
4
4
|
import {Utils} from "@eluvio/elv-client-js";
|
|
5
5
|
import PlayerControls from "./Controls.js";
|
|
6
6
|
import {MergeDefaultParameters} from "../ui/Common";
|
|
7
7
|
|
|
8
|
+
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
9
|
+
|
|
8
10
|
const PlayerProfiles = {
|
|
9
11
|
default: {
|
|
10
12
|
label: "Default",
|
|
@@ -40,6 +42,7 @@ export class EluvioPlayer {
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
constructor({target, video, parameters, SetErrorMessage}) {
|
|
45
|
+
this.latest = true;
|
|
43
46
|
this.loading = true;
|
|
44
47
|
this.target = target;
|
|
45
48
|
this.video = video;
|
|
@@ -53,8 +56,12 @@ export class EluvioPlayer {
|
|
|
53
56
|
this.canPlay = false;
|
|
54
57
|
this.isLive = false;
|
|
55
58
|
this.dvrEnabled = false;
|
|
59
|
+
this.dvrAvailable = false;
|
|
56
60
|
this.behindLiveEdge = false;
|
|
57
61
|
this.publicMetadataUrl = undefined;
|
|
62
|
+
this.airplayAvailable = false;
|
|
63
|
+
this.chromecastAvailable = false;
|
|
64
|
+
this.casting = false;
|
|
58
65
|
|
|
59
66
|
try {
|
|
60
67
|
// If custom HLS parameters are specified, set profile to custom
|
|
@@ -84,11 +91,9 @@ export class EluvioPlayer {
|
|
|
84
91
|
configUrl: this.clientOptions.network
|
|
85
92
|
});
|
|
86
93
|
|
|
87
|
-
this.clientOptions.
|
|
88
|
-
token:
|
|
89
|
-
|
|
90
|
-
this.clientOptions.client.utils.B64(JSON.stringify({qspace_id: await this.clientOptions.client.ContentSpaceId()}))
|
|
91
|
-
});
|
|
94
|
+
if(this.clientOptions.staticToken) {
|
|
95
|
+
this.clientOptions.client.SetStaticToken({token: this.clientOptions.staticToken});
|
|
96
|
+
}
|
|
92
97
|
|
|
93
98
|
return this.clientOptions.client;
|
|
94
99
|
})();
|
|
@@ -151,9 +156,9 @@ export class EluvioPlayer {
|
|
|
151
156
|
|
|
152
157
|
if(this.sourceOptions.contentInfo.liveDVR === EluvioPlayerParameters.liveDVR.ON && offeringProperties.dvr_available) {
|
|
153
158
|
options.dvr = 1;
|
|
154
|
-
this.
|
|
159
|
+
this.dvrAvailable = true;
|
|
155
160
|
} else {
|
|
156
|
-
this.
|
|
161
|
+
this.dvrAvailable = false;
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
if(offeringProperties.live) {
|
|
@@ -200,6 +205,10 @@ export class EluvioPlayer {
|
|
|
200
205
|
const versionHash = playoutUrl.split("/").find(segment => segment.startsWith("hq__"));
|
|
201
206
|
|
|
202
207
|
return {
|
|
208
|
+
playoutParameters: {
|
|
209
|
+
...playoutParameters,
|
|
210
|
+
options
|
|
211
|
+
},
|
|
203
212
|
protocol,
|
|
204
213
|
drm,
|
|
205
214
|
playoutUrl,
|
|
@@ -294,6 +303,8 @@ export class EluvioPlayer {
|
|
|
294
303
|
|
|
295
304
|
this.__Reset();
|
|
296
305
|
|
|
306
|
+
|
|
307
|
+
|
|
297
308
|
this.initialized = false;
|
|
298
309
|
this.loading = true;
|
|
299
310
|
this.initTime = Date.now();
|
|
@@ -331,6 +342,24 @@ export class EluvioPlayer {
|
|
|
331
342
|
}
|
|
332
343
|
}
|
|
333
344
|
|
|
345
|
+
// Handle Chromecast
|
|
346
|
+
this.castHandler = new Cast({
|
|
347
|
+
player: this,
|
|
348
|
+
onReady: () => {
|
|
349
|
+
this.chromecastAvailable = true;
|
|
350
|
+
this.__SettingsUpdate();
|
|
351
|
+
},
|
|
352
|
+
onUpdate: () => {
|
|
353
|
+
this.__SettingsUpdate();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Detect Airplay availability
|
|
358
|
+
this.__RegisterVideoEventListener("webkitplaybacktargetavailabilitychanged", event => {
|
|
359
|
+
this.airplayAvailable = event.availability === "available";
|
|
360
|
+
this.__SettingsUpdate();
|
|
361
|
+
});
|
|
362
|
+
|
|
334
363
|
this.__RegisterVideoEventListener("play", () => {
|
|
335
364
|
this.reloads = 0;
|
|
336
365
|
this.playbackStarted = true;
|
|
@@ -350,7 +379,7 @@ export class EluvioPlayer {
|
|
|
350
379
|
});
|
|
351
380
|
|
|
352
381
|
this.__RegisterVideoEventListener("seeking", () => {
|
|
353
|
-
if(!this.isLive || !this.
|
|
382
|
+
if(!this.isLive || !this.dvrAvailable) { return; }
|
|
354
383
|
const diff = this.video.duration - this.video.currentTime;
|
|
355
384
|
this.behindLiveEdge = diff > 15;
|
|
356
385
|
this.__SettingsUpdate();
|
|
@@ -363,7 +392,7 @@ export class EluvioPlayer {
|
|
|
363
392
|
this.__RegisterVideoEventListener("ended", () => this.controls && this.controls.CollectionPlayNext({autoplay: true}));
|
|
364
393
|
}
|
|
365
394
|
|
|
366
|
-
let { versionHash, playoutUrl, protocol, drm, drms, multiviewOptions } = await this.__PlayoutOptions();
|
|
395
|
+
let { versionHash, playoutUrl, protocol, drm, drms, multiviewOptions, playoutParameters } = await this.__PlayoutOptions();
|
|
367
396
|
|
|
368
397
|
this.contentHash = versionHash;
|
|
369
398
|
|
|
@@ -376,6 +405,13 @@ export class EluvioPlayer {
|
|
|
376
405
|
|
|
377
406
|
this.authorizationToken = authorizationToken;
|
|
378
407
|
|
|
408
|
+
this.playoutUrl = playoutUrl.toString();
|
|
409
|
+
|
|
410
|
+
this.castHandler.SetMedia({
|
|
411
|
+
playoutOptions: this.sourceOptions.playoutOptions,
|
|
412
|
+
playoutParameters
|
|
413
|
+
});
|
|
414
|
+
|
|
379
415
|
if(this.__destroyed) { return; }
|
|
380
416
|
|
|
381
417
|
if(protocol === "hls") {
|
|
@@ -480,7 +516,8 @@ export class EluvioPlayer {
|
|
|
480
516
|
async __InitializeHLS({playoutUrl, authorizationToken, drm, drms, multiviewOptions}) {
|
|
481
517
|
this.HLS = (await import("hls.js")).default;
|
|
482
518
|
|
|
483
|
-
|
|
519
|
+
const nativeHLSSupported = this.video.canPlayType("application/vnd.apple.mpegURL");
|
|
520
|
+
if((nativeHLSSupported && isIOS) || [EluvioPlayerParameters.drms.FAIRPLAY, EluvioPlayerParameters.drms.SAMPLE_AES].includes(drm) || !this.HLS.isSupported()) {
|
|
484
521
|
// HLS JS NOT SUPPORTED - Handle native player
|
|
485
522
|
this.nativeHLS = true;
|
|
486
523
|
|
|
@@ -550,7 +587,7 @@ export class EluvioPlayer {
|
|
|
550
587
|
...customProfileSettings
|
|
551
588
|
};
|
|
552
589
|
|
|
553
|
-
if(this.
|
|
590
|
+
if(this.dvrAvailable) {
|
|
554
591
|
delete this.hlsOptions.liveMaxLatencyDuration;
|
|
555
592
|
delete this.hlsOptions.liveMaxLatencyDurationCount;
|
|
556
593
|
}
|
|
@@ -670,6 +707,14 @@ export class EluvioPlayer {
|
|
|
670
707
|
|
|
671
708
|
this.hlsPlayer = hlsPlayer;
|
|
672
709
|
this.player = hlsPlayer;
|
|
710
|
+
|
|
711
|
+
if(nativeHLSSupported) {
|
|
712
|
+
/* Add alternate source for airplay */
|
|
713
|
+
const source = document.createElement("source");
|
|
714
|
+
source.src = this.playoutUrl.toString();
|
|
715
|
+
this.video.appendChild(source);
|
|
716
|
+
this.video.disableRemotePlayback = false;
|
|
717
|
+
}
|
|
673
718
|
}
|
|
674
719
|
}
|
|
675
720
|
|
|
@@ -981,6 +1026,7 @@ export class EluvioPlayer {
|
|
|
981
1026
|
}
|
|
982
1027
|
|
|
983
1028
|
__DestroyPlayer() {
|
|
1029
|
+
this.castHandler.Disconnect();
|
|
984
1030
|
this.__destroyed = true;
|
|
985
1031
|
this.__Reset();
|
|
986
1032
|
}
|
|
@@ -1014,6 +1060,7 @@ export class EluvioPlayer {
|
|
|
1014
1060
|
}
|
|
1015
1061
|
});
|
|
1016
1062
|
|
|
1063
|
+
this.__settingsListeners = [];
|
|
1017
1064
|
this.__listenerDisposers = [];
|
|
1018
1065
|
this.__showPlayerProfileForm = false;
|
|
1019
1066
|
|
|
@@ -1035,8 +1082,12 @@ export class EluvioPlayer {
|
|
|
1035
1082
|
this.canPlay = false;
|
|
1036
1083
|
this.isLive = false;
|
|
1037
1084
|
this.behindLiveEdge = false;
|
|
1085
|
+
this.dvrAvailable = false;
|
|
1038
1086
|
this.dvrEnabled = false;
|
|
1039
1087
|
this.publicMetadataUrl = undefined;
|
|
1088
|
+
this.airplayAvailable = false;
|
|
1089
|
+
this.chromecastAvailable = false;
|
|
1090
|
+
this.casting = false;
|
|
1040
1091
|
}
|
|
1041
1092
|
|
|
1042
1093
|
async __HardReload(error, delay=6000) {
|
|
@@ -1113,6 +1164,17 @@ export class EluvioPlayer {
|
|
|
1113
1164
|
}
|
|
1114
1165
|
}
|
|
1115
1166
|
}
|
|
1167
|
+
|
|
1168
|
+
__SetCasting(casting) {
|
|
1169
|
+
if(casting) {
|
|
1170
|
+
this.controls.SetDVREnabled(false);
|
|
1171
|
+
this.controls.Pause();
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
this.casting = casting;
|
|
1175
|
+
|
|
1176
|
+
this.__SettingsUpdate();
|
|
1177
|
+
}
|
|
1116
1178
|
}
|
|
1117
1179
|
|
|
1118
1180
|
EluvioPlayer.EluvioPlayerParameters = EluvioPlayerParameters;
|
|
@@ -32,3 +32,5 @@ export const RotateIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\
|
|
|
32
32
|
export const ContentBadgeIcon = `<svg width=\"30\" height=\"30\" viewBox=\"0 0 30 30\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15 27.5C15 27.5 25 22.5 25 15V6.25L15 2.5L5 6.25V15C5 22.5 15 27.5 15 27.5Z\" stroke=\"currentColor\" stroke-width=\"2.14286\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M19.715 11.3574L13.8221 17.8396L11.1436 14.8931\" stroke=\"currentColor\" stroke-width=\"2.14286\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;
|
|
33
33
|
export const ContentCredentialsIcon = `<svg width=\"26\" height=\"26\" viewBox=\"0 0 26 26\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M23.7508 12.9997V23.7508H13.0003C7.05943 23.7508 2.24919 18.9406 2.24919 12.9997C2.24919 7.05888 7.05943 2.24864 13.0003 2.24864C18.9411 2.24864 23.7508 7.05888 23.7508 12.9997ZM0 12.9997C0 5.82396 5.82396 0 12.9997 0C20.1755 0 26 5.82396 26 12.9997V25.9995H13.0003C5.82396 25.9995 0 20.1755 0 12.9997ZM5.174 13.5197C5.174 16.1976 6.98078 18.4599 9.85371 18.4599C12.2198 18.4599 13.8185 16.9 14.2084 14.8589H11.8686C11.5698 15.7951 10.8155 16.367 9.85371 16.367C8.39758 16.367 7.44886 15.2227 7.44886 13.5202C7.44886 11.8178 8.39813 10.6735 9.85371 10.6735C10.7899 10.6735 11.5305 11.2066 11.8429 12.0903H14.1959C13.7797 10.1011 12.1941 8.58055 9.85371 8.58055C6.96822 8.58001 5.174 10.8418 5.174 13.5197ZM17.3419 8.83999H15.132V18.2131H17.4331V13.3253C17.4331 12.4022 17.6931 11.8041 18.1349 11.4273C18.5249 11.0761 19.0318 10.8942 19.8642 10.8942H20.4491V8.72256H19.8773C18.668 8.72256 17.8624 9.16442 17.3424 9.8406V8.82633V8.83999H17.3419Z\" fill=\"currentColor\"/></svg>`;
|
|
34
34
|
export const CopyIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-copy\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path></svg>`;
|
|
35
|
+
export const AirplayIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-airplay\"><path d=\"M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1\"></path><polygon points=\"12 15 17 21 7 21 12 15\"></polygon></svg>`;
|
|
36
|
+
export const ChromecastIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-cast\"><path d=\"M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6\"></path><line x1=\"2\" y1=\"20\" x2=\"2.01\" y2=\"20\"></line></svg>`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-airplay"><path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"></path><polygon points="12 15 17 21 7 21 12 15"></polygon></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cast"><path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path><line x1="2" y1="20" x2="2.01" y2="20"></line></svg>
|
|
@@ -543,6 +543,57 @@
|
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
.dvr-toggle {
|
|
547
|
+
--border-color: #a2a3a3;
|
|
548
|
+
|
|
549
|
+
display: flex;
|
|
550
|
+
height: 30px;
|
|
551
|
+
position: relative;
|
|
552
|
+
|
|
553
|
+
&__border {
|
|
554
|
+
border: 2px solid var(--border-color);
|
|
555
|
+
border-radius: 5px;
|
|
556
|
+
height: 100%;
|
|
557
|
+
inset: 0;
|
|
558
|
+
opacity: 0.5;
|
|
559
|
+
position: absolute;
|
|
560
|
+
z-index: 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
&__live,
|
|
564
|
+
&__dvr {
|
|
565
|
+
align-items: center;
|
|
566
|
+
border-radius: 5px;
|
|
567
|
+
color: var(--border-color);
|
|
568
|
+
cursor: pointer!important;
|
|
569
|
+
display: flex;
|
|
570
|
+
font-size: 16px;
|
|
571
|
+
font-weight: 500;
|
|
572
|
+
height: 100%;
|
|
573
|
+
justify-content: center;
|
|
574
|
+
opacity: 0.5;
|
|
575
|
+
transition: background-color 0.25s ease, color 0.25s ease, opacity 0.25s ease;
|
|
576
|
+
width: 75px;
|
|
577
|
+
z-index: 1;
|
|
578
|
+
|
|
579
|
+
&--active {
|
|
580
|
+
color: var(--color-text);
|
|
581
|
+
opacity: 1;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
&__live {
|
|
586
|
+
&--active {
|
|
587
|
+
background-color: #ea3323;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
&__dvr {
|
|
592
|
+
&--active {
|
|
593
|
+
background-color: #4666ac;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
546
597
|
|
|
547
598
|
/* Player size/orientation adjustments */
|
|
548
599
|
|
|
@@ -597,6 +648,20 @@
|
|
|
597
648
|
height: 15px;
|
|
598
649
|
}
|
|
599
650
|
}
|
|
651
|
+
|
|
652
|
+
.dvr-toggle {
|
|
653
|
+
height: 25px;
|
|
654
|
+
|
|
655
|
+
&__border {
|
|
656
|
+
border-width: 1px;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
&__live,
|
|
660
|
+
&__dvr {
|
|
661
|
+
font-size: 10px;
|
|
662
|
+
width: 40px;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
600
665
|
}
|
|
601
666
|
|
|
602
667
|
:global(.__eluvio-player--size-sm) {
|
|
@@ -89,6 +89,20 @@
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
google-cast-launcher {
|
|
93
|
+
--disconnected-color: var(--color-button)!important;
|
|
94
|
+
--connected-color: var(--color-text-highlight)!important;
|
|
95
|
+
|
|
96
|
+
animation: 0.5s fadein ease!important;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
height: 30px!important;
|
|
99
|
+
width: 30px!important;
|
|
100
|
+
|
|
101
|
+
* {
|
|
102
|
+
transition: color 0.15s ease;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
92
106
|
/* Content Info */
|
|
93
107
|
|
|
94
108
|
.info-container {
|
|
@@ -342,6 +356,7 @@
|
|
|
342
356
|
|
|
343
357
|
.menu-control-container {
|
|
344
358
|
position: relative;
|
|
359
|
+
z-index: var(--layer-menu);
|
|
345
360
|
|
|
346
361
|
.icon-button {
|
|
347
362
|
border: 1px solid transparent;
|
|
@@ -45,6 +45,22 @@
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
.cast-indicator-container {
|
|
49
|
+
align-items: center;
|
|
50
|
+
display: flex;
|
|
51
|
+
inset: 0;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
position: absolute;
|
|
54
|
+
z-index: var(--layer-center-button);
|
|
55
|
+
|
|
56
|
+
svg {
|
|
57
|
+
color: #FFF;
|
|
58
|
+
height: 50px;
|
|
59
|
+
width: 50px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
48
64
|
.controls {
|
|
49
65
|
z-index: var(--layer-controls);
|
|
50
66
|
}
|
package/lib/ui/BuildIcons.cjs
CHANGED
|
@@ -35,6 +35,8 @@ const iconSource = {
|
|
|
35
35
|
ContentBadgeIcon: Path.resolve(__dirname, "../static/icons/svgs/content-badge.svg"),
|
|
36
36
|
ContentCredentialsIcon: Path.resolve(__dirname, "../static/icons/svgs/content-credentials.svg"),
|
|
37
37
|
CopyIcon: Path.resolve(__dirname, "../static/icons/svgs/copy.svg"),
|
|
38
|
+
AirplayIcon: Path.resolve(__dirname, "../static/icons/svgs/airplay.svg"),
|
|
39
|
+
ChromecastIcon: Path.resolve(__dirname, "../static/icons/svgs/cast.svg")
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
let iconFile = "// WARNING: Do not edit this file manually\n"
|
package/lib/ui/Components.jsx
CHANGED
|
@@ -61,23 +61,30 @@ export const UserActionIndicator = ({action}) => {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
export const SeekBar = ({player, videoState, setRecentUserAction, className=""}) => {
|
|
64
|
-
const [currentTime, setCurrentTime] = useState(player.
|
|
64
|
+
const [currentTime, setCurrentTime] = useState(player.controls.GetCurrentTime());
|
|
65
65
|
const [bufferFraction, setBufferFraction] = useState(0);
|
|
66
66
|
const [seekKeydownHandler, setSeekKeydownHandler] = useState(undefined);
|
|
67
|
+
const [dvrEnabled, setDVREnabled] = useState(player.controls.IsDVREnabled());
|
|
67
68
|
|
|
68
69
|
useEffect(() => {
|
|
69
70
|
setSeekKeydownHandler(SeekSliderKeyDown(player, setRecentUserAction));
|
|
70
71
|
|
|
71
|
-
const disposeVideoTimeObserver = ObserveVideoTime({
|
|
72
|
+
const disposeVideoTimeObserver = ObserveVideoTime({player, setCurrentTime, rate: 60});
|
|
72
73
|
const disposeVideoBufferObserver = ObserveVideoBuffer({video: player.video, setBufferFraction});
|
|
74
|
+
const disposeSettingsListener = player.controls.RegisterSettingsListener(() => {
|
|
75
|
+
if(!player.controls) { return; }
|
|
76
|
+
|
|
77
|
+
setDVREnabled(player.controls.IsDVREnabled())
|
|
78
|
+
});
|
|
73
79
|
|
|
74
80
|
return () => {
|
|
75
81
|
disposeVideoTimeObserver && disposeVideoTimeObserver();
|
|
76
82
|
disposeVideoBufferObserver && disposeVideoBufferObserver();
|
|
83
|
+
disposeSettingsListener && disposeSettingsListener();
|
|
77
84
|
};
|
|
78
|
-
}, []);
|
|
85
|
+
}, [player && player.controls]);
|
|
79
86
|
|
|
80
|
-
if(player.isLive && !
|
|
87
|
+
if(player.isLive && !dvrEnabled) {
|
|
81
88
|
return null;
|
|
82
89
|
}
|
|
83
90
|
|
|
@@ -85,7 +92,7 @@ export const SeekBar = ({player, videoState, setRecentUserAction, className=""})
|
|
|
85
92
|
<div className={`${className} ${CommonStyles["seek-container"]} ${className}`}>
|
|
86
93
|
<progress
|
|
87
94
|
max={1}
|
|
88
|
-
value={bufferFraction}
|
|
95
|
+
value={player.casting ? 0 : bufferFraction}
|
|
89
96
|
className={CommonStyles["seek-buffer"]}
|
|
90
97
|
/>
|
|
91
98
|
<progress
|
|
@@ -288,6 +295,38 @@ export const SettingsMenu = ({player, Hide, className=""}) => {
|
|
|
288
295
|
);
|
|
289
296
|
};
|
|
290
297
|
|
|
298
|
+
export const DVRToggle = ({player}) => {
|
|
299
|
+
const [dvrEnabled, setDVREnabled] = useState(player.dvrEnabled);
|
|
300
|
+
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const disposer = player.controls.RegisterSettingsListener(() => {
|
|
303
|
+
if(!player.controls) { return; }
|
|
304
|
+
|
|
305
|
+
setDVREnabled(player.controls.IsDVREnabled())
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return () => disposer && disposer();
|
|
309
|
+
}, [player && player.controls]);
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<div className={CommonStyles["dvr-toggle"]}>
|
|
313
|
+
<button
|
|
314
|
+
onClick={() => player.controls.SetDVREnabled(false)}
|
|
315
|
+
className={`${CommonStyles["dvr-toggle__live"]} ${!dvrEnabled ? CommonStyles["dvr-toggle__live--active"] : ""}`}
|
|
316
|
+
>
|
|
317
|
+
LIVE
|
|
318
|
+
</button>
|
|
319
|
+
<button
|
|
320
|
+
onClick={() => player.controls.SetDVREnabled(true)}
|
|
321
|
+
className={`${CommonStyles["dvr-toggle__dvr"]} ${dvrEnabled ? CommonStyles["dvr-toggle__dvr--active"] : ""}`}
|
|
322
|
+
>
|
|
323
|
+
DVR
|
|
324
|
+
</button>
|
|
325
|
+
<div className={CommonStyles["dvr-toggle__border"]}/>
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
};
|
|
329
|
+
|
|
291
330
|
export const Copy = async (value) => {
|
|
292
331
|
try {
|
|
293
332
|
value = (value || "").toString();
|
package/lib/ui/Observers.js
CHANGED
|
@@ -49,12 +49,14 @@ export const RegisterModal = ({element, Hide}) => {
|
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
export const ObserveVideo = ({
|
|
52
|
+
export const ObserveVideo = ({player, setVideoState}) => {
|
|
53
53
|
const UpdateVideoState = function () {
|
|
54
|
-
|
|
54
|
+
if(!player || !player.controls) { return; }
|
|
55
|
+
|
|
56
|
+
const buffer = player.video.buffered;
|
|
55
57
|
let end = 0;
|
|
56
58
|
for(let i = 0; i < buffer.length; i++) {
|
|
57
|
-
if(buffer.start(i) >
|
|
59
|
+
if(buffer.start(i) > player.controls.GetCurrentTime()) { continue; }
|
|
58
60
|
|
|
59
61
|
if(buffer.end(i) > end) {
|
|
60
62
|
end = buffer.end(i);
|
|
@@ -62,11 +64,11 @@ export const ObserveVideo = ({target, video, setVideoState}) => {
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
setVideoState({
|
|
65
|
-
playing:
|
|
66
|
-
duration:
|
|
67
|
-
volume:
|
|
68
|
-
muted:
|
|
69
|
-
rate:
|
|
67
|
+
playing: player.controls.IsPlaying(),
|
|
68
|
+
duration: player.controls.GetDuration(),
|
|
69
|
+
volume: player.controls.GetVolume(),
|
|
70
|
+
muted: player.controls.IsMuted(),
|
|
71
|
+
rate: player.controls.GetPlaybackRate(),
|
|
70
72
|
fullscreen: !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement)
|
|
71
73
|
});
|
|
72
74
|
};
|
|
@@ -80,12 +82,15 @@ export const ObserveVideo = ({target, video, setVideoState}) => {
|
|
|
80
82
|
"ratechange"
|
|
81
83
|
];
|
|
82
84
|
|
|
83
|
-
events.map(event => video.addEventListener(event, UpdateVideoState));
|
|
84
|
-
target.addEventListener("fullscreenchange", UpdateVideoState);
|
|
85
|
+
events.map(event => player.video.addEventListener(event, UpdateVideoState));
|
|
86
|
+
player.target.addEventListener("fullscreenchange", UpdateVideoState);
|
|
87
|
+
|
|
88
|
+
const castListenerDisposer = player.castHandler.RegisterListener(UpdateVideoState);
|
|
85
89
|
|
|
86
90
|
return () => {
|
|
87
|
-
events.map(event => video.removeEventListener(event, UpdateVideoState));
|
|
88
|
-
target.removeEventListener("fullscreenchange", UpdateVideoState);
|
|
91
|
+
events.map(event => player.video.removeEventListener(event, UpdateVideoState));
|
|
92
|
+
player.target.removeEventListener("fullscreenchange", UpdateVideoState);
|
|
93
|
+
castListenerDisposer && castListenerDisposer();
|
|
89
94
|
};
|
|
90
95
|
};
|
|
91
96
|
|
|
@@ -113,10 +118,12 @@ export const ObserveVideoBuffer = ({video, setBufferFraction}) => {
|
|
|
113
118
|
return () => video.removeEventListener("progress", UpdateBufferState);
|
|
114
119
|
};
|
|
115
120
|
|
|
116
|
-
export const ObserveVideoTime = ({
|
|
121
|
+
export const ObserveVideoTime = ({player, rate=10, setCurrentTime}) => {
|
|
117
122
|
// Current time doesn't update quickly enough from events for smooth movement - use interval instead
|
|
118
123
|
const currentTimeInterval = setInterval(() => {
|
|
119
|
-
|
|
124
|
+
if(!player.controls) { return; }
|
|
125
|
+
|
|
126
|
+
setCurrentTime(player.controls.GetCurrentTime());
|
|
120
127
|
}, 1000 / rate);
|
|
121
128
|
|
|
122
129
|
return () => {
|
|
@@ -334,10 +341,13 @@ export const ObserveKeydown = ({player, setRecentUserAction}) => {
|
|
|
334
341
|
index: playbackRates.active.index + (event.key === "<" ? -1 : 1)
|
|
335
342
|
});
|
|
336
343
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
344
|
+
|
|
345
|
+
if(result) {
|
|
346
|
+
setRecentUserAction({
|
|
347
|
+
action: result.increase ? ACTIONS.PLAYBACK_RATE_UP : ACTIONS.PLAYBACK_RATE_DOWN,
|
|
348
|
+
text: `${result.rate.toFixed(2)}x`
|
|
349
|
+
});
|
|
350
|
+
}
|
|
341
351
|
break;
|
|
342
352
|
case "c":
|
|
343
353
|
result = player.controls.ToggleTextTrack();
|
package/lib/ui/PlayerUI.jsx
CHANGED
|
@@ -19,6 +19,7 @@ import {Spinner, UserActionIndicator} from "./Components.jsx";
|
|
|
19
19
|
import TVControls from "./TVControls.jsx";
|
|
20
20
|
import PlayerProfileForm from "./PlayerProfileForm.jsx";
|
|
21
21
|
import {ImageUrl, MergeDefaultParameters} from "./Common.js";
|
|
22
|
+
import {ChromecastIcon} from "../static/icons/Icons.js";
|
|
22
23
|
|
|
23
24
|
const Poster = ({player}) => {
|
|
24
25
|
const [imageUrl, setImageUrl] = useState(undefined);
|
|
@@ -64,6 +65,7 @@ const PlayerUI = ({target, parameters, InitCallback, ErrorCallback, Unmount, Res
|
|
|
64
65
|
const [recentlyInteracted, setRecentlyInteracted] = useState(true);
|
|
65
66
|
const [recentUserAction, setRecentUserAction] = useState(undefined);
|
|
66
67
|
const [allowRotation, setAllowRotation] = useState(undefined);
|
|
68
|
+
const [casting, setCasting] = useState(false);
|
|
67
69
|
const videoRef = useRef();
|
|
68
70
|
|
|
69
71
|
const playerSet = !!player;
|
|
@@ -110,6 +112,7 @@ const PlayerUI = ({target, parameters, InitCallback, ErrorCallback, Unmount, Res
|
|
|
110
112
|
setPlaybackStarted(newPlayer.playbackStarted);
|
|
111
113
|
setPlayerInitialized(!newPlayer.loading);
|
|
112
114
|
setShowPlayerProfileForm(newPlayer.__showPlayerProfileForm);
|
|
115
|
+
setCasting(newPlayer && newPlayer.casting);
|
|
113
116
|
}
|
|
114
117
|
);
|
|
115
118
|
|
|
@@ -225,6 +228,7 @@ const PlayerUI = ({target, parameters, InitCallback, ErrorCallback, Unmount, Res
|
|
|
225
228
|
muted={[EluvioPlayerParameters.muted.ON, EluvioPlayerParameters.muted.WHEN_NOT_VISIBLE].includes(parameters.playerOptions.muted)}
|
|
226
229
|
controls={parameters.playerOptions.controls === EluvioPlayerParameters.controls.DEFAULT}
|
|
227
230
|
loop={parameters.playerOptions.loop === EluvioPlayerParameters.loop.ON}
|
|
231
|
+
crossOrigin="anonymous"
|
|
228
232
|
className={PlayerStyles.video}
|
|
229
233
|
/>
|
|
230
234
|
{
|
|
@@ -237,6 +241,12 @@ const PlayerUI = ({target, parameters, InitCallback, ErrorCallback, Unmount, Res
|
|
|
237
241
|
<Spinner className={PlayerStyles["spinner"]} />
|
|
238
242
|
</div>
|
|
239
243
|
}
|
|
244
|
+
{
|
|
245
|
+
!casting ? null :
|
|
246
|
+
<div className={PlayerStyles["cast-indicator-container"]}>
|
|
247
|
+
<div dangerouslySetInnerHTML={{ __html: ChromecastIcon }} />
|
|
248
|
+
</div>
|
|
249
|
+
}
|
|
240
250
|
{
|
|
241
251
|
!errorMessage ? null :
|
|
242
252
|
<div className={PlayerStyles["error-message"]}>{ errorMessage }</div>
|
package/lib/ui/TVControls.jsx
CHANGED
|
@@ -9,7 +9,14 @@ import {ImageUrl, PlayerClick, Time} from "./Common.js";
|
|
|
9
9
|
import EluvioPlayerParameters from "../player/PlayerParameters.js";
|
|
10
10
|
|
|
11
11
|
import EluvioLogo from "../static/images/Logo.png";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
CollectionMenu,
|
|
14
|
+
ContentVerificationMenu,
|
|
15
|
+
DVRToggle,
|
|
16
|
+
SeekBar,
|
|
17
|
+
SettingsMenu,
|
|
18
|
+
SVG
|
|
19
|
+
} from "./Components.jsx";
|
|
13
20
|
|
|
14
21
|
export const IconButton = ({icon, ...props}) => {
|
|
15
22
|
return (
|
|
@@ -21,7 +28,7 @@ const TimeIndicator = ({player, videoState}) => {
|
|
|
21
28
|
const [currentTime, setCurrentTime] = useState(player.video.currentTime);
|
|
22
29
|
|
|
23
30
|
useEffect(() => {
|
|
24
|
-
const disposeVideoTimeObserver = ObserveVideoTime({
|
|
31
|
+
const disposeVideoTimeObserver = ObserveVideoTime({player, setCurrentTime, rate: 10});
|
|
25
32
|
|
|
26
33
|
return () => disposeVideoTimeObserver && disposeVideoTimeObserver();
|
|
27
34
|
}, []);
|
|
@@ -250,7 +257,7 @@ const TVControls = ({player, playbackStarted, canPlay, recentlyInteracted, setRe
|
|
|
250
257
|
useEffect(() => {
|
|
251
258
|
setPlayerClickHandler(PlayerClick({player, setRecentUserAction}));
|
|
252
259
|
|
|
253
|
-
const disposeVideoObserver = ObserveVideo({
|
|
260
|
+
const disposeVideoObserver = ObserveVideo({player, setVideoState});
|
|
254
261
|
|
|
255
262
|
return () => disposeVideoObserver && disposeVideoObserver();
|
|
256
263
|
}, []);
|
|
@@ -290,7 +297,7 @@ const TVControls = ({player, playbackStarted, canPlay, recentlyInteracted, setRe
|
|
|
290
297
|
// Take focus off of this button because it should no longer be selectable after playback starts
|
|
291
298
|
player.target.firstChild.focus();
|
|
292
299
|
}}
|
|
293
|
-
className={`${ControlStyles["center-play-button"]} ${ControlStyles["icon-button--drop-shadow-focus"]} ${canPlay && !playbackStarted ? "" : ControlStyles["center-play-button--hidden"]}`}
|
|
300
|
+
className={`${ControlStyles["center-play-button"]} ${ControlStyles["icon-button--drop-shadow-focus"]} ${(canPlay && !playbackStarted && !player.casting) ? "" : ControlStyles["center-play-button--hidden"]}`}
|
|
294
301
|
/>
|
|
295
302
|
{
|
|
296
303
|
showInfo ?
|
|
@@ -309,13 +316,8 @@ const TVControls = ({player, playbackStarted, canPlay, recentlyInteracted, setRe
|
|
|
309
316
|
<div className={ControlStyles["spacer"]}/>
|
|
310
317
|
{
|
|
311
318
|
!player.isLive ? null :
|
|
312
|
-
player.
|
|
313
|
-
<
|
|
314
|
-
onClick={() => player.controls.Seek({time: player.controls.GetDuration() - 2})}
|
|
315
|
-
className={`${ControlStyles["live-indicator"]} ${player.isLive && player.behindLiveEdge ? ControlStyles["live-indicator--faded"] : ""}`}
|
|
316
|
-
>
|
|
317
|
-
Live
|
|
318
|
-
</button> :
|
|
319
|
+
player.controls.IsDVRAvailable() ?
|
|
320
|
+
<DVRToggle player={player} /> :
|
|
319
321
|
<div className={ControlStyles["live-indicator"]}>
|
|
320
322
|
Live
|
|
321
323
|
</div>
|
|
@@ -343,6 +345,18 @@ const TVControls = ({player, playbackStarted, canPlay, recentlyInteracted, setRe
|
|
|
343
345
|
<CenterButtons player={player} videoState={videoState}/>
|
|
344
346
|
<div className={ControlStyles["bottom-right-controls"]}>
|
|
345
347
|
<ContentVerificationControls player={player} />
|
|
348
|
+
{
|
|
349
|
+
!player.airplayAvailable ? null :
|
|
350
|
+
<IconButton
|
|
351
|
+
aria-label="Airplay"
|
|
352
|
+
onClick={() => player.video.webkitShowPlaybackTargetPicker()}
|
|
353
|
+
icon={Icons.AirplayIcon}
|
|
354
|
+
/>
|
|
355
|
+
}
|
|
356
|
+
{
|
|
357
|
+
!player.chromecastAvailable ? null :
|
|
358
|
+
<google-cast-launcher></google-cast-launcher>
|
|
359
|
+
}
|
|
346
360
|
{
|
|
347
361
|
!player.controls.GetOptions().hasAnyOptions ? null :
|
|
348
362
|
<MenuButton
|