senza-sdk 4.2.51-c1ae854.0 → 4.2.51

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.51-c1ae854.0",
3
+ "version": "4.2.51",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
package/src/lifecycle.js CHANGED
@@ -87,7 +87,7 @@ class Lifecycle extends EventTarget {
87
87
  /**
88
88
  * @event Lifecycle#userdisconnected
89
89
  * @description Fired when the user session ends .
90
- * This event is useful for cleaning up application state or saving data before the application closes.
90
+ * This event is useful for cleaning up application state or saving data before the application closes. Event callback should return promise to ensure that the event is handled before the application is terminated
91
91
  * @example
92
92
  * lifecycle.addEventListener("userdisconnected", () => {
93
93
  * console.log("User session ended, cleaning up application state");
@@ -410,12 +410,12 @@ class Lifecycle extends EventTarget {
410
410
  }
411
411
 
412
412
  /**
413
- * Controls the autoBackground feature<br>
413
+ * @deprecated Use `lifecycle.configure()` instead.
414
+ * Controls the autoBackground feature.<br>
414
415
  * When enabled, the application will automatically move to the background state after a configurable
415
- * period of inactivity. The delay depends on whether video is playing ({@link Lifecycle#autoBackgroundDelay})
416
- * or the UI is idle ({@link Lifecycle#autoBackgroundOnUIDelay}).
417
- * The application will return to the foreground state when a key is pressed.
416
+ * period of inactivity. Use the `configure` method to set timeouts for video playback and UI states.
418
417
  * @type {boolean}
418
+ * @see {@link Lifecycle#configure}
419
419
  */
420
420
  set autoBackground(enabled) {
421
421
  this._autoBackground = enabled;
@@ -431,10 +431,12 @@ class Lifecycle extends EventTarget {
431
431
  }
432
432
 
433
433
  /**
434
+ * @deprecated Use `lifecycle.configure()` instead.
434
435
  * The number of seconds of user inactivity before the application moves to the background state while playing video.
435
- * This setting is used when remotePlayer is in playing state.
436
+ * Use the `configure` method to set this timeout.
436
437
  * @type {integer}
437
438
  * @default 30
439
+ * @see {@link Lifecycle#configure}
438
440
  */
439
441
  set autoBackgroundDelay(delay) {
440
442
  this._autoBackgroundOnVideoDelay = delay;
@@ -444,12 +446,18 @@ class Lifecycle extends EventTarget {
444
446
  }
445
447
  }
446
448
 
449
+ get autoBackgroundDelay() {
450
+ return this._autoBackgroundOnVideoDelay;
451
+ }
452
+
447
453
  /**
448
- * The number of seconds of user inactivity before the application moves to the background state while in the UI (not playing).
449
- * This setting is used when remotePlayer is not in playing state.
450
- * @type {integer}
451
- * @default 30
452
- */
454
+ * @deprecated Use `lifecycle.configure()` instead.
455
+ * The number of seconds of user inactivity before the application moves to the background state while in the UI (not playing).
456
+ * Use the `configure` method to set this timeout.
457
+ * @type {integer}
458
+ * @default -1
459
+ * @see {@link Lifecycle#configure}
460
+ */
453
461
  set autoBackgroundOnUIDelay(delay) {
454
462
  this._autoBackgroundOnUIDelay = delay;
455
463
 
@@ -458,13 +466,10 @@ class Lifecycle extends EventTarget {
458
466
  }
459
467
  }
460
468
 
461
- get autoBackgroundDelay() {
462
- return this._autoBackgroundOnVideoDelay;
463
- }
464
-
465
469
  get autoBackgroundOnUIDelay() {
466
470
  return this._autoBackgroundOnUIDelay;
467
471
  }
472
+
468
473
  /**
469
474
  * @private
470
475
  */
@@ -824,9 +829,9 @@ class Lifecycle extends EventTarget {
824
829
  }
825
830
 
826
831
  /**
827
- * Override addEventListener to handle "userdisconnected" event specially
832
+ * Add event listener for lifecycle events
828
833
  * @param {string} type - The event type to listen for
829
- * @param {Function} listener - The callback function
834
+ * @param {Function} listener - The callback function. Listeners for 'userdisconnected' events should return a promise to ensure the event is processed before the application exits.
830
835
  * @param {Object} options - Event listener options
831
836
  */
832
837
  addEventListener(type, listener, options) {
@@ -840,7 +845,7 @@ class Lifecycle extends EventTarget {
840
845
  }
841
846
 
842
847
  /**
843
- * Override removeEventListener to handle "userdisconnected" event specially
848
+ * Remove event listener
844
849
  * @param {string} type - The event type
845
850
  * @param {Function} listener - The callback function to remove
846
851
  * @param {Object} options - Event listener options
@@ -9,8 +9,7 @@ import {
9
9
  SeekState,
10
10
  TargetPlayingState,
11
11
  isSubtitlesTranslationAllowed,
12
- isSubtitlesTranslationPattern,
13
- SetAudioLanguageState
12
+ isSubtitlesTranslationPattern
14
13
  } from "./utils";
15
14
  import { lifecycle } from "./lifecycle";
16
15
  import { writeLicenseResponse } from "./api";
@@ -891,7 +890,6 @@ class RemotePlayer extends EventTarget {
891
890
  if (this._loadMode === this.LoadMode.LOADING || this._loadMode === this.LoadMode.UNLOADING) {
892
891
  throw new RemotePlayerError(6501, "Cannot call load() while previous load/unload is still in progress");
893
892
  }
894
- this._abortSetAudioLanguage = true;
895
893
  this._abortSeeking = true;
896
894
  if (reset) {
897
895
  this._reset();
@@ -1191,6 +1189,7 @@ class RemotePlayer extends EventTarget {
1191
1189
  const prevSelectedAudioTrack = this._selectedAudioTrack;
1192
1190
  for (const track of this.getAudioTracks()) {
1193
1191
  if (track.id === audioTrackId) {
1192
+ this._selectedAudioTrack = audioTrackId;
1194
1193
  found = true;
1195
1194
  break;
1196
1195
  }
@@ -1199,34 +1198,19 @@ class RemotePlayer extends EventTarget {
1199
1198
  sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
1200
1199
  return Promise.resolve();
1201
1200
  }
1202
- if (this._selectedAudioTrack === audioTrackId) {
1201
+ if (this._remotePlayerApiVersion < 2) {
1202
+ return Promise.resolve(); // Resolve immediately for older versions
1203
+ }
1204
+ if (prevSelectedAudioTrack === this._selectedAudioTrack) {
1203
1205
  return Promise.resolve(); // Audio language already selected
1204
1206
  }
1205
1207
 
1206
- switch (this._remotePlayerApiVersion) {
1207
- case 0:
1208
- case 1:
1209
- this._selectedAudioTrack = audioTrackId;
1210
- return Promise.resolve(); // Resolve immediately for older versions
1211
- case 2:
1212
- return this._selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack);
1213
- default:
1214
- this._pendingAudioLanguage = audioTrackId;
1215
- return this._atomicSetAudioLanguage();
1216
- }
1208
+ return this._selectAudioTrack(audioTrackId, prevSelectedAudioTrack);
1217
1209
  }
1218
1210
 
1219
- /**
1220
- * Handles the asynchronous selection of an audio track.
1221
- * If the player is playing, it pauses before changing the track and resumes playback if necessary.
1222
- *
1223
- * @param {string} audioTrackId - The ID of the audio track to select.
1224
- * @param {string} prevSelectedAudioTrack - The previously selected audio track ID.
1225
- * @returns {Promise<void>} Resolves when the operation is complete.
1226
- * */
1227
- async _selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack) {
1211
+ async _selectAudioTrack(audioTrackId, prevSelectedAudioTrack) {
1228
1212
  const prevIsPlaying = this._isPlaying;
1229
- sdkLogger.log(`remotePlayer _selectAudioTrackV2: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
1213
+ sdkLogger.log(`remotePlayer _selectAudioTrack: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
1230
1214
  try {
1231
1215
  if (this._isPlaying) await this.pause();
1232
1216
  let position = this.currentTime;
@@ -1234,9 +1218,9 @@ class RemotePlayer extends EventTarget {
1234
1218
  position = this._videoElement.currentTime;
1235
1219
  }
1236
1220
  await this._load(this._loadedUrl, position, audioTrackId, undefined, false);
1237
- this._selectedAudioTrack = audioTrackId;
1238
1221
  } catch (e) {
1239
- // Do NOT reject - just log
1222
+ // Do NOT reject - just log and revert selection
1223
+ this._selectedAudioTrack = prevSelectedAudioTrack;
1240
1224
  sdkLogger.warn(`Failed to select audio track ${audioTrackId}: ${e.message}`);
1241
1225
  return;
1242
1226
  }
@@ -1253,63 +1237,6 @@ class RemotePlayer extends EventTarget {
1253
1237
  }
1254
1238
  }
1255
1239
 
1256
- /**
1257
- * Handles the asynchronous selection of an audio track.
1258
- * If the player is playing, it stops subtitle streamType before changing the track and resumes subtitle streamType playback if necessary.
1259
- * Available only from v3 and on
1260
- *
1261
- * @param {string} audioTrackId - The ID of the audio track to select.
1262
- * @param {string} prevSelectedAudioTrack - The previously selected audio track ID.
1263
- * @returns {Promise<void>} Resolves when the operation is complete.
1264
- * */
1265
- async _selectAudioTrackV3(audioTrackId, prevSelectedAudioTrack) {
1266
- sdkLogger.log(`remotePlayer _selectAudioTrackV3: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
1267
- if (window.cefQuery) {
1268
- const FCID = getFCID();
1269
- const logger = sdkLogger.withFields({ FCID });
1270
- logger.log("remotePlayer _selectAudioTrackV3: sending setAudioLanguage action");
1271
- const message = {
1272
- type: "remotePlayer.setAudioLanguage",
1273
- class: "remotePlayer",
1274
- action: "setAudioLanguage",
1275
- fcid: FCID,
1276
- language: audioTrackId,
1277
- };
1278
- const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
1279
- return new Promise((resolve, reject) => {
1280
- let timerId = 0;
1281
- const timeBeforeSendingRequest = Date.now();
1282
- const queryId = window.cefQuery({
1283
- request: JSON.stringify(request),
1284
- persistent: false,
1285
- onSuccess: () => {
1286
- this._selectedAudioTrack = audioTrackId;
1287
- const duration = Date.now() - timeBeforeSendingRequest;
1288
- logger.withFields({ duration }).log(`setAudioLanguage completed successfully after ${duration} ms`);
1289
- timerId = clearTimer(timerId);
1290
- resolve();
1291
- },
1292
- onFailure: (code, msg) => {
1293
- const duration = Date.now() - timeBeforeSendingRequest;
1294
- logger.withFields({ duration }).log(`setAudioLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
1295
- timerId = clearTimer(timerId);
1296
- reject(new RemotePlayerError(code, msg));
1297
- }
1298
- });
1299
- logger.log(`window.cefQuery for setAudioLanguage returned query id ${queryId}`);
1300
- const timeout = this._remotePlayerConfirmationTimeout + 1000;
1301
- timerId = setTimeout(() => {
1302
- logger.log(`setAudioLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
1303
- window.cefQueryCancel(queryId);
1304
- reject(new RemotePlayerError(6000, `setAudioLanguage reached timeout of ${timeout} ms`));
1305
- }, timeout, queryId);
1306
- });
1307
- }
1308
-
1309
- sdkLogger.error("remotePlayer _selectAudioTrackV3: window.cefQuery is undefined");
1310
- return Promise.resolve(undefined);
1311
- }
1312
-
1313
1240
  /** Select a specific text (subtitle) track.
1314
1241
  * Track id should come from a call to getTextTracks.
1315
1242
  * If no tracks exist - this is a no-op.
@@ -1329,6 +1256,7 @@ class RemotePlayer extends EventTarget {
1329
1256
  const prevSelectedTextTrack = this._selectedSubtitlesTrack;
1330
1257
  for (const track of this.getTextTracks()) {
1331
1258
  if (track.id === textTrackId) {
1259
+ this._selectedSubtitlesTrack = textTrackId;
1332
1260
  found = true;
1333
1261
  break;
1334
1262
  }
@@ -1337,20 +1265,14 @@ class RemotePlayer extends EventTarget {
1337
1265
  sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
1338
1266
  return Promise.resolve();
1339
1267
  }
1340
- if (this._selectedSubtitlesTrack === textTrackId) {
1268
+ if (this._remotePlayerApiVersion < 2) {
1269
+ return Promise.resolve(); // Resolve immediately for older versions
1270
+ }
1271
+ if (prevSelectedTextTrack === this._selectedSubtitlesTrack) {
1341
1272
  return Promise.resolve(); // Subtitle language already selected
1342
1273
  }
1343
1274
 
1344
- switch (this._remotePlayerApiVersion) {
1345
- case 0:
1346
- case 1:
1347
- this._selectedSubtitlesTrack = textTrackId;
1348
- return Promise.resolve(); // Resolve immediately for older versions
1349
- case 2:
1350
- return this._selectTextTrackV2(textTrackId, prevSelectedTextTrack);
1351
- default:
1352
- return this._selectTextTrackV3(textTrackId, prevSelectedTextTrack);
1353
- }
1275
+ return this._selectTextTrack(textTrackId, prevSelectedTextTrack);
1354
1276
  }
1355
1277
 
1356
1278
  /**
@@ -1361,9 +1283,9 @@ class RemotePlayer extends EventTarget {
1361
1283
  * @param {string} prevSelectedTextTrack - The previously selected text track ID.
1362
1284
  * @returns {Promise<void>} Resolves when the operation is complete.
1363
1285
  * */
1364
- async _selectTextTrackV2(textTrackId, prevSelectedTextTrack) {
1286
+ async _selectTextTrack(textTrackId, prevSelectedTextTrack) {
1365
1287
  const prevIsPlaying = this._isPlaying;
1366
- sdkLogger.log(`remotePlayer _selectTextTrackV2: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
1288
+ sdkLogger.log(`remotePlayer _selectTextTrack: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
1367
1289
  try {
1368
1290
  if (this._isPlaying) await this.pause();
1369
1291
  let position = this.currentTime;
@@ -1371,9 +1293,9 @@ class RemotePlayer extends EventTarget {
1371
1293
  position = this._videoElement.currentTime;
1372
1294
  }
1373
1295
  await this._load(this._loadedUrl, position, undefined, textTrackId, false);
1374
- this._selectedSubtitlesTrack = textTrackId;
1375
1296
  } catch (e) {
1376
- // Do NOT reject - just log
1297
+ // Do NOT reject - just log and revert selection
1298
+ this._selectedSubtitlesTrack = prevSelectedTextTrack;
1377
1299
  sdkLogger.warn(`Failed to select text track ${textTrackId}: ${e.message}`);
1378
1300
  return;
1379
1301
  }
@@ -1390,65 +1312,6 @@ class RemotePlayer extends EventTarget {
1390
1312
  }
1391
1313
  }
1392
1314
 
1393
- /**
1394
- * Handles the asynchronous selection of a text track.
1395
- * If the player is playing, it stops subtitle streamType before changing the track and resumes subtitle streamType playback if necessary.
1396
- * Available only from v3 and on
1397
- *
1398
- * @param {string} textTrackId - The ID of the text track to select.
1399
- * @param {string} prevSelectedTextTrack - The previously selected text track ID.
1400
- * @returns {Promise<void>} Resolves when the operation is complete.
1401
- * */
1402
- async _selectTextTrackV3(textTrackId, prevSelectedTextTrack) {
1403
- sdkLogger.log(`remotePlayer _selectTextTrackV3: prevAudioTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
1404
- if (window.cefQuery) {
1405
- const FCID = getFCID();
1406
- const logger = sdkLogger.withFields({ FCID });
1407
- logger.log("remotePlayer _selectTextTrackV3: sending setSubtitleLanguage action");
1408
- const message = {
1409
- type: "remotePlayer.setSubtitleLanguage",
1410
- class: "remotePlayer",
1411
- action: "setSubtitleLanguage",
1412
- fcid: FCID,
1413
- language: textTrackId,
1414
- };
1415
- const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
1416
- return new Promise((resolve, reject) => {
1417
- let timerId = 0;
1418
- const timeBeforeSendingRequest = Date.now();
1419
- const queryId = window.cefQuery({
1420
- request: JSON.stringify(request),
1421
- persistent: false,
1422
- onSuccess: () => {
1423
- this._selectedSubtitlesTrack = textTrackId;
1424
- const duration = Date.now() - timeBeforeSendingRequest;
1425
- logger.withFields({ duration }).log(`setSubtitleLanguage completed successfully after ${duration} ms`);
1426
- timerId = clearTimer(timerId);
1427
- resolve();
1428
- },
1429
- onFailure: (code, msg) => {
1430
- const duration = Date.now() - timeBeforeSendingRequest;
1431
- logger.withFields({ duration }).log(`setSubtitleLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
1432
- timerId = clearTimer(timerId);
1433
- reject(new RemotePlayerError(code, msg));
1434
- }
1435
- });
1436
- logger.log(`window.cefQuery for setSubtitleLanguage returned query id ${queryId}`);
1437
- const timeout = this._remotePlayerConfirmationTimeout + 1000;
1438
- timerId = setTimeout(() => {
1439
- logger.log(`setSubtitleLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
1440
- window.cefQueryCancel(queryId);
1441
- reject(new RemotePlayerError(6000, `setSubtitleLanguage reached timeout of ${timeout} ms`));
1442
- }, timeout, queryId);
1443
- });
1444
- }
1445
- sdkLogger.error("remotePlayer _selectAudioTrackV3: window.cefQuery is undefined");
1446
- return Promise.resolve(undefined);
1447
-
1448
-
1449
- }
1450
-
1451
-
1452
1315
  /**
1453
1316
  * Enable or disable the subtitles.
1454
1317
  * If the player is in an unloaded state, the request will be applied next time content is played.
@@ -1603,6 +1466,8 @@ class RemotePlayer extends EventTarget {
1603
1466
  // The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
1604
1467
  if (this._videoElement) {
1605
1468
  this._videoElement.playbackRate = 1.0;
1469
+ // on seek we need to reset any on going sync operation
1470
+ this._ptsSessionId++;
1606
1471
  }
1607
1472
 
1608
1473
  // Seeking is in progress
@@ -1675,51 +1540,6 @@ class RemotePlayer extends EventTarget {
1675
1540
  this._isSeekingByApplication = false;
1676
1541
  sdkLogger.info("Seeking: local video element seeking end");
1677
1542
  }
1678
-
1679
- async _atomicSetAudioLanguage() {
1680
- sdkLogger.info("Seeking: local video element seeking start while isPLaying=", this._isPlaying);
1681
-
1682
- this._abortSetAudioLanguage = false;
1683
- this._isSetAudioByApplication = true;
1684
-
1685
- let state = SetAudioLanguageState.INIT;
1686
-
1687
- let previousPendingAudioLanguage = this._pendingAudioLanguage;
1688
- let initialAudioLanguage= this._pendingAudioLanguage;
1689
- let res;
1690
-
1691
- while (!this._abortSetAudioLanguage && state !== SetAudioLanguageState.DONE) {
1692
- try {
1693
- // TODO - Implement the logic for setting audio language
1694
- switch(state) {
1695
- case SetAudioLanguageState.INIT:
1696
- state = this._isPlaying ? SetAudioLanguageState.STOPPED : SetAudioLanguageState.SET;
1697
- break;
1698
- case SetAudioLanguageState.STOPPED:
1699
- await lifecycle.moveToForeground();
1700
- state = SetAudioLanguageState.SET;
1701
- break;
1702
- case SetAudioLanguageState.SET:
1703
- initialAudioLanguage = this._pendingAudioLanguage;
1704
- previousPendingAudioLanguage = this._selectedAudioTrack;
1705
- res = await this._selectAudioTrackV3(initialAudioLanguage, previousPendingAudioLanguage);
1706
- state = SetAudioLanguageState.DONE;
1707
- break;
1708
- }
1709
- } catch (error) {
1710
- sdkLogger.error(`Error during seeking process: ${error.message}`);
1711
- state = SeekState.DONE;
1712
- res = Promise.reject(error);
1713
- } finally {
1714
- if (!this._abortSetAudioLanguage) {
1715
- this._play();
1716
- }
1717
- this._isSetAudioByApplication = false;
1718
- sdkLogger.info("Seeking: local video element seeking end");
1719
- }
1720
- }
1721
- return res
1722
- }
1723
1543
  }
1724
1544
  /**
1725
1545
  *
package/src/utils.js CHANGED
@@ -155,15 +155,6 @@ export const TargetPlayingState = Object.freeze({
155
155
  PLAYING_ABR: "playingAbr"
156
156
  });
157
157
 
158
- export const SetAudioLanguageState = Object.freeze({
159
- INIT: "init",
160
- STOPPED: "stopped",
161
- SET: "set",
162
- MULTI_SET: "multiSet",
163
- WAITING: "waiting",
164
- DONE: "done"
165
- });
166
-
167
158
  export const iso6393to1 = {
168
159
  "aar": "aa",
169
160
  "abk": "ab",