@eluvio/elv-player-js 1.0.86 → 1.0.88
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/package.json +1 -1
- package/src/PlayerControls.js +127 -83
- package/src/index.js +317 -146
- package/src/static/icons/arrow-left.svg +4 -0
- package/src/static/stylesheets/player.scss +32 -4
package/package.json
CHANGED
package/src/PlayerControls.js
CHANGED
|
@@ -8,6 +8,7 @@ import MutedIcon from "./static/icons/media/no volume icon.svg";
|
|
|
8
8
|
import VolumeLowIcon from "./static/icons/media/low volume icon.svg";
|
|
9
9
|
import VolumeHighIcon from "./static/icons/media/Volume icon.svg";
|
|
10
10
|
import MultiViewIcon from "./static/icons/multiview.svg";
|
|
11
|
+
import LeftArrow from "./static/icons/arrow-left.svg";
|
|
11
12
|
|
|
12
13
|
import Logo from "./static/images/ELUV.IO white 20 px V2.png";
|
|
13
14
|
|
|
@@ -122,7 +123,6 @@ class PlayerControls {
|
|
|
122
123
|
};
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
this.settingsMenuContent = "none";
|
|
126
126
|
this.timeouts = {};
|
|
127
127
|
this.played = false;
|
|
128
128
|
this.controlsHover = false;
|
|
@@ -487,7 +487,11 @@ class PlayerControls {
|
|
|
487
487
|
classes: ["eluvio-player__controls__slider-container__input", "eluvio-player__controls__progress-slider"]
|
|
488
488
|
});
|
|
489
489
|
|
|
490
|
-
progressSlider.addEventListener("input", () =>
|
|
490
|
+
progressSlider.addEventListener("input", () => {
|
|
491
|
+
if(!this.video.duration) { return; }
|
|
492
|
+
|
|
493
|
+
this.video.currentTime = this.video.duration * parseFloat(progressSlider.value || 0);
|
|
494
|
+
});
|
|
491
495
|
|
|
492
496
|
// Progress Bar
|
|
493
497
|
const progressBar = CreateElement({
|
|
@@ -561,6 +565,7 @@ class PlayerControls {
|
|
|
561
565
|
type: "div",
|
|
562
566
|
classes: ["eluvio-player__controls__settings-menu", "eluvio-player__controls__settings-menu-hidden"]
|
|
563
567
|
});
|
|
568
|
+
this.settingsMenu.setAttribute("data-mode", "hidden");
|
|
564
569
|
|
|
565
570
|
this.target.addEventListener("keydown", event => event && (event.key || "").toLowerCase() === "escape" && this.HideSettingsMenu());
|
|
566
571
|
|
|
@@ -658,95 +663,89 @@ class PlayerControls {
|
|
|
658
663
|
}
|
|
659
664
|
}
|
|
660
665
|
|
|
661
|
-
|
|
662
|
-
if(
|
|
663
|
-
this.settingsButton.focus();
|
|
664
|
-
} else if(this.settingsMenuContent === "multiview") {
|
|
665
|
-
this.multiviewButton.focus();
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
this.settingsMenu.innerHTML = "";
|
|
669
|
-
this.settingsMenu.classList.add("eluvio-player__controls__settings-menu-hidden");
|
|
670
|
-
this.settingsMenuContent = "none";
|
|
671
|
-
}
|
|
666
|
+
AddSetting({Retrieve, Set}) {
|
|
667
|
+
if(!Retrieve) { return; }
|
|
672
668
|
|
|
673
|
-
|
|
674
|
-
this.settingsMenu.innerHTML = "";
|
|
669
|
+
const { label, options } = Retrieve();
|
|
675
670
|
|
|
676
|
-
|
|
677
|
-
this.HideSettingsMenu();
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
671
|
+
const currentOption = options.find(option => option.active);
|
|
680
672
|
|
|
681
|
-
|
|
682
|
-
|
|
673
|
+
const optionSelectionButton = CreateElement({
|
|
674
|
+
parent: this.settingsMenu,
|
|
675
|
+
type: Set ? "button" : "div",
|
|
676
|
+
classes: ["eluvio-player__controls__settings-menu__option"]
|
|
677
|
+
});
|
|
683
678
|
|
|
684
|
-
|
|
685
|
-
if(this.GetLevels) {
|
|
686
|
-
const levels = this.GetLevels();
|
|
679
|
+
optionSelectionButton.innerHTML = currentOption && currentOption.activeLabel || label;
|
|
687
680
|
|
|
688
|
-
|
|
681
|
+
if(Set) {
|
|
682
|
+
optionSelectionButton.addEventListener("click", () => {
|
|
683
|
+
this.settingsMenu.innerHTML = "";
|
|
684
|
+
this.settingsMenu.setAttribute("data-mode", "settings-submenu");
|
|
689
685
|
|
|
690
|
-
|
|
691
|
-
const resolutionButton = CreateElement({
|
|
686
|
+
const backButton = CreateElement({
|
|
692
687
|
parent: this.settingsMenu,
|
|
693
|
-
type:
|
|
694
|
-
classes: ["eluvio-player__controls__settings-menu__option"]
|
|
688
|
+
type: "button",
|
|
689
|
+
classes: ["eluvio-player__controls__settings-menu__option", "eluvio-player__controls__settings-menu__option-back"],
|
|
695
690
|
});
|
|
696
691
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
692
|
+
CreateElement({
|
|
693
|
+
parent: backButton,
|
|
694
|
+
type: "svg"
|
|
695
|
+
}).innerHTML = LeftArrow;
|
|
702
696
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
697
|
+
CreateElement({
|
|
698
|
+
parent: backButton,
|
|
699
|
+
}).innerHTML = label;
|
|
706
700
|
|
|
707
|
-
|
|
701
|
+
backButton.addEventListener("click", () => this.ShowSettingsMenu());
|
|
702
|
+
|
|
703
|
+
options
|
|
704
|
+
.forEach(option => {
|
|
705
|
+
const optionButton = CreateElement({
|
|
708
706
|
parent: this.settingsMenu,
|
|
709
707
|
type: "button",
|
|
710
|
-
classes: ["eluvio-player__controls__settings-menu__option"]
|
|
708
|
+
classes: ["eluvio-player__controls__settings-menu__option", option.active ? "eluvio-player__controls__settings-menu__option-selected" : ""]
|
|
711
709
|
});
|
|
712
710
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
711
|
+
optionButton.innerHTML = option.label;
|
|
712
|
+
|
|
713
|
+
optionButton.addEventListener("click", () => {
|
|
714
|
+
Set(option.index);
|
|
716
715
|
this.HideSettingsMenu();
|
|
717
716
|
});
|
|
718
|
-
|
|
719
|
-
levels
|
|
720
|
-
.sort((a, b) => a.bitrate < b.bitrate ? 1 : -1)
|
|
721
|
-
.forEach(level => {
|
|
722
|
-
const levelOption = CreateElement({
|
|
723
|
-
parent: this.settingsMenu,
|
|
724
|
-
type: "button",
|
|
725
|
-
classes: ["eluvio-player__controls__settings-menu__option", level.active ? "eluvio-player__controls__settings-menu__option-selected" : ""]
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
if(level.audioTrack) {
|
|
729
|
-
levelOption.innerHTML = `${level.bitrate / 1000}kbps`;
|
|
730
|
-
} else {
|
|
731
|
-
levelOption.innerHTML = `${level.resolution} (${(level.bitrate / 1000 / 1000).toFixed(1)}Mbps)`;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
levelOption.addEventListener("click", () => {
|
|
735
|
-
this.SetLevel(level.index);
|
|
736
|
-
this.HideSettingsMenu();
|
|
737
|
-
});
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
// Focus on first element in list when menu opened
|
|
741
|
-
const firstItem = this.settingsMenu.querySelector("button");
|
|
742
|
-
if(firstItem) {
|
|
743
|
-
firstItem.focus();
|
|
744
|
-
}
|
|
745
717
|
});
|
|
718
|
+
|
|
719
|
+
// Focus on first element in list when menu opened
|
|
720
|
+
const firstItem = this.settingsMenu.querySelector("button");
|
|
721
|
+
if(firstItem) {
|
|
722
|
+
firstItem.focus();
|
|
746
723
|
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
ShowSettingsMenu() {
|
|
729
|
+
this.settingsMenu.innerHTML = "";
|
|
730
|
+
this.settingsMenu.classList.remove("eluvio-player__controls__settings-menu-hidden");
|
|
731
|
+
this.settingsMenu.setAttribute("data-mode", "settings");
|
|
732
|
+
|
|
733
|
+
if(this.GetLevels) {
|
|
734
|
+
this.AddSetting({Retrieve: this.GetLevels, Set: this.SetLevel});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if(this.GetAudioTracks) {
|
|
738
|
+
const tracks = (this.GetAudioTracks() || {}).options || [];
|
|
739
|
+
|
|
740
|
+
if(tracks.length > 1) {
|
|
741
|
+
this.AddSetting({Retrieve: this.GetAudioTracks, Set: this.SetAudioTrack});
|
|
747
742
|
}
|
|
748
743
|
}
|
|
749
744
|
|
|
745
|
+
if(this.GetTextTracks) {
|
|
746
|
+
this.AddSetting({Retrieve: this.GetTextTracks, Set: this.SetTextTrack});
|
|
747
|
+
}
|
|
748
|
+
|
|
750
749
|
// Focus on first element in list when menu opened
|
|
751
750
|
const firstItem = this.settingsMenu.querySelector("button");
|
|
752
751
|
if(firstItem) {
|
|
@@ -754,30 +753,75 @@ class PlayerControls {
|
|
|
754
753
|
}
|
|
755
754
|
}
|
|
756
755
|
|
|
757
|
-
|
|
758
|
-
|
|
756
|
+
HideSettingsMenu() {
|
|
757
|
+
const mode = this.settingsMenu.dataset.mode;
|
|
758
|
+
if(mode === "settings") {
|
|
759
|
+
this.settingsButton.focus();
|
|
760
|
+
} else if(mode === "multiview") {
|
|
761
|
+
this.multiviewButton.focus();
|
|
762
|
+
}
|
|
759
763
|
|
|
760
|
-
this.
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
764
|
+
this.settingsMenu.innerHTML = "";
|
|
765
|
+
this.settingsMenu.classList.add("eluvio-player__controls__settings-menu-hidden");
|
|
766
|
+
this.settingsMenu.setAttribute("data-mode", "hidden");
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
UpdateSettings() {
|
|
770
|
+
if(!this.settingsButton) {
|
|
771
|
+
this.settingsButton = CreateImageButton({
|
|
772
|
+
parent: this.rightButtonsContainer,
|
|
773
|
+
svg: SettingsIcon,
|
|
774
|
+
classes: ["eluvio-player__controls__button-settings"],
|
|
775
|
+
prepend: true,
|
|
776
|
+
label: "Settings"
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
this.settingsButton.addEventListener("click", () => {
|
|
780
|
+
this.settingsMenu.dataset.mode === "hidden" ?
|
|
781
|
+
this.ShowSettingsMenu() :
|
|
782
|
+
this.HideSettingsMenu();
|
|
783
|
+
});
|
|
784
|
+
}
|
|
767
785
|
|
|
768
|
-
this.
|
|
786
|
+
if(this.settingsMenu.dataset.mode === "settings") {
|
|
787
|
+
this.ShowSettingsMenu();
|
|
788
|
+
}
|
|
769
789
|
}
|
|
770
790
|
|
|
771
|
-
|
|
791
|
+
SetAudioTrackControls({GetAudioTracks, SetAudioTrack}) {
|
|
772
792
|
if([EluvioPlayerParameters.controls.OFF, EluvioPlayerParameters.controls.OFF_WITH_VOLUME_TOGGLE, EluvioPlayerParameters.controls.DEFAULT].includes(this.playerOptions.controls)) {
|
|
773
793
|
// Controls disabled
|
|
774
794
|
return;
|
|
775
795
|
}
|
|
776
796
|
|
|
777
|
-
this.
|
|
797
|
+
this.GetAudioTracks = GetAudioTracks;
|
|
798
|
+
this.SetAudioTrack = SetAudioTrack;
|
|
799
|
+
|
|
800
|
+
this.UpdateSettings();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
SetTextTrackControls({GetTextTracks, SetTextTrack}) {
|
|
804
|
+
if([EluvioPlayerParameters.controls.OFF, EluvioPlayerParameters.controls.OFF_WITH_VOLUME_TOGGLE, EluvioPlayerParameters.controls.DEFAULT].includes(this.playerOptions.controls)) {
|
|
805
|
+
// Controls disabled
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
this.GetTextTracks = GetTextTracks;
|
|
810
|
+
this.SetTextTrack = SetTextTrack;
|
|
811
|
+
|
|
812
|
+
this.UpdateSettings();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
SetQualityControls({GetLevels, SetLevel}) {
|
|
816
|
+
if([EluvioPlayerParameters.controls.OFF, EluvioPlayerParameters.controls.OFF_WITH_VOLUME_TOGGLE, EluvioPlayerParameters.controls.DEFAULT].includes(this.playerOptions.controls)) {
|
|
817
|
+
// Controls disabled
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
778
820
|
|
|
779
821
|
this.GetLevels = GetLevels;
|
|
780
822
|
this.SetLevel = SetLevel;
|
|
823
|
+
|
|
824
|
+
this.UpdateSettings();
|
|
781
825
|
}
|
|
782
826
|
|
|
783
827
|
InitializeMultiViewControls({AvailableViews, SwitchView}) {
|
|
@@ -823,12 +867,12 @@ class PlayerControls {
|
|
|
823
867
|
async ToggleMultiviewControls({AvailableViews, SwitchView}={}) {
|
|
824
868
|
this.settingsMenu.innerHTML = "";
|
|
825
869
|
|
|
826
|
-
if(this.
|
|
870
|
+
if(this.settingsMenu.dataset.mode === "multiview") {
|
|
827
871
|
this.HideSettingsMenu();
|
|
828
872
|
return;
|
|
829
873
|
}
|
|
830
874
|
|
|
831
|
-
this.
|
|
875
|
+
this.settingsMenu.setAttribute("data-mode", "multiview");
|
|
832
876
|
this.settingsMenu.classList.remove("eluvio-player__controls__settings-menu-hidden");
|
|
833
877
|
|
|
834
878
|
const views = await AvailableViews();
|
package/src/index.js
CHANGED
|
@@ -138,6 +138,8 @@ export class EluvioPlayer {
|
|
|
138
138
|
this.DetectRemoval = this.DetectRemoval.bind(this);
|
|
139
139
|
|
|
140
140
|
this.Initialize(target, parameters);
|
|
141
|
+
|
|
142
|
+
window.EluvioPlayer = this;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
Log(message, error=false) {
|
|
@@ -269,6 +271,7 @@ export class EluvioPlayer {
|
|
|
269
271
|
const imageMetadata = await client.ContentObjectMetadata({
|
|
270
272
|
versionHash: targetHash,
|
|
271
273
|
metadataSubtree: "public",
|
|
274
|
+
authorizationToken: this.sourceOptions.playoutParameters.authorizationToken,
|
|
272
275
|
select: [
|
|
273
276
|
"display_image",
|
|
274
277
|
"asset_metadata/nft/image"
|
|
@@ -319,8 +322,6 @@ export class EluvioPlayer {
|
|
|
319
322
|
offeringURI,
|
|
320
323
|
options
|
|
321
324
|
});
|
|
322
|
-
|
|
323
|
-
window.playoutOptions = this.sourceOptions.playoutOptions;
|
|
324
325
|
}
|
|
325
326
|
} else {
|
|
326
327
|
if(!this.sourceOptions.playoutOptions) {
|
|
@@ -328,8 +329,6 @@ export class EluvioPlayer {
|
|
|
328
329
|
...this.sourceOptions.playoutParameters,
|
|
329
330
|
options
|
|
330
331
|
});
|
|
331
|
-
|
|
332
|
-
window.playoutOptions = this.sourceOptions.playoutOptions;
|
|
333
332
|
}
|
|
334
333
|
}
|
|
335
334
|
|
|
@@ -584,187 +583,359 @@ export class EluvioPlayer {
|
|
|
584
583
|
this.sourceOptions.playoutParameters.authorizationToken ||
|
|
585
584
|
playoutUrl.query(true).authorization;
|
|
586
585
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
InitializeFairPlayStream({playoutOptions: this.sourceOptions.playoutOptions, video: this.video});
|
|
586
|
+
if(protocol === "hls") {
|
|
587
|
+
await this.InitializeHLS({playoutUrl, authorizationToken, drm, drms, multiviewOptions, controlsPromise});
|
|
588
|
+
} else {
|
|
589
|
+
await this.InitializeDash({playoutUrl, authorizationToken, drm, drms, multiviewOptions, controlsPromise});
|
|
590
|
+
}
|
|
593
591
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
592
|
+
if(this.playerOptions.playerCallback) {
|
|
593
|
+
this.playerOptions.playerCallback({
|
|
594
|
+
player: this,
|
|
595
|
+
videoElement: this.video,
|
|
596
|
+
hlsPlayer: this.hlsPlayer,
|
|
597
|
+
dashPlayer: this.dashPlayer,
|
|
598
|
+
posterUrl: this.posterUrl
|
|
599
|
+
});
|
|
600
|
+
}
|
|
597
601
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
playoutUrl.removeQuery("authorization");
|
|
602
|
+
if(this.playerOptions.autoplay === EluvioPlayerParameters.autoplay.ON) {
|
|
603
|
+
this.video.play();
|
|
601
604
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
xhr.setRequestHeader("Authorization", `Bearer ${authorizationToken}`);
|
|
605
|
+
setTimeout(() => {
|
|
606
|
+
if(this.playerOptions.muted === EluvioPlayerParameters.muted.OFF_IF_POSSIBLE && this.video.paused && !this.video.muted) {
|
|
607
|
+
this.video.muted = true;
|
|
608
|
+
this.video.play();
|
|
609
|
+
}
|
|
610
|
+
}, 250);
|
|
611
|
+
}
|
|
610
612
|
|
|
611
|
-
|
|
612
|
-
this.playerOptions.hlsjsOptions.xhrSetup(xhr);
|
|
613
|
-
}
|
|
613
|
+
this.RegisterVisibilityCallback();
|
|
614
614
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
615
|
+
if(this.__destroyed) {
|
|
616
|
+
// If Destroy was called during the initialization process, ensure that the player is properly destroyed
|
|
617
|
+
this.Destroy();
|
|
618
|
+
}
|
|
619
|
+
} catch (error) {
|
|
620
|
+
this.playerOptions.errorCallback(error);
|
|
621
621
|
|
|
622
|
-
|
|
623
|
-
|
|
622
|
+
if(error.status === 500) {
|
|
623
|
+
this.HardReload(error, 10000);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
624
627
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
hlsPlayer.nextLevel = hlsPlayer.currentLevel;
|
|
628
|
-
};
|
|
628
|
+
async InitializeHLS({playoutUrl, authorizationToken, drm, multiviewOptions, controlsPromise}) {
|
|
629
|
+
const HLSPlayer = (await import("hls-fix")).default;
|
|
629
630
|
|
|
630
|
-
|
|
631
|
-
|
|
631
|
+
if(drm === "fairplay") {
|
|
632
|
+
InitializeFairPlayStream({playoutOptions: this.sourceOptions.playoutOptions, video: this.video});
|
|
632
633
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
index,
|
|
638
|
-
active: index === hlsPlayer.currentLevel,
|
|
639
|
-
resolution: level.attrs.RESOLUTION,
|
|
640
|
-
bitrate: level.bitrate,
|
|
641
|
-
audioTrack: !level.videoCodec
|
|
642
|
-
})),
|
|
643
|
-
SetLevel: levelIndex => hlsPlayer.nextLevel = levelIndex
|
|
644
|
-
});
|
|
645
|
-
}
|
|
634
|
+
if(multiviewOptions.enabled) { controlsPromise.then(() => this.controls.InitializeMultiViewControls(multiviewOptions)); }
|
|
635
|
+
this.UpdateTextTracks();
|
|
636
|
+
} else if(!HLSPlayer.isSupported() || drm === "sample-aes") {
|
|
637
|
+
this.video.src = playoutUrl.toString();
|
|
646
638
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
639
|
+
if(multiviewOptions.enabled) { controlsPromise.then(() => this.controls.InitializeMultiViewControls(multiviewOptions)); }
|
|
640
|
+
} else {
|
|
641
|
+
playoutUrl.removeQuery("authorization");
|
|
642
|
+
|
|
643
|
+
// Inject
|
|
644
|
+
const hlsPlayer = new HLSPlayer({
|
|
645
|
+
maxBufferLength: 30,
|
|
646
|
+
maxBufferSize: 300,
|
|
647
|
+
enableWorker: true,
|
|
648
|
+
capLevelToPlayerSize: this.playerOptions.capLevelToPlayerSize,
|
|
649
|
+
xhrSetup: xhr => {
|
|
650
|
+
xhr.setRequestHeader("Authorization", `Bearer ${authorizationToken}`);
|
|
651
|
+
|
|
652
|
+
if((this.playerOptions.hlsjsOptions || {}).xhrSetup) {
|
|
653
|
+
this.playerOptions.hlsjsOptions.xhrSetup(xhr);
|
|
654
|
+
}
|
|
651
655
|
|
|
652
|
-
|
|
653
|
-
|
|
656
|
+
return xhr;
|
|
657
|
+
},
|
|
658
|
+
...(this.playerOptions.hlsjsOptions || {})
|
|
659
|
+
});
|
|
660
|
+
hlsPlayer.loadSource(playoutUrl.toString());
|
|
661
|
+
hlsPlayer.attachMedia(this.video);
|
|
654
662
|
|
|
655
|
-
|
|
656
|
-
|
|
663
|
+
if(multiviewOptions.enabled) {
|
|
664
|
+
const Switch = multiviewOptions.SwitchView;
|
|
657
665
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}, 3000);
|
|
663
|
-
}
|
|
666
|
+
multiviewOptions.SwitchView = async (view) => {
|
|
667
|
+
await Switch(view);
|
|
668
|
+
hlsPlayer.nextLevel = hlsPlayer.currentLevel;
|
|
669
|
+
};
|
|
664
670
|
|
|
665
|
-
|
|
666
|
-
|
|
671
|
+
controlsPromise.then(() => this.controls.InitializeMultiViewControls(multiviewOptions));
|
|
672
|
+
}
|
|
667
673
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
674
|
+
if(this.controls) {
|
|
675
|
+
const UpdateQualityOptions = () => {
|
|
676
|
+
try {
|
|
677
|
+
this.controls.SetQualityControls({
|
|
678
|
+
GetLevels: () => {
|
|
679
|
+
let levels = hlsPlayer.levels
|
|
680
|
+
.map((level, index) => ({
|
|
681
|
+
index,
|
|
682
|
+
active: hlsPlayer.currentLevel === index,
|
|
683
|
+
resolution: level.attrs.RESOLUTION,
|
|
684
|
+
bitrate: level.bitrate,
|
|
685
|
+
audioTrack: !level.videoCodec,
|
|
686
|
+
label:
|
|
687
|
+
level.audioTrack ?
|
|
688
|
+
`${level.bitrate / 1000}kbps` :
|
|
689
|
+
`${level.attrs.RESOLUTION} (${(level.bitrate / 1000 / 1000).toFixed(1)}Mbps)`,
|
|
690
|
+
activeLabel:
|
|
691
|
+
level.audioTrack ?
|
|
692
|
+
`Quality: ${level.bitrate / 1000}kbps` :
|
|
693
|
+
`Quality: ${level.attrs.RESOLUTION}`
|
|
694
|
+
}))
|
|
695
|
+
.sort((a, b) => a.bitrate < b.bitrate ? 1 : -1);
|
|
696
|
+
|
|
697
|
+
levels.unshift({index: -1, label: "Auto"});
|
|
698
|
+
|
|
699
|
+
return {label: "Quality", options: levels};
|
|
700
|
+
},
|
|
701
|
+
SetLevel: levelIndex => {
|
|
702
|
+
hlsPlayer.nextLevel = levelIndex;
|
|
703
|
+
hlsPlayer.streamController.immediateLevelSwitch();
|
|
671
704
|
}
|
|
672
|
-
}
|
|
705
|
+
});
|
|
706
|
+
} catch (error) {
|
|
707
|
+
// eslint-disable-next-line no-console
|
|
708
|
+
console.error("ELUVIO PLAYER:", error);
|
|
673
709
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
hlsPlayer.on(HLSPlayer.Events.SUBTITLE_TRACKS_UPDATED, () => this.UpdateTextTracks());
|
|
713
|
+
hlsPlayer.on(HLSPlayer.Events.LEVEL_LOADED, () => UpdateQualityOptions());
|
|
714
|
+
hlsPlayer.on(HLSPlayer.Events.LEVEL_SWITCHED, () => UpdateQualityOptions());
|
|
715
|
+
hlsPlayer.on(HLSPlayer.Events.SUBTITLE_TRACK_SWITCH, () => this.UpdateTextTracks());
|
|
716
|
+
hlsPlayer.on(HLSPlayer.Events.AUDIO_TRACKS_UPDATED, () => {
|
|
717
|
+
this.controls.SetAudioTrackControls({
|
|
718
|
+
GetAudioTracks: () => {
|
|
719
|
+
const tracks = hlsPlayer.audioTracks.map(track => ({
|
|
720
|
+
index: track.id,
|
|
721
|
+
label: track.name,
|
|
722
|
+
active: track.id === hlsPlayer.audioTrack,
|
|
723
|
+
activeLabel: `Audio: ${track.name}`
|
|
724
|
+
}));
|
|
725
|
+
|
|
726
|
+
return {label: "Audio Track", options: tracks};
|
|
727
|
+
},
|
|
728
|
+
SetAudioTrack: index => {
|
|
729
|
+
hlsPlayer.audioTrack = index;
|
|
730
|
+
hlsPlayer.streamController.immediateLevelSwitch();
|
|
681
731
|
}
|
|
682
|
-
}
|
|
732
|
+
});
|
|
683
733
|
});
|
|
734
|
+
}
|
|
684
735
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
736
|
+
hlsPlayer.on(HLSPlayer.Events.FRAG_LOADED, () => {
|
|
737
|
+
this.errors = 0;
|
|
738
|
+
clearTimeout(this.bufferFullRestartTimeout);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
hlsPlayer.on(HLSPlayer.Events.ERROR, async (event, error) => {
|
|
742
|
+
this.errors += 1;
|
|
743
|
+
|
|
744
|
+
this.Log(`Encountered ${error.details}`);
|
|
745
|
+
this.Log(error);
|
|
746
|
+
|
|
747
|
+
if(error.details === "bufferFullError") {
|
|
748
|
+
this.bufferFullRestartTimeout = setTimeout(() => {
|
|
749
|
+
this.Log("Buffer full error - Restarting player", true);
|
|
750
|
+
this.HardReload(error, 5000);
|
|
751
|
+
}, 3000);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if(error.details === "bufferStalledError") {
|
|
755
|
+
const stallTime = this.video.currentTime;
|
|
756
|
+
|
|
757
|
+
setTimeout(() => {
|
|
758
|
+
if(!this.video.paused && this.video.currentTime === stallTime) {
|
|
759
|
+
this.Log("Buffer stalled error, no progress in 5 seconds - Restarting player", true);
|
|
696
760
|
}
|
|
697
|
-
});
|
|
761
|
+
}, 5000);
|
|
698
762
|
}
|
|
699
763
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
764
|
+
if(error.fatal || this.errors === 3) {
|
|
765
|
+
if(error.response.code === 403) {
|
|
766
|
+
// Not allowed to access
|
|
767
|
+
this.Destroy();
|
|
768
|
+
} else {
|
|
769
|
+
this.HardReload(error);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
});
|
|
705
773
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
});
|
|
774
|
+
this.hlsPlayer = hlsPlayer;
|
|
775
|
+
this.player = hlsPlayer;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
711
778
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
779
|
+
async InitializeDash({playoutUrl, authorizationToken, drm, drms, multiviewOptions, controlsPromise}) {
|
|
780
|
+
const DashPlayer = (await import("dashjs")).default;
|
|
781
|
+
const dashPlayer = DashPlayer.MediaPlayer().create();
|
|
715
782
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
783
|
+
if(this.playerOptions.capLevelToPlayerSize) {
|
|
784
|
+
dashPlayer.updateSettings({
|
|
785
|
+
"streaming": {
|
|
786
|
+
"fastSwitchEnabled": true,
|
|
787
|
+
"abr": {
|
|
788
|
+
"limitBitrateByPortal": true
|
|
789
|
+
}
|
|
721
790
|
}
|
|
791
|
+
});
|
|
792
|
+
}
|
|
722
793
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
794
|
+
playoutUrl.removeQuery("authorization");
|
|
795
|
+
dashPlayer.extend("RequestModifier", function () {
|
|
796
|
+
return {
|
|
797
|
+
modifyRequestHeader: xhr => {
|
|
798
|
+
xhr.setRequestHeader("Authorization", `Bearer ${authorizationToken}`);
|
|
728
799
|
|
|
729
|
-
|
|
800
|
+
return xhr;
|
|
801
|
+
},
|
|
802
|
+
modifyRequestURL: url => url
|
|
803
|
+
};
|
|
804
|
+
});
|
|
730
805
|
|
|
731
|
-
|
|
732
|
-
|
|
806
|
+
// Widevine
|
|
807
|
+
if(drm === EluvioPlayerParameters.drms.WIDEVINE) {
|
|
808
|
+
const widevineUrl = drms.widevine.licenseServers[0];
|
|
733
809
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
810
|
+
dashPlayer.setProtectionData({
|
|
811
|
+
"com.widevine.alpha": {
|
|
812
|
+
"serverURL": widevineUrl
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
dashPlayer.initialize(
|
|
818
|
+
this.video,
|
|
819
|
+
playoutUrl.toString(),
|
|
820
|
+
this.playerOptions.autoplay === EluvioPlayerParameters.autoplay.ON
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
if(multiviewOptions.enabled) { controlsPromise.then(() => this.controls.InitializeMultiViewControls(multiviewOptions)); }
|
|
824
|
+
|
|
825
|
+
const UpdateQualityOptions = () => {
|
|
826
|
+
try {
|
|
827
|
+
this.controls.SetQualityControls({
|
|
828
|
+
GetLevels: () => {
|
|
829
|
+
let levels = dashPlayer.getBitrateInfoListFor("video")
|
|
830
|
+
.map((level) => ({
|
|
831
|
+
index: level.qualityIndex,
|
|
832
|
+
active: level.qualityIndex === this.player.getQualityFor("video"),
|
|
833
|
+
resolution: `${level.width}x${level.height}`,
|
|
834
|
+
bitrate: level.bitrate,
|
|
835
|
+
label: `${level.width}x${level.height} (${(level.bitrate / 1000 / 1000).toFixed(1)}Mbps)`,
|
|
836
|
+
activeLabel: `Quality: ${level.width}x${level.height}`,
|
|
837
|
+
}))
|
|
838
|
+
.sort((a, b) => a.bitrate < b.bitrate ? 1 : -1);
|
|
839
|
+
|
|
840
|
+
levels.unshift({index: -1, label: "Auto"});
|
|
841
|
+
|
|
842
|
+
return { label: "Quality", options: levels };
|
|
843
|
+
},
|
|
844
|
+
SetLevel: levelIndex => {
|
|
845
|
+
dashPlayer.setQualityFor("video", levelIndex);
|
|
846
|
+
dashPlayer.updateSettings({
|
|
847
|
+
streaming: {
|
|
848
|
+
trackSwitchMode: "alwaysReplace",
|
|
849
|
+
fastSwitchEnabled: true,
|
|
850
|
+
abr: {
|
|
851
|
+
autoSwitchBitrate: {
|
|
852
|
+
video: levelIndex === -1
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
741
858
|
});
|
|
859
|
+
} catch (error) {
|
|
860
|
+
// eslint-disable-next-line no-console
|
|
861
|
+
console.error("ELUVIO PLAYER:", error);
|
|
742
862
|
}
|
|
863
|
+
};
|
|
743
864
|
|
|
744
|
-
|
|
745
|
-
|
|
865
|
+
const UpdateAudioTracks = () => {
|
|
866
|
+
this.controls.SetAudioTrackControls({
|
|
867
|
+
GetAudioTracks: () => {
|
|
868
|
+
const tracks = this.player.getTracksFor("audio").map(track => ({
|
|
869
|
+
index: track.index,
|
|
870
|
+
label: track.labels && track.labels.length > 0 ? track.labels[0].text : track.lang,
|
|
871
|
+
active: track.index === dashPlayer.getCurrentTrackFor("audio").index,
|
|
872
|
+
activeLabel: `Audio: ${track.labels && track.labels.length > 0 ? track.labels[0].text : track.lang}`
|
|
873
|
+
}));
|
|
874
|
+
|
|
875
|
+
return { label: "Audio Track", options: tracks };
|
|
876
|
+
},
|
|
877
|
+
SetAudioTrack: index => {
|
|
878
|
+
const track = dashPlayer.getTracksFor("audio").find(track => track.index === index);
|
|
879
|
+
dashPlayer.setCurrentTrack(track);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
};
|
|
746
883
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
884
|
+
dashPlayer.on(DashPlayer.MediaPlayer.events.QUALITY_CHANGE_RENDERED, () => UpdateQualityOptions());
|
|
885
|
+
dashPlayer.on(DashPlayer.MediaPlayer.events.TRACK_CHANGE_RENDERED, () => {
|
|
886
|
+
UpdateAudioTracks();
|
|
887
|
+
this.UpdateTextTracks({dashPlayer});
|
|
888
|
+
});
|
|
889
|
+
dashPlayer.on(DashPlayer.MediaPlayer.events.MANIFEST_LOADED, () => {
|
|
890
|
+
UpdateQualityOptions();
|
|
891
|
+
UpdateAudioTracks();
|
|
892
|
+
this.UpdateTextTracks({dashPlayer});
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
this.player = dashPlayer;
|
|
896
|
+
this.dashPlayer = dashPlayer;
|
|
897
|
+
}
|
|
754
898
|
|
|
755
|
-
|
|
899
|
+
UpdateTextTracks({dashPlayer}={}) {
|
|
900
|
+
this.controls.SetTextTrackControls({
|
|
901
|
+
GetTextTracks: () => {
|
|
902
|
+
const activeTrackIndex = Array.from(this.video.textTracks).findIndex(track => track.mode === "showing");
|
|
903
|
+
|
|
904
|
+
let tracks;
|
|
905
|
+
if(dashPlayer) {
|
|
906
|
+
tracks = dashPlayer.getTracksFor("text").map((track, index) => ({
|
|
907
|
+
index: index,
|
|
908
|
+
label: track.labels && track.labels.length > 0 ? track.labels[0].text : track.lang,
|
|
909
|
+
active: index === activeTrackIndex,
|
|
910
|
+
activeLabel: `Subtitles: ${track.labels && track.labels.length > 0 ? track.labels[0].text : track.lang}`
|
|
911
|
+
}));
|
|
912
|
+
} else {
|
|
913
|
+
tracks = Array.from(this.video.textTracks).map((track, index) => ({
|
|
914
|
+
index: index,
|
|
915
|
+
label: track.label || track.language,
|
|
916
|
+
active: track.mode === "showing",
|
|
917
|
+
activeLabel: `Subtitles: ${track.label || track.language}`
|
|
918
|
+
}));
|
|
919
|
+
}
|
|
756
920
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
921
|
+
tracks.unshift({
|
|
922
|
+
index: -1,
|
|
923
|
+
label: "Disabled",
|
|
924
|
+
active: activeTrackIndex < 0,
|
|
925
|
+
activeLabel: "Subtitles: Disabled"
|
|
926
|
+
});
|
|
763
927
|
|
|
764
|
-
|
|
765
|
-
|
|
928
|
+
return { label: "Subtitles", options: tracks };
|
|
929
|
+
},
|
|
930
|
+
SetTextTrack: index => {
|
|
931
|
+
const tracks = Array.from(this.video.textTracks);
|
|
932
|
+
tracks.map(track => track.mode = "disabled");
|
|
933
|
+
|
|
934
|
+
if(index >= 0) {
|
|
935
|
+
tracks[index].mode = "showing";
|
|
936
|
+
}
|
|
766
937
|
}
|
|
767
|
-
}
|
|
938
|
+
});
|
|
768
939
|
}
|
|
769
940
|
}
|
|
770
941
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="25" height="16" viewBox="0 0 25 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M1.5 8L24 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M8.5 1L1.5 8L8.5 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -6,6 +6,8 @@ $black: #000;
|
|
|
6
6
|
|
|
7
7
|
$background-color: $black;
|
|
8
8
|
$controls-color: rgba(0, 0, 0, 0.4);
|
|
9
|
+
$menu-color: rgba(0, 0, 0, 0.8);
|
|
10
|
+
$menu-active-color: rgba(255, 255, 255, 0.1);
|
|
9
11
|
$button-color: rgba($white, 0.8);
|
|
10
12
|
$progress-color: #0885fb;
|
|
11
13
|
$slider-color: rgba($gray, 0.5);
|
|
@@ -343,7 +345,7 @@ $button-height: 35px;
|
|
|
343
345
|
}
|
|
344
346
|
|
|
345
347
|
&__settings-menu {
|
|
346
|
-
background: $
|
|
348
|
+
background: $menu-color;
|
|
347
349
|
border-radius: 3px;
|
|
348
350
|
bottom: 45px;
|
|
349
351
|
color: $button-color;
|
|
@@ -352,7 +354,7 @@ $button-height: 35px;
|
|
|
352
354
|
overflow-y: auto;
|
|
353
355
|
position: absolute;
|
|
354
356
|
right: 10px;
|
|
355
|
-
width:
|
|
357
|
+
width: 250px;
|
|
356
358
|
z-index: 100;
|
|
357
359
|
|
|
358
360
|
&-hidden {
|
|
@@ -369,16 +371,34 @@ $button-height: 35px;
|
|
|
369
371
|
cursor: pointer;
|
|
370
372
|
display: block;
|
|
371
373
|
font-size: 14px;
|
|
372
|
-
height:
|
|
374
|
+
height: max-content;
|
|
375
|
+
line-height: 1.4;
|
|
373
376
|
padding: 9px 20px;
|
|
377
|
+
text-align: left;
|
|
374
378
|
width: 100%;
|
|
375
379
|
|
|
380
|
+
&-back {
|
|
381
|
+
display: flex;
|
|
382
|
+
font-weight: 500;
|
|
383
|
+
width: 100%;
|
|
384
|
+
|
|
385
|
+
svg {
|
|
386
|
+
height: 15px;
|
|
387
|
+
margin-right: 5px;
|
|
388
|
+
width: 15px;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
div {
|
|
392
|
+
width: 100%;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
376
396
|
&-selected {
|
|
377
397
|
color: $button-color;
|
|
378
398
|
}
|
|
379
399
|
|
|
380
400
|
&:hover {
|
|
381
|
-
background: $
|
|
401
|
+
background: $menu-active-color;
|
|
382
402
|
color: $button-color;
|
|
383
403
|
}
|
|
384
404
|
|
|
@@ -647,6 +667,14 @@ $button-height: 35px;
|
|
|
647
667
|
}
|
|
648
668
|
}
|
|
649
669
|
|
|
670
|
+
.eluvio-player__controls__settings-menu {
|
|
671
|
+
height: 100%;
|
|
672
|
+
padding: 20px 0;
|
|
673
|
+
right: 0;
|
|
674
|
+
top: 0;
|
|
675
|
+
width: 100%;
|
|
676
|
+
}
|
|
677
|
+
|
|
650
678
|
.eluvio-player__hotspot-overlay {
|
|
651
679
|
.eluvio-player__hotspot-overlay__target {
|
|
652
680
|
.eluvio-player__hotspot-overlay__target__title {
|