@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.
@@ -0,0 +1,280 @@
1
+ /* eslint-disable no-undef */
2
+
3
+ class Cast {
4
+ constructor({player, onReady, onUpdate}) {
5
+ this.player = player;
6
+ this.ready = false;
7
+ this.onReady = onReady;
8
+ this.connected = false;
9
+ this.playbackRate = 1;
10
+
11
+ window["__onGCastApiAvailable"] = isAvailable =>
12
+ isAvailable && this.Initialize();
13
+
14
+ window.CastController = this;
15
+
16
+ this.__listeners = [];
17
+
18
+ this.onUpdate = onUpdate;
19
+
20
+ this.LoadScript();
21
+ }
22
+
23
+ LoadScript() {
24
+ const tag = document.createElement("script");
25
+ tag.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
26
+ document.querySelector("head").appendChild(tag);
27
+ }
28
+
29
+ Initialize() {
30
+ cast.framework.CastContext.getInstance().setOptions({
31
+ receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
32
+ //receiverApplicationId: "8E2BD113",
33
+ autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
34
+ });
35
+
36
+ this.remotePlayer = new cast.framework.RemotePlayer();
37
+ this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer);
38
+
39
+ this.remotePlayerController.addEventListener(
40
+ cast.framework.RemotePlayerEventType.ANY_CHANGE,
41
+ event => this.Update(event)
42
+ );
43
+
44
+ const context = cast.framework.CastContext.getInstance();
45
+ context.addEventListener(
46
+ cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
47
+ event => {
48
+ switch (event.sessionState) {
49
+ case cast.framework.SessionState.SESSION_STARTED:
50
+ this.Start();
51
+ break;
52
+ case cast.framework.SessionState.SESSION_RESUMED:
53
+ break;
54
+ case cast.framework.SessionState.SESSION_ENDED:
55
+ this.Disconnect(true);
56
+ // Update locally as necessary
57
+ break;
58
+ }
59
+ });
60
+
61
+ this.ready = true;
62
+
63
+ if(this.playoutUrl) {
64
+ this.onReady && this.onReady();
65
+ }
66
+ }
67
+
68
+ async SetMedia({playoutOptions, playoutParameters}) {
69
+ // Get dash options
70
+ let dashOptions = ((playoutOptions || {}).dash || {}).playoutMethods || {};
71
+ let options = dashOptions.clear || dashOptions.widevine;
72
+
73
+ if(!options) {
74
+ // Dash options might be available under the default_dash offering
75
+ try {
76
+ playoutOptions = await (await this.player.__Client()).PlayoutOptions({
77
+ ...playoutParameters,
78
+ offering: "default_dash"
79
+ });
80
+
81
+ dashOptions = ((playoutOptions || {}).dash || {}).playoutMethods || {};
82
+ options = dashOptions.clear || dashOptions.widevine || {};
83
+ } catch(error) {
84
+ this.player.Log("Unable to find dash playout options for chromecast");
85
+ }
86
+ }
87
+
88
+ this.playoutUrl = (options || {}).playoutUrl;
89
+
90
+ if(this.playoutUrl && this.ready) {
91
+ this.onReady && this.onReady();
92
+ }
93
+ }
94
+
95
+ async Start() {
96
+ this.player && this.player.__SetCasting(true);
97
+
98
+ window.addEventListener("beforeunload", () => this.Disconnect(true));
99
+
100
+ try {
101
+ const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
102
+ const mediaInfo = new chrome.cast.media.MediaInfo(this.playoutUrl, "application/dash+xml");
103
+
104
+ mediaInfo.contentUrl = this.playoutUrl;
105
+ mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
106
+ mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
107
+ mediaInfo.metadata.title = "Test Chromecast";
108
+
109
+ let { title, subtitle, image, headers } = (this.player.controls.GetContentInfo() || {});
110
+ mediaInfo.metadata.title = title || "Eluvio";
111
+
112
+ if(!subtitle && headers && headers.length > 0) {
113
+ subtitle = headers.join("‎ ‎ ‎ ‎ ");
114
+ }
115
+
116
+ mediaInfo.metadata.subtitle = subtitle || "";
117
+
118
+ if(image) {
119
+ mediaInfo.metadata.images = [
120
+ new chrome.cast.Image(image)
121
+ ];
122
+ }
123
+
124
+ const request = new chrome.cast.media.LoadRequest(mediaInfo);
125
+ await castSession.loadMedia(request);
126
+
127
+ this.connected = true;
128
+ } catch(error) {
129
+ this.player.Log("Failed to start chromecast stream:", true);
130
+ this.player.Log(error, true);
131
+ this.Disconnect(true);
132
+ }
133
+ }
134
+
135
+ /* Controls */
136
+ IsPlaying() {
137
+ return !this.remotePlayer.isPaused;
138
+ }
139
+
140
+ TogglePlay() {
141
+ this.remotePlayerController.playOrPause();
142
+ }
143
+
144
+ GetCurrentTime() {
145
+ return this.remotePlayer.currentTime;
146
+ }
147
+
148
+ GetDuration() {
149
+ return this.remotePlayer.duration || this.player.video.duration;
150
+ }
151
+
152
+ Play() {
153
+ this.remotePlayer.isPaused && this.remotePlayerController.playOrPause();
154
+ }
155
+
156
+ Pause() {
157
+ !this.remotePlayer.isPaused && this.remotePlayerController.playOrPause();
158
+ }
159
+
160
+ Stop() {
161
+ this.remotePlayerController.stop();
162
+ }
163
+
164
+ IsMuted() {
165
+ return this.remotePlayer.isMuted;
166
+ }
167
+
168
+ Mute() {
169
+ !this.remotePlayer.isMuted && this.remotePlayerController.muteOrUnmute();
170
+ }
171
+
172
+ Unmute() {
173
+ this.remotePlayer.isMuted && this.remotePlayerController.muteOrUnmute();
174
+ }
175
+
176
+ ToggleMuted() {
177
+ this.remotePlayerController.muteOrUnmute();
178
+ }
179
+
180
+ GetVolume() {
181
+ return this.remotePlayer.volumeLevel;
182
+ }
183
+
184
+ SetVolume({fraction, relativeFraction}) {
185
+ let volume;
186
+ if(relativeFraction) {
187
+ volume = Math.min(1, Math.max(0, this.remotePlayer.volume + relativeFraction));
188
+ } else {
189
+ volume = fraction;
190
+ }
191
+
192
+ if(this.player.video.volume > 0) {
193
+ this.remotePlayer.isMuted && this.remotePlayerController.muteOrUnmute();
194
+ }
195
+
196
+ this.remotePlayer.volumeLevel = volume;
197
+ this.remotePlayerController.setVolumeLevel();
198
+
199
+ return volume;
200
+ }
201
+
202
+ Seek({fraction, time, relativeSeconds}) {
203
+ const originalTime = this.remotePlayer.currentTime;
204
+
205
+ if(relativeSeconds) {
206
+ time = Math.max(
207
+ 0,
208
+ Math.min(
209
+ this.remotePlayer.duration,
210
+ this.remotePlayer.currentTime + relativeSeconds
211
+ )
212
+ );
213
+ } else if(typeof fraction !== "undefined") {
214
+ time = this.remotePlayer.duration * fraction;
215
+ }
216
+
217
+ this.remotePlayer.currentTime = time;
218
+ this.remotePlayerController.seek();
219
+
220
+ return originalTime <= this.player.video.currentTime;
221
+ }
222
+
223
+ GetPlaybackRate() {
224
+ return this.playbackRate || 1;
225
+ }
226
+
227
+ SetPlaybackRate({index, rate}) {
228
+ if(typeof index !== "undefined") {
229
+ const option = this.player.controls.GetPlaybackRates().options[index];
230
+
231
+ if(option) {
232
+ rate = option.rate || 1;
233
+ }
234
+ }
235
+
236
+ const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
237
+ const media = castSession.getMediaSession();
238
+ castSession.sendMessage("urn:x-cast:com.google.cast.media",{
239
+ type: "SET_PLAYBACK_RATE",
240
+ playbackRate: rate,
241
+ mediaSessionId: media.mediaSessionId,
242
+ requestId: 2
243
+ })
244
+ .then(() => this.playbackRate = rate);
245
+ }
246
+
247
+ Disconnect(force) {
248
+ (this.connected || force) && cast.framework.CastContext.getInstance().endCurrentSession();
249
+ this.connected = false;
250
+ this.player && this.player.__SetCasting(false);
251
+ }
252
+
253
+ // Register a listener that will be called any time the video settings have changed
254
+ RegisterListener(listener) {
255
+ this.__listeners.push(listener);
256
+
257
+ return () => this.__listeners = this.__listeners.filter(l => l !== listener);
258
+ }
259
+
260
+ // Indicate to controls that the settings have updated
261
+ Update(event) {
262
+ try {
263
+ this.onUpdate && this.onUpdate(event);
264
+ } catch(error) {
265
+ this.player.Log("Failed to call update listener", true);
266
+ this.player.Log(error, true);
267
+ }
268
+
269
+ this.__listeners.forEach(listener => {
270
+ try {
271
+ listener(event);
272
+ } catch (error) {
273
+ this.player.Log("Failed to call cast listener", true);
274
+ this.player.Log(error, true);
275
+ }
276
+ });
277
+ }
278
+ }
279
+
280
+ export default Cast;
@@ -72,6 +72,8 @@ class PlayerControls {
72
72
  * @returns {boolean} - Whether or not the video is currently playing
73
73
  */
74
74
  IsPlaying() {
75
+ if(this.player.casting) { return this.player.castHandler.IsPlaying(); }
76
+
75
77
  return !this.player.video.paused;
76
78
  }
77
79
 
@@ -93,6 +95,8 @@ class PlayerControls {
93
95
  * @returns {boolean} - Whether or not the video was able to start playing. Browser autoplay policies may block video from playing without user interaction.
94
96
  */
95
97
  async Play() {
98
+ if(this.player.casting) { return this.player.castHandler.Play(); }
99
+
96
100
  return await this.player.__Play();
97
101
  }
98
102
 
@@ -102,6 +106,8 @@ class PlayerControls {
102
106
  * @methodGroup Playback
103
107
  */
104
108
  Pause() {
109
+ if(this.player.casting) { return this.player.castHandler.Pause(); }
110
+
105
111
  this.player.video.pause();
106
112
  }
107
113
 
@@ -112,6 +118,8 @@ class PlayerControls {
112
118
  * @returns {boolean} - False if the video was paused, true if playback was started
113
119
  */
114
120
  TogglePlay() {
121
+ if(this.player.casting) { return this.player.castHandler.TogglePlay(); }
122
+
115
123
  if(this.player.video.paused) {
116
124
  this.Play();
117
125
  return true;
@@ -127,6 +135,8 @@ class PlayerControls {
127
135
  * @methodGroup Playback
128
136
  */
129
137
  Stop() {
138
+ if(this.player.casting) { return this.player.castHandler.Stop(); }
139
+
130
140
  this.Pause();
131
141
  this.Seek({time: 0});
132
142
  this.player.playbackStarted = false;
@@ -142,6 +152,8 @@ class PlayerControls {
142
152
  * @returns {number} - The current time of the video
143
153
  */
144
154
  GetCurrentTime() {
155
+ if(this.player.casting) { return this.player.castHandler.GetCurrentTime(); }
156
+
145
157
  return this.player.video.currentTime;
146
158
  }
147
159
 
@@ -153,6 +165,8 @@ class PlayerControls {
153
165
  * @returns {number} - The duration of the video. May be Infinity if content is live
154
166
  */
155
167
  GetDuration() {
168
+ if(this.player.casting) { return this.player.castHandler.GetDuration(); }
169
+
156
170
  return this.player.video.duration;
157
171
  }
158
172
 
@@ -168,6 +182,8 @@ class PlayerControls {
168
182
  * @returns {boolean} - False if the video was paused, true if playback was started
169
183
  */
170
184
  Seek({fraction, time, relativeSeconds}) {
185
+ if(this.player.casting) { return this.player.castHandler.Seek({fraction, time, relativeSeconds}); }
186
+
171
187
  if(!this.player.video || (fraction && !this.player.video.duration)) {
172
188
  return;
173
189
  }
@@ -199,6 +215,8 @@ class PlayerControls {
199
215
  * @returns {number} - The current volume of the video
200
216
  */
201
217
  GetVolume() {
218
+ if(this.player.casting) { return this.player.castHandler.GetVolume(); }
219
+
202
220
  return this.player.video.volume;
203
221
  }
204
222
 
@@ -213,6 +231,8 @@ class PlayerControls {
213
231
  * @returns {boolean} - The resulting volume of the video
214
232
  */
215
233
  SetVolume({fraction, relativeFraction}) {
234
+ if(this.player.casting) { return this.player.castHandler.SetVolume({fraction, relativeFraction}); }
235
+
216
236
  if(relativeFraction) {
217
237
  this.player.video.volume = Math.min(1, Math.max(0, this.GetVolume() + relativeFraction));
218
238
  } else {
@@ -234,6 +254,8 @@ class PlayerControls {
234
254
  * @returns {boolean} - Whether or not the video is currently muted
235
255
  */
236
256
  IsMuted() {
257
+ if(this.player.casting) { return this.player.castHandler.IsMuted(); }
258
+
237
259
  return this.player.video.muted;
238
260
  }
239
261
 
@@ -243,6 +265,8 @@ class PlayerControls {
243
265
  * @methodGroup Volume
244
266
  */
245
267
  Mute() {
268
+ if(this.player.casting) { return this.player.castHandler.Mute(); }
269
+
246
270
  this.player.video.muted = true;
247
271
  }
248
272
 
@@ -252,6 +276,8 @@ class PlayerControls {
252
276
  * @methodGroup Volume
253
277
  */
254
278
  Unmute() {
279
+ if(this.player.casting) { return this.player.castHandler.Unmute(); }
280
+
255
281
  this.player.video.muted = false;
256
282
  }
257
283
 
@@ -263,6 +289,8 @@ class PlayerControls {
263
289
  * @returns {boolean} - True if the video was muted, false if unmuted
264
290
  */
265
291
  ToggleMuted() {
292
+ if(this.player.casting) { return this.player.castHandler.ToggleMuted(); }
293
+
266
294
  this.player.video.muted = !this.player.video.muted;
267
295
  return this.player.video.muted;
268
296
  }
@@ -667,12 +695,14 @@ class PlayerControls {
667
695
  tracks[index].mode = "showing";
668
696
  }
669
697
  } else if(this.player.dashPlayer) {
670
- this.player.dashPlayer.setTextTrack(parseInt(index));
698
+ this.player.dashPlayer.setTextTrack(index);
671
699
  }
672
700
 
673
701
  if(index >= 0) {
674
702
  this.__lastTextTrackIndex = index;
675
703
  }
704
+
705
+ setTimeout(() => this.player.__SettingsUpdate(), 500);
676
706
  }
677
707
 
678
708
  /**
@@ -715,6 +745,19 @@ class PlayerControls {
715
745
  }
716
746
  }
717
747
 
748
+ /**
749
+ * Retrieve the current playback rate
750
+ *
751
+ * @methodGroup Playback Rate
752
+ *
753
+ * @returns {number} - The current playback rate
754
+ */
755
+ GetPlaybackRate() {
756
+ if(this.player.casting) { return this.player.castHandler.GetPlaybackRate(); }
757
+
758
+ return this.player.video.playbackRate || 1;
759
+ }
760
+
718
761
  /**
719
762
  * Retrieve the currently available playback rates
720
763
  *
@@ -723,12 +766,20 @@ class PlayerControls {
723
766
  * @returns {Object} - All options, as well as the active option.
724
767
  */
725
768
  GetPlaybackRates() {
726
- const options = ["0.25", "0.5", "0.75", "1", "1.25", "1.5", "1.75", "2"]
769
+ const currentPlaybackRate = this.GetPlaybackRate();
770
+ let options = ["0.25", "0.5", "0.75", "1", "1.25", "1.5", "1.75", "2"];
771
+
772
+ // Chromecast doesn't support < 0.5
773
+ if(this.player.casting) {
774
+ options = ["0.5", "0.75", "1", "1.25", "1.5", "1.75", "2"];
775
+ }
776
+
777
+ options = options
727
778
  .map((speed, index) => ({
728
779
  index,
729
780
  rate: parseFloat(speed),
730
781
  label: `${speed}x`,
731
- active: this.player.video.playbackRate.toFixed(2) === parseFloat(speed).toFixed(2)
782
+ active: currentPlaybackRate.toFixed(2) === parseFloat(speed).toFixed(2)
732
783
  }));
733
784
 
734
785
  let active = options.find(option => option.active);
@@ -736,8 +787,8 @@ class PlayerControls {
736
787
  if(!active) {
737
788
  active = {
738
789
  index: -1,
739
- rate: this.player.video.playbackRate,
740
- label: `${this.player.video.playbackRate}x`,
790
+ rate: this.GetPlaybackRate(),
791
+ label: `${this.GetPlaybackRate()}x`,
741
792
  active: true
742
793
  };
743
794
  }
@@ -759,6 +810,8 @@ class PlayerControls {
759
810
  * @returns {Object} - The rate that was set, as well as whether the rate was increased or decreased
760
811
  */
761
812
  SetPlaybackRate({index, rate}) {
813
+ if(this.player.casting) { return this.player.castHandler.SetPlaybackRate({index, rate}); }
814
+
762
815
  const originalSpeed = this.player.video.playbackRate;
763
816
 
764
817
  if(rate) {
@@ -780,6 +833,51 @@ class PlayerControls {
780
833
  }
781
834
  }
782
835
 
836
+ /**
837
+ * Retrieve whether or not DVR is available for the current content
838
+ *
839
+ * @methodGroup DVR
840
+ *
841
+ * @returns {boolean} - Whether or not DVR is available for the current content
842
+ */
843
+ IsDVRAvailable() {
844
+ return this.player.dvrAvailable && !this.player.casting && isFinite(this.GetDuration());
845
+ }
846
+
847
+ /**
848
+ * Retrieve whether or not DVR is active for the current content
849
+ *
850
+ * @methodGroup DVR
851
+ *
852
+ * @returns {boolean} - Whether or not DVR is active for the current content
853
+ */
854
+ IsDVREnabled() {
855
+ return this.player.dvrEnabled;
856
+ }
857
+
858
+ /**
859
+ * Activate or deactivate DVR (if available)
860
+ *
861
+ * @methodGroup DVR
862
+ * @param {boolean} enabled - Whether or not DVR should be enabled
863
+ *
864
+ * @returns {boolean} - If DVR is enabled or disabled
865
+ */
866
+ SetDVREnabled(enabled) {
867
+ const originallyEnabled = this.player.dvrEnabled;
868
+
869
+ this.player.dvrEnabled = this.IsDVRAvailable() && enabled;
870
+
871
+ this.player.__SettingsUpdate();
872
+
873
+ if(originallyEnabled && !this.player.dvrEnabled) {
874
+ // Seek to live edge if DVR was disabled
875
+ this.Seek({time: this.GetDuration() - 2});
876
+ }
877
+
878
+ return this.player.dvrEnabled;
879
+ }
880
+
783
881
  /**
784
882
  * Retrieve the currently available player profiles
785
883
  *