senza-sdk 4.2.12 → 4.2.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "senza-sdk",
3
- "version": "4.2.12",
3
+ "version": "4.2.13",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
@@ -38,10 +38,10 @@
38
38
  ],
39
39
  "coverageThreshold": {
40
40
  "global": {
41
- "branches": 85,
42
- "functions": 90,
43
- "lines": 92,
44
- "statements": 92
41
+ "branches": 85.5,
42
+ "functions": 90.5,
43
+ "lines": 92.5,
44
+ "statements": 92.5
45
45
  }
46
46
  }
47
47
  },
package/src/api.js CHANGED
@@ -180,7 +180,7 @@ export async function init() {
180
180
  }
181
181
 
182
182
  await remotePlayer._init(sessionInfoObj?.settings?.["ui-streamer"], originalTriggerEvent);
183
- await lifecycle._init();
183
+ await lifecycle._init(sessionInfoObj?.settings?.["ui-streamer"]);
184
184
 
185
185
  const devSequence = sessionInfoObj?.settings?.["ui-streamer"]?.devSequence;
186
186
  if (devSequence) {
package/src/lifecycle.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getPlatformInfo } from "./api";
2
- import { getFCID, isAudioSyncEnabled, sdkLogger, StreamType, SwitchMode } from "./utils";
2
+ import {getFCID, isAudioSyncEnabled, clearTimer, sdkLogger, StreamType, SwitchMode, SenzaError} from "./utils";
3
3
  import { sessionInfo } from "./SessionInfo";
4
- import { remotePlayer } from "./remotePlayer";
4
+ import { DEFAULT_PLAY_TIMEOUT, DEFAULT_STOP_TIMEOUT, remotePlayer } from "./remotePlayer";
5
5
 
6
6
  /**
7
7
  * Lifecycle is a singleton class that manages the application lifecycle states.<br>
@@ -74,7 +74,7 @@ class Lifecycle extends EventTarget {
74
74
 
75
75
  /** @private Initialize the lifecycle
76
76
  */
