@eluvio/elv-player-js 2.0.21 → 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-DdX0-7dQ.mjs → Analytics-B72tGTQx.mjs} +1 -1
- package/dist/{Analytics-B6MHUb3Q.js → Analytics-CDXMirgj.js} +1 -1
- package/dist/{dash.all.min-BgLUy4QY.js → dash.all.min-3hdqLcqd.js} +1 -1
- package/dist/{dash.all.min-DkmG-Qxu.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-BhYSs-gw.mjs → index-BrZn3sD8.mjs} +77 -79
- package/dist/{index-Clf0EQQK.mjs → index-BxTtC119.mjs} +19599 -19553
- package/dist/{index-MQ4UPeZq.js → index-CO76930D.js} +3 -3
- package/lib/player/Cast.js +280 -0
- package/lib/player/Controls.js +103 -5
- package/lib/player/Player.js +84 -20
- 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-4uggw66b.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,8 +303,12 @@ export class EluvioPlayer {
|
|
|
294
303
|
|
|
295
304
|
this.__Reset();
|
|
296
305
|
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
this.initialized = false;
|
|
297
309
|
this.loading = true;
|
|
298
310
|
this.initTime = Date.now();
|
|
311
|
+
this.restartParameters = restartParameters;
|
|
299
312
|
|
|
300
313
|
this.__SettingsUpdate();
|
|
301
314
|
|
|
@@ -329,6 +342,24 @@ export class EluvioPlayer {
|
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
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
|
+
|
|
332
363
|
this.__RegisterVideoEventListener("play", () => {
|
|
333
364
|
this.reloads = 0;
|
|
334
365
|
this.playbackStarted = true;
|
|
@@ -348,7 +379,7 @@ export class EluvioPlayer {
|
|
|
348
379
|
});
|
|
349
380
|
|
|
350
381
|
this.__RegisterVideoEventListener("seeking", () => {
|
|
351
|
-
if(!this.isLive || !this.
|
|
382
|
+
if(!this.isLive || !this.dvrAvailable) { return; }
|
|
352
383
|
const diff = this.video.duration - this.video.currentTime;
|
|
353
384
|
this.behindLiveEdge = diff > 15;
|
|
354
385
|
this.__SettingsUpdate();
|
|
@@ -361,7 +392,7 @@ export class EluvioPlayer {
|
|
|
361
392
|
this.__RegisterVideoEventListener("ended", () => this.controls && this.controls.CollectionPlayNext({autoplay: true}));
|
|
362
393
|
}
|
|
363
394
|
|
|
364
|
-
let { versionHash, playoutUrl, protocol, drm, drms, multiviewOptions } = await this.__PlayoutOptions();
|
|
395
|
+
let { versionHash, playoutUrl, protocol, drm, drms, multiviewOptions, playoutParameters } = await this.__PlayoutOptions();
|
|
365
396
|
|
|
366
397
|
this.contentHash = versionHash;
|
|
367
398
|
|
|
@@ -374,6 +405,13 @@ export class EluvioPlayer {
|
|
|
374
405
|
|
|
375
406
|
this.authorizationToken = authorizationToken;
|
|
376
407
|
|
|
408
|
+
this.playoutUrl = playoutUrl.toString();
|
|
409
|
+
|
|
410
|
+
this.castHandler.SetMedia({
|
|
411
|
+
playoutOptions: this.sourceOptions.playoutOptions,
|
|
412
|
+
playoutParameters
|
|
413
|
+
});
|
|
414
|
+
|
|
377
415
|
if(this.__destroyed) { return; }
|
|
378
416
|
|
|
379
417
|
if(protocol === "hls") {
|
|
@@ -422,6 +460,9 @@ export class EluvioPlayer {
|
|
|
422
460
|
// If Destroy was called during the initialization process, ensure that the player is properly destroyed
|
|
423
461
|
this.__DestroyPlayer();
|
|
424
462
|
}
|
|
463
|
+
|
|
464
|
+
this.initialized = true;
|
|
465
|
+
this.restartParameters = undefined;
|
|
425
466
|
} catch (error) {
|
|
426
467
|
// If playout failed due to a permission issue, check the content to see if there is a message to display
|
|
427
468
|
let permissionErrorMessage;
|
|
@@ -460,7 +501,7 @@ export class EluvioPlayer {
|
|
|
460
501
|
} else if(error.status >= 500) {
|
|
461
502
|
this.__HardReload(error, 10000);
|
|
462
503
|
} else {
|
|
463
|
-
this.
|
|
504
|
+
this.__HardReload(error, 20000);
|
|
464
505
|
}
|
|
465
506
|
|
|
466
507
|
if(this.playerOptions.errorCallback) {
|
|
@@ -475,7 +516,8 @@ export class EluvioPlayer {
|
|
|
475
516
|
async __InitializeHLS({playoutUrl, authorizationToken, drm, drms, multiviewOptions}) {
|
|
476
517
|
this.HLS = (await import("hls.js")).default;
|
|
477
518
|
|
|
478
|
-
|
|
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()) {
|
|
479
521
|
// HLS JS NOT SUPPORTED - Handle native player
|
|
480
522
|
this.nativeHLS = true;
|
|
481
523
|
|
|
@@ -545,7 +587,7 @@ export class EluvioPlayer {
|
|
|
545
587
|
...customProfileSettings
|
|
546
588
|
};
|
|
547
589
|
|
|
548
|
-
if(this.
|
|
590
|
+
if(this.dvrAvailable) {
|
|
549
591
|
delete this.hlsOptions.liveMaxLatencyDuration;
|
|
550
592
|
delete this.hlsOptions.liveMaxLatencyDurationCount;
|
|
551
593
|
}
|
|
@@ -665,6 +707,14 @@ export class EluvioPlayer {
|
|
|
665
707
|
|
|
666
708
|
this.hlsPlayer = hlsPlayer;
|
|
667
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
|
+
}
|
|
668
718
|
}
|
|
669
719
|
}
|
|
670
720
|
|
|
@@ -757,12 +807,9 @@ export class EluvioPlayer {
|
|
|
757
807
|
]
|
|
758
808
|
.map(event => dashPlayer.on(event, () => this.__SettingsUpdate()));
|
|
759
809
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
this.__SettingsUpdate();
|
|
765
|
-
});
|
|
810
|
+
this.video.textTracks.addEventListener("change", () =>
|
|
811
|
+
this.__SettingsUpdate()
|
|
812
|
+
);
|
|
766
813
|
|
|
767
814
|
this.player = dashPlayer;
|
|
768
815
|
this.dashPlayer = dashPlayer;
|
|
@@ -979,6 +1026,7 @@ export class EluvioPlayer {
|
|
|
979
1026
|
}
|
|
980
1027
|
|
|
981
1028
|
__DestroyPlayer() {
|
|
1029
|
+
this.castHandler.Disconnect();
|
|
982
1030
|
this.__destroyed = true;
|
|
983
1031
|
this.__Reset();
|
|
984
1032
|
}
|
|
@@ -1012,6 +1060,7 @@ export class EluvioPlayer {
|
|
|
1012
1060
|
}
|
|
1013
1061
|
});
|
|
1014
1062
|
|
|
1063
|
+
this.__settingsListeners = [];
|
|
1015
1064
|
this.__listenerDisposers = [];
|
|
1016
1065
|
this.__showPlayerProfileForm = false;
|
|
1017
1066
|
|
|
@@ -1033,8 +1082,12 @@ export class EluvioPlayer {
|
|
|
1033
1082
|
this.canPlay = false;
|
|
1034
1083
|
this.isLive = false;
|
|
1035
1084
|
this.behindLiveEdge = false;
|
|
1085
|
+
this.dvrAvailable = false;
|
|
1036
1086
|
this.dvrEnabled = false;
|
|
1037
1087
|
this.publicMetadataUrl = undefined;
|
|
1088
|
+
this.airplayAvailable = false;
|
|
1089
|
+
this.chromecastAvailable = false;
|
|
1090
|
+
this.casting = false;
|
|
1038
1091
|
}
|
|
1039
1092
|
|
|
1040
1093
|
async __HardReload(error, delay=6000) {
|
|
@@ -1087,7 +1140,7 @@ export class EluvioPlayer {
|
|
|
1087
1140
|
this.SetErrorMessage(undefined);
|
|
1088
1141
|
this.__Initialize(
|
|
1089
1142
|
this.originalParameters,
|
|
1090
|
-
!this.video ?
|
|
1143
|
+
!this.video || !this.initialized ? this.restartParameters :
|
|
1091
1144
|
{
|
|
1092
1145
|
muted: this.video.muted,
|
|
1093
1146
|
volume: this.video.volume,
|
|
@@ -1111,6 +1164,17 @@ export class EluvioPlayer {
|
|
|
1111
1164
|
}
|
|
1112
1165
|
}
|
|
1113
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
|
+
}
|
|
1114
1178
|
}
|
|
1115
1179
|
|
|
1116
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>
|