senza-sdk 4.2.60 → 4.2.62
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 +3 -2
- package/src/deviceManager.js +3 -3
- package/src/remotePlayer.js +9 -0
- package/src/senzaShakaPlayer.js +215 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "senza-sdk",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.62",
|
|
4
4
|
"main": "./src/api.js",
|
|
5
5
|
"description": "API for Senza application",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"prepublish": "npm run build",
|
|
15
15
|
"eslint": "eslint --max-warnings 0 src test",
|
|
16
16
|
"build": "npx webpack --config webpack.config.js",
|
|
17
|
-
"test": "jest --coverage"
|
|
17
|
+
"test": "jest --coverage --verbose",
|
|
18
|
+
"testall" : "jest --coverage --verbose && npm run eslint --fix"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@babel/cli": "^7.13.16",
|
package/src/deviceManager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getFCID, sdkLogger, getRestResponse } from "./utils";
|
|
2
|
-
import {isRunningE2E} from "./api";
|
|
3
|
-
import {sessionInfo} from "./SessionInfo";
|
|
2
|
+
import { isRunningE2E } from "./api";
|
|
3
|
+
import { sessionInfo } from "./SessionInfo";
|
|
4
4
|
|
|
5
5
|
const wifiInfo = {};
|
|
6
6
|
let wifi_ap_data;
|
|
@@ -290,7 +290,7 @@ class DeviceManager extends EventTarget {
|
|
|
290
290
|
// This api is part of epic HSDEV-4185
|
|
291
291
|
async getWifiInfo() {
|
|
292
292
|
await Promise.all([getWifiApData(), getWifiStatus()]);
|
|
293
|
-
return {...wifi_ap_data, ...wifi_status};
|
|
293
|
+
return { ...wifi_ap_data, ...wifi_status };
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
package/src/remotePlayer.js
CHANGED
|
@@ -171,6 +171,15 @@ class RemotePlayer extends EventTarget {
|
|
|
171
171
|
* });
|
|
172
172
|
* */
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* @event RemotePlayer#playing
|
|
176
|
+
* @description playing event will be dispatched when the remote player started playback of the first audio frame. This event can be used to synchronize the local player with the remote player.
|
|
177
|
+
* @example
|
|
178
|
+
* remotePlayer.addEventListener("playing", () => {
|
|
179
|
+
* console.info("remotePlayer playing");
|
|
180
|
+
* });
|
|
181
|
+
* */
|
|
182
|
+
|
|
174
183
|
/**
|
|
175
184
|
* @event RemotePlayer#loadedmetadata
|
|
176
185
|
* @description loadedmetadata event will be dispatched when the remote player metadata is loaded
|
package/src/senzaShakaPlayer.js
CHANGED
|
@@ -55,35 +55,92 @@ const senzaShaka = { ...shaka };
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
export class SenzaShakaPlayer extends shaka.Player {
|
|
58
|
+
/** @private {SenzaShakaPlayer|null} Previous instance of the player */
|
|
58
59
|
static _prevInstance = null;
|
|
60
|
+
|
|
59
61
|
/**
|
|
60
62
|
* @private
|
|
61
63
|
* @type {Object.<string, string>}
|
|
62
|
-
* @description Map
|
|
64
|
+
* @description Map of audio track languages to their IDs
|
|
63
65
|
*/
|
|
64
66
|
_audioTracksMap = {};
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
69
|
* @private
|
|
68
70
|
* @type {Object.<string, string>}
|
|
69
|
-
* @description Map
|
|
71
|
+
* @description Map of text track languages to their IDs
|
|
70
72
|
*/
|
|
71
73
|
_textTracksMap = {};
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @private
|
|
77
|
+
* @type {number}
|
|
78
|
+
* @description Timeout in milliseconds to wait for playing event
|
|
79
|
+
* @default 3000
|
|
80
|
+
*/
|
|
81
|
+
_playingTimeout = 3000;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @private
|
|
85
|
+
* @type {number|null}
|
|
86
|
+
* @description Timestamp when play was called
|
|
87
|
+
*/
|
|
88
|
+
_playStartTime = null;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @private
|
|
92
|
+
* @type {boolean}
|
|
93
|
+
* @description Whether to stop remote player on error
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
_shouldStopRemotePlayerOnError = false;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @private
|
|
100
|
+
* @type {Function|null}
|
|
101
|
+
* @description Original play function from video element
|
|
102
|
+
*/
|
|
103
|
+
_originalPlay = null;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @private
|
|
107
|
+
* @type {Function|null}
|
|
108
|
+
* @description Resolve function for play promise
|
|
109
|
+
*/
|
|
110
|
+
_playPromiseResolve = null;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @private
|
|
114
|
+
* @type {Function|null}
|
|
115
|
+
* @description Reject function for play promise
|
|
116
|
+
*/
|
|
117
|
+
_playPromiseReject = null;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @private
|
|
121
|
+
* @type {number|null}
|
|
122
|
+
* @description Timer ID for play timeout
|
|
123
|
+
*/
|
|
124
|
+
_playTimeoutId = null;
|
|
125
|
+
|
|
73
126
|
/**
|
|
74
127
|
* @private
|
|
75
128
|
* @type {Object.<string, Function>}
|
|
76
|
-
* @description
|
|
129
|
+
* @description Event listeners for video element
|
|
77
130
|
*/
|
|
78
131
|
_videoEventListeners = {
|
|
79
132
|
"play": () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
133
|
+
// keep old play behavior, in case playing timeout is defined as 0
|
|
134
|
+
if (this._playingTimeout === 0) {
|
|
135
|
+
this.remotePlayer.play()
|
|
136
|
+
.catch(error => {
|
|
137
|
+
sdkLogger.error("Failed to play remote player:", error);
|
|
138
|
+
this.handleSenzaError(error.code, error.message || "Unknown play error");
|
|
139
|
+
});
|
|
140
|
+
}
|
|
85
141
|
},
|
|
86
|
-
"pause": () => {
|
|
142
|
+
"pause" : () => {
|
|
143
|
+
this._resetPlayPromise();
|
|
87
144
|
this.remotePlayer.pause()
|
|
88
145
|
.catch(error => {
|
|
89
146
|
sdkLogger.error("Failed to pause remote player:", error);
|
|
@@ -97,7 +154,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
97
154
|
/**
|
|
98
155
|
* @private
|
|
99
156
|
* @type {Object.<string, Function>}
|
|
100
|
-
* @description
|
|
157
|
+
* @description Event listeners for remote player
|
|
101
158
|
*/
|
|
102
159
|
_remotePlayerEventListeners = {
|
|
103
160
|
"ended": () => {
|
|
@@ -109,7 +166,6 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
109
166
|
},
|
|
110
167
|
"error": (event) => {
|
|
111
168
|
sdkLogger.log("remotePlayer error:", event.detail.errorCode, event.detail.message);
|
|
112
|
-
// we need to move to foreground here for the auto background mode to work
|
|
113
169
|
lifecycle.moveToForeground();
|
|
114
170
|
this.handleSenzaError(event.detail.errorCode, event.detail.message);
|
|
115
171
|
},
|
|
@@ -166,17 +222,85 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
166
222
|
this._textTracksMap[lang] = track.id;
|
|
167
223
|
}
|
|
168
224
|
}
|
|
225
|
+
},
|
|
226
|
+
"playing": async () => {
|
|
227
|
+
sdkLogger.info("remotePlayer playing event received");
|
|
228
|
+
// If playing timeout was not expored, and the feature is set, handle the playing event
|
|
229
|
+
if (this._playingTimeout > 0 && this._playTimeoutId !== null) {
|
|
230
|
+
this._handlePlayingEvent();
|
|
231
|
+
}
|
|
169
232
|
}
|
|
170
233
|
};
|
|
171
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Clears the play timeout and resets the timer ID
|
|
237
|
+
* @private
|
|
238
|
+
* @description Cancels any pending play timeout and nullifies the timeout ID
|
|
239
|
+
*/
|
|
240
|
+
_clearPlayTimeout = () => {
|
|
241
|
+
if (this._playTimeoutId) {
|
|
242
|
+
sdkLogger.info("Clearing play timeout");
|
|
243
|
+
clearTimeout(this._playTimeoutId);
|
|
244
|
+
this._playTimeoutId = null;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* @private
|
|
249
|
+
* @description Handles the playing event from the remote player.
|
|
250
|
+
*/
|
|
251
|
+
_handlePlayingEvent = async () => {
|
|
252
|
+
|
|
253
|
+
this._clearPlayTimeout();
|
|
172
254
|
|
|
255
|
+
if (this.videoElement && this._originalPlay && this._playPromiseResolve) {
|
|
256
|
+
try {
|
|
257
|
+
const elapsedTime = Date.now() - this._playStartTime;
|
|
258
|
+
sdkLogger.info(`Time for playback start ${elapsedTime}ms`);
|
|
259
|
+
await this._originalPlay.call(this.videoElement);
|
|
260
|
+
sdkLogger.info("Video element play resolved successfully");
|
|
261
|
+
this._playPromiseResolve();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
this._handlePlayPromiseError(error);
|
|
264
|
+
return;
|
|
265
|
+
} finally {
|
|
266
|
+
this._playPromiseResolve = null;
|
|
267
|
+
this._playPromiseReject = null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
173
271
|
/**
|
|
174
|
-
* Flag indicating whether the remote player should be stopped when an error occurs
|
|
175
272
|
* @private
|
|
176
|
-
* @
|
|
177
|
-
* @default false
|
|
273
|
+
* @description Resets the play promise state, clearing any existing resolve/reject functions and timeouts.
|
|
178
274
|
*/
|
|
179
|
-
|
|
275
|
+
_resetPlayPromise = () => {
|
|
276
|
+
if (this._playPromiseResolve) {
|
|
277
|
+
sdkLogger.info("Resolving play promise");
|
|
278
|
+
this._playPromiseResolve();
|
|
279
|
+
}
|
|
280
|
+
this._playPromiseResolve = null;
|
|
281
|
+
this._playPromiseReject = null;
|
|
282
|
+
this._clearPlayTimeout();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Handles errors for play promises and remote player events.
|
|
288
|
+
* @private
|
|
289
|
+
* @param {Error} error - The error object.
|
|
290
|
+
*/
|
|
291
|
+
_handlePlayPromiseError(error) {
|
|
292
|
+
|
|
293
|
+
sdkLogger.error("Error while waiting for playing event:", error);
|
|
294
|
+
if (this._playPromiseReject) {
|
|
295
|
+
this._playPromiseReject(error);
|
|
296
|
+
this._playPromiseResolve = null;
|
|
297
|
+
this._playPromiseReject = null;
|
|
298
|
+
}
|
|
299
|
+
if (this._playTimeoutId) {
|
|
300
|
+
clearTimeout(this._playTimeoutId);
|
|
301
|
+
this._playTimeoutId = null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
180
304
|
|
|
181
305
|
/**
|
|
182
306
|
* Creates an instance of SenzaShakaPlayer, which is a subclass of shaka.Player.
|
|
@@ -201,15 +325,62 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
201
325
|
|
|
202
326
|
this.remotePlayer = remotePlayer;
|
|
203
327
|
this._addRemotePlayerEventListeners();
|
|
328
|
+
SenzaShakaPlayer._prevInstance = this;
|
|
329
|
+
const playTimeout = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"]?.playingEventTimeout;
|
|
330
|
+
this._playingTimeout = (playTimeout >= 0) ? playTimeout*1000 : this._playingTimeout;
|
|
331
|
+
|
|
204
332
|
// if video element is provided, add the listeres here. In this case ,there is no need to call attach.
|
|
205
333
|
if (videoElement) {
|
|
206
|
-
this.videoElement
|
|
207
|
-
this._attachVideoElementToRemotePlayer();
|
|
334
|
+
this._attach(videoElement);
|
|
208
335
|
sdkLogger.warn("SenzaShakaPlayer constructor Adding videoElement in the constructor is going to be deprecated in the future. Please use attach method instead.");
|
|
209
336
|
}
|
|
210
|
-
SenzaShakaPlayer._prevInstance = this;
|
|
211
337
|
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
_attach(videoElement) {
|
|
341
|
+
this.videoElement = videoElement;
|
|
342
|
+
|
|
343
|
+
// Store original play and replace with our implementation.
|
|
344
|
+
if (this._playingTimeout > 0) {
|
|
345
|
+
this._originalPlay = this.videoElement.play;
|
|
346
|
+
this.videoElement.play = async () => {
|
|
347
|
+
if (this._playPromiseResolve || this._playPromiseReject) {
|
|
348
|
+
sdkLogger.warn("play() was called while a play promise is already pending. Ignoring play call.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Clear any existing timeout
|
|
353
|
+
if (this._playTimeoutId) {
|
|
354
|
+
clearTimeout(this._playTimeoutId);
|
|
355
|
+
}
|
|
356
|
+
// Store the timestamp of the play call
|
|
357
|
+
this._playStartTime = Date.now();
|
|
358
|
+
|
|
359
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
360
|
+
this._playPromiseResolve = resolve;
|
|
361
|
+
this._playPromiseReject = reject;
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
});
|
|
365
|
+
// Set timeout to reject if playing event doesn't arrive
|
|
366
|
+
this._playTimeoutId = setTimeout(() => {
|
|
367
|
+
sdkLogger.error("Playing event timeout reached");
|
|
368
|
+
// when timeout reached start the local playback anyway
|
|
369
|
+
this._handlePlayingEvent();
|
|
370
|
+
}, this._playingTimeout);
|
|
371
|
+
await this.remotePlayer.play()
|
|
372
|
+
.catch(error => {
|
|
373
|
+
sdkLogger.error("Failed to play remote player:", error);
|
|
374
|
+
this.handleSenzaError(error.code, error.message || "Unknown play error");
|
|
375
|
+
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Create a new promise that will resolve when the real play succeeds
|
|
379
|
+
return returnPromise;
|
|
380
|
+
};
|
|
381
|
+
};
|
|
212
382
|
|
|
383
|
+
this._attachVideoElementToRemotePlayer();
|
|
213
384
|
}
|
|
214
385
|
|
|
215
386
|
/**
|
|
@@ -220,8 +391,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
220
391
|
*/
|
|
221
392
|
async attach(videoElement, initializeMediaSource = true) {
|
|
222
393
|
await super.attach(videoElement, initializeMediaSource);
|
|
223
|
-
this.videoElement
|
|
224
|
-
this._attachVideoElementToRemotePlayer();
|
|
394
|
+
this._attach(videoElement);
|
|
225
395
|
}
|
|
226
396
|
|
|
227
397
|
/**
|
|
@@ -235,6 +405,17 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
235
405
|
* @export
|
|
236
406
|
*/
|
|
237
407
|
async detach(keepAdManager = false) {
|
|
408
|
+
// Clear any pending timeout
|
|
409
|
+
this._resetPlayPromise();
|
|
410
|
+
|
|
411
|
+
if (this.videoElement && this._originalPlay) {
|
|
412
|
+
// Clear any pending play promise
|
|
413
|
+
this._playPromiseResolve = null;
|
|
414
|
+
this._playPromiseReject = null;
|
|
415
|
+
// Restore original play function before detaching
|
|
416
|
+
this.videoElement.play = this._originalPlay;
|
|
417
|
+
this._originalPlay = null;
|
|
418
|
+
}
|
|
238
419
|
|
|
239
420
|
await super.detach(keepAdManager);
|
|
240
421
|
this._audioTracksMap = {};
|
|
@@ -265,6 +446,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
265
446
|
// Call the remote player's unload method
|
|
266
447
|
try {
|
|
267
448
|
await lifecycle.moveToForeground();
|
|
449
|
+
this._clearPlayTimeout();
|
|
268
450
|
await remotePlayer.unload();
|
|
269
451
|
} catch (error) {
|
|
270
452
|
sdkLogger.error("Failed to unload remote player:", error);
|
|
@@ -391,7 +573,8 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
391
573
|
sdkLogger.error("Error while trying to stop video element playback:", stopError);
|
|
392
574
|
}
|
|
393
575
|
}
|
|
394
|
-
|
|
576
|
+
// Handle error while waiting for play event
|
|
577
|
+
this._handlePlayPromiseError(error);
|
|
395
578
|
this.dispatchEvent(new shaka.util.FakeEvent("error", errorMap));
|
|
396
579
|
}
|
|
397
580
|
|
|
@@ -487,6 +670,17 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
487
670
|
return super.destroy();
|
|
488
671
|
}
|
|
489
672
|
|
|
673
|
+
/**
|
|
674
|
+
* A temporary override for older versions of Shaka.
|
|
675
|
+
* Senza doesn't support out-of-band subtitles
|
|
676
|
+
*/
|
|
677
|
+
addTextTrack(uri, language, kind, mimeType, codec, label, forced = false) {
|
|
678
|
+
sdkLogger.warn("addTextTrack is deprecated, please use addTextTrackAsync");
|
|
679
|
+
super.addTextTrackAsync(uri, language, kind, mimeType, codec, label, forced).then(subs => {
|
|
680
|
+
sdkLogger.warn("addTextTrackAsync Done" + subs);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
490
684
|
/**
|
|
491
685
|
* Override the configure method to add custom configuration handling
|
|
492
686
|
* Supports the following additional configuration options:
|