77
- async _init() {
77
+ async _init(uiStreamerSettings) {
78
78
  if (window.cefQuery) {
79
79
  this._state = await new Promise((resolve) => {
80
80
  window.cefQuery({
@@ -91,6 +91,8 @@ class Lifecycle extends EventTarget {
91
91
  });
92
92
  });
93
93
  this._isInitialized = true;
94
+ this._playTimeout = uiStreamerSettings?.playTimeout ?? DEFAULT_PLAY_TIMEOUT;
95
+ this._stopTimeout = uiStreamerSettings?.stopTimeout ?? DEFAULT_STOP_TIMEOUT;
94
96
  }
95
97
  }
96
98
 
@@ -152,7 +154,7 @@ class Lifecycle extends EventTarget {
152
154
  return Promise.resolve(false);
153
155
  }
154
156
  this._inTransition = true;
155
-
157
+ const FCID = getFCID();
156
158
  if (sessionInfo.sessionInfoObj?.settings?.["ui-streamer"]?.remotePlayerApiVersion >= 2) {
157
159
  return new Promise((resolve, reject) => {
158
160
  const FCID = getFCID();
@@ -162,27 +164,41 @@ class Lifecycle extends EventTarget {
162
164
  type: "remotePlayer.stop",
163
165
  class: "remotePlayer",
164
166
  action: "stop",
165
- streamType: isAudioSyncEnabled() ? StreamType.VIDEO | StreamType.SUBTITLE : StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE
167
+ streamType: isAudioSyncEnabled() ? StreamType.VIDEO | StreamType.SUBTITLE : StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE,
168
+ fcid: FCID
166
169
  };
167
- window.cefQuery({
168
- request: JSON.stringify({ target: "TC", waitForResponse: false, internalAction: "uiActive", message: JSON.stringify(message) }),
170
+ let timerId = 0;
171
+ const timeBeforeSendingRequest = Date.now();
172
+ const queryId = window.cefQuery({
173
+ request: JSON.stringify({ target: "TC", waitForResponse: true, internalAction: "uiActive", message: JSON.stringify(message) }),
169
174
  persistent: false,
170
175
  onSuccess: () => {
171
- logger.log("stop successfully sent");
176
+ const duration = Date.now() - timeBeforeSendingRequest;
177
+ logger.withFields({ duration }).log(`stop completed successfully after ${duration} ms`);
172
178
  this._inTransition = false;
179
+ timerId = clearTimer(timerId);
173
180
  resolve(true);
174
181
  },
175
182
  onFailure: (code, msg) => {
176
- logger.error(`stop failed: ${code} ${msg}`);
183
+ const duration = Date.now() - timeBeforeSendingRequest;
184
+ logger.withFields({ duration }).log(`stop failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
177
185
  this._inTransition = false;
178
- reject(`stop failed: ${code} ${msg}`);
186
+ timerId = clearTimer(timerId);
187
+ reject(new SenzaError(code, msg));
179
188
  }
180
189
  });
190
+ logger.log(`window.cefQuery for stop returned query id ${queryId}`);
191
+ const stopTimeout = this._stopTimeout + 1000;
192
+ timerId = setTimeout(() => {
193
+ logger.log(`stop reached timeout of ${stopTimeout} ms, canceling query id ${queryId}`);
194
+ this._inTransition = false;
195
+ window.cefQueryCancel(queryId);
196
+ reject(new SenzaError(6000, `stop reached timeout of ${stopTimeout} ms`));
197
+ }, stopTimeout, queryId);
181
198
  });
182
199
  }
183
200
 
184
201
  return new Promise((resolve, reject) => {
185
- const FCID = getFCID();
186
202
  const logger = sdkLogger.withFields({ FCID });
187
203
  logger.log("lifecycle moveToForeground: sending uiActiveRequest action");
188
204
  window.cefQuery({
@@ -246,24 +262,45 @@ class Lifecycle extends EventTarget {
246
262
  message.class = "remotePlayer";
247
263
  message.switchMode = isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
248
264
  message.streamType = StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE;
249
- request = { target: "TC", waitForResponse: false, internalAction: "uiExit", message: JSON.stringify(message) };
265
+ request = {
266
+ target: "TC",
267
+ waitForResponse: true,
268
+ internalAction: "uiExit",
269
+ message: JSON.stringify(message)
270
+ };
250
271
  } else {
251
272
  request = message;
252
273
  }
253
- window.cefQuery({
274
+ let timerId = 0;
275
+ const timeBeforeSendingRequest = Date.now();
276
+ const queryId = window.cefQuery({
254
277
  request: JSON.stringify(request),
255
278
  persistent: false,
256
279
  onSuccess: () => {
257
- logger.log("[ moveToBackground ] play successfully sent");
280
+ const duration = Date.now() - timeBeforeSendingRequest;
281
+ logger.withFields({ duration }).log(`play completed successfully after ${duration} ms`);
258
282
  this._inTransition = false;
259
- resolve(true);
283
+ timerId = clearTimer(timerId);
284
+ resolve();
260
285
  },
261
286
  onFailure: (code, msg) => {
287
+ const duration = Date.now() - timeBeforeSendingRequest;
288
+ logger.withFields({ duration }).log(`play failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
262
289
  this._inTransition = false;
263
- logger.error(`[ moveToBackground ] play failed: ${code} ${msg}`);
264
- reject(`[ moveToBackground ] play failed: ${code} ${msg}`);
290
+ timerId = clearTimer(timerId);
291
+ reject(new SenzaError(code, msg));
265
292
  }
266
293
  });
294
+ if (sessionInfo.sessionInfoObj?.settings?.["ui-streamer"]?.remotePlayerApiVersion >= 2) {
295
+ logger.log(`window.cefQuery for play returned query id ${queryId}`);
296
+ const playTimeout = this._playTimeout + 1000;
297
+ timerId = setTimeout(() => {
298
+ logger.log(`play reached timeout of ${playTimeout} ms, canceling query id ${queryId}`);
299
+ this._inTransition = false;
300
+ window.cefQueryCancel(queryId);
301
+ reject(new SenzaError(6000, `play reached timeout of ${playTimeout} ms`));
302
+ }, playTimeout, queryId);
303
+ }
267
304
  });
268
305
  }
269
306
  sdkLogger.warn("lifecycle moveToBackground: window.cefQuery is undefined");
@@ -1,5 +1,5 @@
1
1
 
2
- import { getFCID, isAudioSyncEnabled, sdkLogger, StreamType, SwitchMode } from "./utils";
2
+ import { getFCID, isAudioSyncEnabled, clearTimer, sdkLogger, StreamType, SwitchMode } from "./utils";
3
3
  import { lifecycle } from "./lifecycle";
4
4
  import { writeLicenseResponse } from "./api";
5
5
  import { mergeAutoTranslationLanguages } from "./subtitlesUtils";
@@ -8,6 +8,8 @@ import { sessionInfo } from "./SessionInfo";
8
8
  const INVALID_SET_PLAYABLE_URI_CODE = 99;
9
9
  const DEFAULT_SET_PLAYABLE_URI_TIMEOUT = 5000;
10
10
  const DEFAULT_UNLOAD_TIMEOUT = 5000;
11
+ export const DEFAULT_PLAY_TIMEOUT = 5000;
12
+ export const DEFAULT_STOP_TIMEOUT = 5000;
11
13
 
12
14
  const cloneDeep = (element) => {
13
15
  return JSON.parse(JSON.stringify(element));
@@ -17,7 +19,7 @@ const cloneDeep = (element) => {
17
19
  /** Error object to be thrown on remotePlayer api failures.
18
20
  * [See error list]{@link RemotePlayer#error}
19
21
  */
20
- class RemotePlayerError extends Error {
22
+ export class RemotePlayerError extends Error {
21
23
  constructor(code, message) {
22
24
  super(message);
23
25
  this.code = code;
@@ -258,7 +260,7 @@ class RemotePlayer extends EventTarget {
258
260
  * | 3403 | Player | Problem accessing segments, server authentication issue |
259
261
  * | 3404 | Player | Problem accessing segments, server returned not found |
260
262
  * | 3900-3999 | Player | Internal player error |
261
- * | 6000 | Player | The call to load() has reached the configurable timeout with no response from the remote player |
263
+ * | 6000 | Player | The remote player api call has reached the configurable timeout with no response from the remote player |
262
264
  * | 6001 | Player | play() was called while the remote player is not loaded |
263
265
  * | 6002 | Player | load() was called while the application was in state 'background' or 'inTransitionToBackground' |
264
266
  * | 6500 | Player | remotePlayer api was called before initializing remotePlayer |
@@ -413,6 +415,7 @@ class RemotePlayer extends EventTarget {
413
415
  this._isInitialized = true;
414
416
  this._setPlayableUriTimeout = uiStreamerSettings?.setPlayableUriTimeout ?? DEFAULT_SET_PLAYABLE_URI_TIMEOUT;
415
417
  this._unloadTimeout = uiStreamerSettings?.unloadTimeout ?? DEFAULT_UNLOAD_TIMEOUT;
418
+ this._playTimeout = uiStreamerSettings?.playTimeout ?? DEFAULT_PLAY_TIMEOUT;
416
419
  this._requestVideoFrameInfo = uiStreamerSettings?.requestVideoFrameInfo ?? true;
417
420
 
418
421
  let playbackMetadata = {};
@@ -644,10 +647,7 @@ class RemotePlayer extends EventTarget {
644
647
  if (position > 0) {
645
648
  setPlaybackInfo({ playbackPosition: position });
646
649
  }
647
- if (timerId) {
648
- clearTimeout(timerId);
649
- timerId = 0;
650
- }
650
+ timerId = clearTimer(timerId);
651
651
  this._changeLoadMode(this.LoadMode.LOADED);
652
652
  this._loadedUrl = url;
653
653
  this.dispatchEvent(new Event("canplay"));
@@ -656,10 +656,7 @@ class RemotePlayer extends EventTarget {
656
656
  onFailure: (code, msg) => {
657
657
  const duration = Date.now() - timeBeforeSendingRequest;
658
658
  logger.withFields({ duration }).log(`setPlayableUri failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
659
- if (timerId) {
660
- clearTimeout(timerId);
661
- timerId = 0;
662
- }
659
+ timerId = clearTimer(timerId);
663
660
  // If setPlayableUri failed because the request was invalid, we want to retain the previous load mode
664
661
  if (code === INVALID_SET_PLAYABLE_URI_CODE) {
665
662
  this._changeLoadMode(previousLoadMode);
@@ -721,10 +718,7 @@ class RemotePlayer extends EventTarget {
721
718
  onSuccess: () => {
722
719
  const duration = Date.now() - timeBeforeSendingRequest;
723
720
  logger.withFields({ duration }).log(`unload completed successfully after ${duration} ms`);
724
- if (timerId) {
725
- clearTimeout(timerId);
726
- timerId = 0;
727
- }
721
+ timerId = clearTimer(timerId);
728
722
  this._reset();
729
723
  this._changeLoadMode(this.LoadMode.NOT_LOADED);
730
724
  this._loadedUrl = undefined;
@@ -733,10 +727,7 @@ class RemotePlayer extends EventTarget {
733
727
  onFailure: (code, msg) => {
734
728
  const duration = Date.now() - timeBeforeSendingRequest;
735
729
  logger.withFields({ duration }).log(`unload failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
736
- if (timerId) {
737
- clearTimeout(timerId);
738
- timerId = 0;
739
- }
730
+ timerId = clearTimer(timerId);
740
731
  this._changeLoadMode(previousLoadMode);
741
732
  reject(new RemotePlayerError(code, msg));
742
733
  }
@@ -792,29 +783,46 @@ class RemotePlayer extends EventTarget {
792
783
  subtitlesLanguage,
793
784
  playbackPosition: this.currentTime
794
785
  };
786
+ let waitForResponse = false;
795
787
  if (sessionInfo.sessionInfoObj?.settings?.["ui-streamer"]?.remotePlayerApiVersion >= 2) {
796
788
  if (isAudioSyncEnabled()) {
797
789
  message.switchMode = SwitchMode.SEAMLESS;
798
790
  message.streamType = StreamType.AUDIO;
791
+ waitForResponse = true;
799
792
  } else {
800
793
  logger.log("remotePlayer play request ignored and will be sent with the lifecycle.moveToBackground()");
801
794
  return Promise.resolve();
802
795
  }
803
796
  }
804
- const request = { target: "TC", waitForResponse: false, message: JSON.stringify(message) };
797
+ const request = { target: "TC", waitForResponse: waitForResponse, message: JSON.stringify(message) };
805
798
  return new Promise((resolve, reject) => {
806
- window.cefQuery({
799
+ let timerId = 0;
800
+ const timeBeforeSendingRequest = Date.now();
801
+ const queryId = window.cefQuery({
807
802
  request: JSON.stringify(request),
808
803
  persistent: false,
809
804
  onSuccess: () => {
810
- logger.log("remotePlayer play request successfully sent");
805
+ const duration = Date.now() - timeBeforeSendingRequest;
806
+ logger.withFields({ duration }).log(`play completed successfully after ${duration} ms`);
807
+ timerId = clearTimer(timerId);
811
808
  resolve();
812
809
  },
813
810
  onFailure: (code, msg) => {
814
- logger.error(`remotePlayer play request failed: ${code} ${msg}`);
811
+ const duration = Date.now() - timeBeforeSendingRequest;
812
+ logger.withFields({ duration }).log(`play failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
813
+ timerId = clearTimer(timerId);
815
814
  reject(new RemotePlayerError(code, msg));
816
815
  }
817
816
  });
817
+ logger.log(`window.cefQuery for play returned query id ${queryId}`);
818
+ if (waitForResponse) {
819
+ const playTimeout = this._playTimeout + 1000;
820
+ timerId = setTimeout(() => {
821
+ logger.log(`play reached timeout of ${playTimeout} ms, canceling query id ${queryId}`);
822
+ window.cefQueryCancel(queryId);
823
+ reject(new RemotePlayerError(6000, `play reached timeout of ${playTimeout} ms`));
824
+ }, playTimeout, queryId);
825
+ }
818
826
  });
819
827
  }
820
828
  sdkLogger.error("remotePlayer play: window.cefQuery is undefined");
package/src/utils.js CHANGED
@@ -9,6 +9,14 @@ export function getFCID() {
9
9
  return Math.round(Math.random() * 100000) + "-" + getPlatformInfo().sessionInfo?.connectionId;
10
10
  }
11
11
 
12
+ export class SenzaError extends Error {
13
+ constructor(code, message) {
14
+ super(message);
15
+ this.code = code;
16
+ this.msg = message;
17
+ }
18
+ }
19
+
12
20
  /* eslint-disable no-console */
13
21
  class SdkLogger {
14
22
 
@@ -90,6 +98,14 @@ export async function getRestResponse(messageName) {
90
98
  });
91
99
  }
92
100
 
101
+ export function clearTimer(timerId) {
102
+ if (timerId) {
103
+ clearTimeout(timerId);
104
+ timerId = 0;
105
+ }
106
+ return timerId;
107
+ }
108
+
93
109
  export function isAudioSyncEnabled() {
94
110
  const clientUiAudioSettings = sessionInfo.sessionInfoObj?.settings?.client?.["ui_audio"];
95
111
  return clientUiAudioSettings?.enabled && clientUiAudioSettings?.from_cdn && clientUiAudioSettings?.ui_sync_enabled;