@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.
@@ -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
  }
@@ -717,6 +745,19 @@ class PlayerControls {
717
745
  }
718
746
  }
719
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
+
720
761
  /**
721
762
  * Retrieve the currently available playback rates
722
763
  *
@@ -725,12 +766,20 @@ class PlayerControls {
725
766
  * @returns {Object} - All options, as well as the active option.
726
767
  */
727
768
  GetPlaybackRates() {
728
- 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
729
778
  .map((speed, index) => ({
730
779
  index,
731
780
  rate: parseFloat(speed),
732
781
  label: `${speed}x`,
733
- active: this.player.video.playbackRate.toFixed(2) === parseFloat(speed).toFixed(2)
782
+ active: currentPlaybackRate.toFixed(2) === parseFloat(speed).toFixed(2)
734
783
  }));
735
784
 
736
785
  let active = options.find(option => option.active);
@@ -738,8 +787,8 @@ class PlayerControls {
738
787
  if(!active) {
739
788
  active = {
740
789
  index: -1,
741
- rate: this.player.video.playbackRate,
742
- label: `${this.player.video.playbackRate}x`,
790
+ rate: this.GetPlaybackRate(),
791
+ label: `${this.GetPlaybackRate()}x`,
743
792
  active: true
744
793
  };
745
794
  }
@@ -761,6 +810,8 @@ class PlayerControls {
761
810
  * @returns {Object} - The rate that was set, as well as whether the rate was increased or decreased
762
811
  */
763
812
  SetPlaybackRate({index, rate}) {
813
+ if(this.player.casting) { return this.player.castHandler.SetPlaybackRate({index, rate}); }
814
+
764
815
  const originalSpeed = this.player.video.playbackRate;
765
816
 
766
817
  if(rate) {
@@ -782,6 +833,51 @@ class PlayerControls {
782
833
  }
783
834
  }
784
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
+
785
881
  /**
786
882
  * Retrieve the currently available player profiles
787
883
  *