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/dist/bundle.js +1 -1
- package/package.json +5 -5
- package/src/api.js +1 -1
- package/src/lifecycle.js +54 -17
- package/src/remotePlayer.js +31 -23
- package/src/utils.js +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "senza-sdk",
|
|
3
|
-
"version": "4.2.
|
|
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 {
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = {
|
|
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
|
-
|
|
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
|
-
|
|
280
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
281
|
+
logger.withFields({ duration }).log(`play completed successfully after ${duration} ms`);
|
|
258
282
|
this._inTransition = false;
|
|
259
|
-
|
|
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
|
-
|
|
264
|
-
reject(
|
|
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");
|
package/src/remotePlayer.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
797
|
+
const request = { target: "TC", waitForResponse: waitForResponse, message: JSON.stringify(message) };
|
|
805
798
|
return new Promise((resolve, reject) => {
|
|
806
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|