senza-sdk 4.3.7-b2908c5.0 → 4.4.1-4ca75a1.0

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.
@@ -35,6 +35,13 @@
35
35
  SOFTWARE.
36
36
  */
37
37
 
38
+ /*
39
+ @license
40
+ EME Encryption Scheme Polyfill
41
+ Copyright 2019 Google LLC
42
+ SPDX-License-Identifier: Apache-2.0
43
+ */
44
+
38
45
  /*
39
46
  @license
40
47
  MSS Transmuxer
@@ -65,9 +72,9 @@
65
72
 
66
73
  /*
67
74
  @license
68
- Shaka Player
69
- Copyright 2025 Google LLC
70
- SPDX-License-Identifier: Apache-2.0
75
+ glMatrix: https://github.com/toji/gl-matrix/
76
+ Copyright 2015-2021, Brandon Jones, Colin MacKenzie IV
77
+ SPDX-License-Identifier: MIT
71
78
  */
72
79
 
73
80
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "senza-sdk",
3
- "version": "4.3.7-b2908c5.0",
3
+ "version": "4.4.1-4ca75a1.0",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
@@ -53,15 +53,15 @@
53
53
  "statements": 93.46
54
54
  },
55
55
  "src/interface": {
56
- "branches": 63.9,
57
- "functions": 39.28,
58
- "lines": 64.61,
59
- "statements": 65
56
+ "branches": 68.42,
57
+ "functions": 39.63,
58
+ "lines": 64.94,
59
+ "statements": 65.32
60
60
  }
61
61
  }
62
62
  },
63
63
  "dependencies": {
64
- "shaka-player": "4.15.8",
64
+ "shaka-player": "^4.12.5",
65
65
  "moment": "^2.30.1"
66
66
  }
67
67
  }
package/src/api.js CHANGED
@@ -370,31 +370,6 @@ export function setTimezone(timezone) {
370
370
  }
371
371
  }
372
372
 
373
- /**
374
- * @deprecated use deviceManager.deviceInfo instead.
375
- * Returns the device information, including device id, connection id, community, tenant and client ip
376
- * */
377
- export function getDeviceInfo() {
378
- // STOP DO NOT CHANGE THIS FUNCTION AS IT IS DEPRECATED!!!!
379
- if (isRunningE2E()) {
380
- return {
381
- deviceId: deviceManager.deviceInfo.deviceId,
382
- connectionId: deviceManager.deviceInfo.connectionId,
383
- community: deviceManager.deviceInfo.community,
384
- tenant: deviceManager.deviceInfo.tenant,
385
- clientIp: deviceManager.deviceInfo.clientIp
386
- };
387
- }
388
- sdkLogger.log("getDeviceInfo running locally, returning dummy info");
389
- return {
390
- deviceId: "123456789",
391
- connectionId: "dummy",
392
- community: "LocalDev",
393
- tenant: "XXXXXX",
394
- clientIp: "0.0.0.0"
395
- };
396
- }
397
-
398
373
  export { lifecycle, remotePlayer, alarmManager, deviceManager, platformManager, messageManager, ShakaPlayerImplementation as ShakaPlayer, shakaImplementation as shaka, showSequence, initSequence };
399
374
 
400
375
  // Assign the senza library to the old name (hs) for backward compatibility
@@ -130,7 +130,7 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
130
130
 
131
131
  // Initialize lifecycle first to make sure the state is updated.
132
132
  await lifecycle._init(sessionInfoObj?.settings?.["ui-streamer"], triggerEvent);
133
- await remotePlayer._init(sessionInfoObj?.settings?.["ui-streamer"], triggerEvent);
133
+ await remotePlayer._init(sessionInfoObj, triggerEvent);
134
134
  alarmManager._init();
135
135
  messageManager._init();
136
136
  sdkLogger.log("All submodules initialized");
@@ -29,7 +29,9 @@ class Lifecycle extends LifecycleInterface {
29
29
  * @private
30
30
  */
31
31
  this._isInitialized = false;
32
- this._inTransition = false;
32
+ this._inTransitionToForeground = false;
33
+ this._inTransitionToBackground = false;
34
+ this._inTransitionToStandby = false;
33
35
 
34
36
  /**
35
37
  * Event listeners manager for the userdisconnected event
@@ -310,7 +312,7 @@ class Lifecycle extends LifecycleInterface {
310
312
  // This api is part of epic HSDEV-713
311
313
  _moveToUiStandby() {
312
314
  if (window.cefQuery) {
313
- this._inTransition = true;
315
+ this._inTransitionToStandby = true;
314
316
  return new Promise((resolve, reject) => {
315
317
  const FCID = getFCID();
316
318
  const request = { target: "TC", waitForResponse: false, internalAction: "uiExit", message: JSON.stringify({ type: "uiStandbyRequest", fcid: FCID }) };
@@ -321,12 +323,12 @@ class Lifecycle extends LifecycleInterface {
321
323
  persistent: false,
322
324
  onSuccess: () => {
323
325
  logger.log("[ moveToUiStandby ] moveToUiStandby successfully sent");
324
- this._inTransition = false;
326
+ this._inTransitionToStandby = false;
325
327
  resolve(true);
326
328
  },
327
329
  onFailure: (code, msg) => {
328
330
  logger.error(`[ moveToUiStandby ] moveToUiStandby failed: ${code} ${msg}`);
329
- this._inTransition = false;
331
+ this._inTransitionToStandby = false;
330
332
  reject(`moveToUiStandby failed: ${code} ${msg}`);
331
333
  }
332
334
  });
@@ -423,6 +425,14 @@ class Lifecycle extends LifecycleInterface {
423
425
  this._countdown = null;
424
426
  }
425
427
 
428
+ /**
429
+ * @private
430
+ */
431
+ _isInTransition() {
432
+ return this._inTransitionToForeground || this._inTransitionToBackground || this._inTransitionToStandby;
433
+ }
434
+
435
+
426
436
  getState() {
427
437
  if (window.cefQuery) {
428
438
  return new Promise((resolve, reject) => {
@@ -445,14 +455,19 @@ class Lifecycle extends LifecycleInterface {
445
455
 
446
456
  moveToForeground() {
447
457
  if (window.cefQuery) {
448
- if (this._inTransition || this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND) {
449
- sdkLogger.warn(`lifecycle moveToForeground: No need to transition to foreground, state: ${this._state} transition: ${this._inTransition}`);
458
+ const inTransition = this._isInTransition();
459
+ if (inTransition || this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND) {
460
+ sdkLogger.warn(`lifecycle moveToForeground: No need to transition to foreground, state: ${this._state} transition: ${inTransition}`);
450
461
  return Promise.resolve(false);
451
462
  }
452
- this._inTransition = true;
463
+ this._inTransitionToForeground = true;
453
464
  alarmManager._moveToForegroundCalled();
454
465
  const FCID = getFCID();
455
466
  if (this._remotePlayerApiVersion >= 2) {
467
+ // Only update to playing UI if we started seeking in ABR. But, if we are seeking while already paused, keep the target seek state as is.
468
+ if (remotePlayer._isSeekingByApplication && remotePlayer._targetSeekPlayingState === TargetPlayingState.PLAYING_ABR) {
469
+ remotePlayer._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
470
+ }
456
471
  return new Promise((resolve, reject) => {
457
472
  const FCID = getFCID();
458
473
  const logger = sdkLogger.withFields({ FCID });
@@ -472,14 +487,14 @@ class Lifecycle extends LifecycleInterface {
472
487
  onSuccess: () => {
473
488
  const duration = Date.now() - timeBeforeSendingRequest;
474
489
  logger.withFields({ duration }).log(`stop completed successfully after ${duration} ms`);
475
- this._inTransition = false;
490
+ this._inTransitionToForeground = false;
476
491
  timerId = clearTimer(timerId);
477
492
  resolve(true);
478
493
  },
479
494
  onFailure: (code, msg) => {
480
495
  const duration = Date.now() - timeBeforeSendingRequest;
481
496
  logger.withFields({ duration }).log(`stop failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
482
- this._inTransition = false;
497
+ this._inTransitionToForeground = false;
483
498
  timerId = clearTimer(timerId);
484
499
  reject(new SenzaError(code, msg));
485
500
  }
@@ -488,7 +503,7 @@ class Lifecycle extends LifecycleInterface {
488
503
  const timeout = this._remotePlayerConfirmationTimeout + 1000;
489
504
  timerId = setTimeout(() => {
490
505
  logger.log(`stop reached timeout of ${timeout} ms, canceling query id ${queryId}`);
491
- this._inTransition = false;
506
+ this._inTransitionToForeground = false;
492
507
  window.cefQueryCancel(queryId);
493
508
  reject(new SenzaError(6000, `stop reached timeout of ${timeout} ms`));
494
509
  }, timeout, queryId);
@@ -503,11 +518,11 @@ class Lifecycle extends LifecycleInterface {
503
518
  persistent: false,
504
519
  onSuccess: () => {
505
520
  logger.log("uiActiveRequest successfully sent");
506
- this._inTransition = false;
521
+ this._inTransitionToForeground = false;
507
522
  resolve(true);
508
523
  },
509
524
  onFailure: (code, msg) => {
510
- this._inTransition = false;
525
+ this._inTransitionToForeground = false;
511
526
  logger.error(`uiActiveRequest failed: ${code} ${msg}`);
512
527
  reject(`uiActiveRequest failed: ${code} ${msg}`);
513
528
  }
@@ -520,10 +535,6 @@ class Lifecycle extends LifecycleInterface {
520
535
 
521
536
  _moveToBackground() {
522
537
  if (window.cefQuery) {
523
- if (this._inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
524
- sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${this._inTransition}`);
525
- return Promise.resolve(false);
526
- }
527
538
  // If audio sync is disabled, we only need to sync before remote player starts playing
528
539
  if (!isAudioSyncConfigured()) {
529
540
  remotePlayer._syncRemotePlayerWithLocalPlayer();
@@ -534,8 +545,8 @@ class Lifecycle extends LifecycleInterface {
534
545
  if (!remotePlayer._isPlaying) {
535
546
  return this._moveToUiStandby();
536
547
  }
537
-
538
- this._inTransition = true;
548
+ remotePlayer._changePlayMode(true);
549
+ this._inTransitionToBackground = true;
539
550
  return new Promise((resolve, reject) => {
540
551
  const FCID = getFCID();
541
552
  const logger = sdkLogger.withFields({ FCID });
@@ -553,7 +564,8 @@ class Lifecycle extends LifecycleInterface {
553
564
  if (this._remotePlayerApiVersion >= 2) {
554
565
  message.type = "remotePlayer.play";
555
566
  message.class = "remotePlayer";
556
- message.switchMode = remotePlayer._isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
567
+ // in background, client expects to get NON_SEAMLESS switch mode.
568
+ message.switchMode = remotePlayer._isAudioSyncEnabled() && this._state !== this.UiState.BACKGROUND ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
557
569
  message.streamType = remotePlayer.textTrackVisibility ? (StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE) : (StreamType.AUDIO | StreamType.VIDEO);
558
570
  request = {
559
571
  target: "TC",
@@ -576,14 +588,14 @@ class Lifecycle extends LifecycleInterface {
576
588
  onSuccess: () => {
577
589
  const duration = Date.now() - timeBeforeSendingRequest;
578
590
  logger.withFields({ duration }).log(`play completed successfully after ${duration} ms`);
579
- this._inTransition = false;
591
+ this._inTransitionToBackground = false;
580
592
  timerId = clearTimer(timerId);
581
593
  resolve();
582
594
  },
583
595
  onFailure: (code, msg) => {
584
596
  const duration = Date.now() - timeBeforeSendingRequest;
585
597
  logger.withFields({ duration }).log(`play failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
586
- this._inTransition = false;
598
+ this._inTransitionToBackground = false;
587
599
  timerId = clearTimer(timerId);
588
600
  reject(new SenzaError(code, msg));
589
601
  }
@@ -593,7 +605,7 @@ class Lifecycle extends LifecycleInterface {
593
605
  const timeout = this._remotePlayerConfirmationTimeout + 1000;
594
606
  timerId = setTimeout(() => {
595
607
  logger.log(`play reached timeout of ${timeout} ms, canceling query id ${queryId}`);
596
- this._inTransition = false;
608
+ this._inTransitionToBackground = false;
597
609
  window.cefQueryCancel(queryId);
598
610
  reject(new SenzaError(6000, `play reached timeout of ${timeout} ms`));
599
611
  }, timeout, queryId);
@@ -606,11 +618,11 @@ class Lifecycle extends LifecycleInterface {
606
618
 
607
619
  moveToBackground() {
608
620
  if (window.cefQuery) {
609
- if (this._inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
610
- sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${this._inTransition}`);
621
+ const inTransition = this._isInTransition();
622
+ if (inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
623
+ sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${inTransition}`);
611
624
  return Promise.resolve(false);
612
625
  }
613
-
614
626
  if (remotePlayer._isSeekingByApplication) {
615
627
  remotePlayer._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
616
628
  return Promise.resolve(true);
@@ -11,7 +11,8 @@ import {
11
11
  SeekState,
12
12
  TargetPlayingState,
13
13
  isSubtitlesTranslationAllowed,
14
- isSubtitlesTranslationPattern
14
+ isSubtitlesTranslationPattern,
15
+ isAppVersionAboveOrEqual
15
16
  } from "./utils";
16
17
  import { lifecycle } from "./lifecycle";
17
18
  import { writeLicenseResponse } from "./api";
@@ -143,10 +144,12 @@ class RemotePlayer extends RemotePlayerInterface {
143
144
  }
144
145
 
145
146
  /** @private Initialize the remote player
146
- * @param {Object} uiStreamerSettings ui-streamer portion of the settings taken from session info
147
+ * @param {Object} sessionObj session information object
148
+ * @param {Object} triggerEvent trigger event object
147
149
  * */
148
- async _init(uiStreamerSettings, triggerEvent) {
150
+ async _init(sessionObj, triggerEvent) {
149
151
  sdkLogger.info("Initializing RemotePlayer");
152
+ const uiStreamerSettings = sessionObj?.settings?.["ui-streamer"];
150
153
  let playerState = {
151
154
  isLoaded: false,
152
155
  playbackUrl: ""
@@ -193,6 +196,7 @@ class RemotePlayer extends RemotePlayerInterface {
193
196
  this._remotePlayerConfirmationTimeout = uiStreamerSettings?.remotePlayerConfirmationTimeout ?? DEFAULT_REMOTE_PLAYER_CONFIRMATION_TIMEOUT;
194
197
  this._remotePlayerApiVersion = uiStreamerSettings?.remotePlayerApiVersion || 1;
195
198
  this._multiSeekDelay = uiStreamerSettings?.multiSeekDelay || MULTI_SEEK_DELAY_MSEC;
199
+ this._deviceAppVersion = sessionObj?.manifest?.["device-app"] || "";
196
200
 
197
201
  sdkLogger.info(`remotePLayer isPlaying=${this._isPlaying}`);
198
202
 
@@ -527,7 +531,8 @@ class RemotePlayer extends RemotePlayerInterface {
527
531
  };
528
532
  let waitForResponse = false;
529
533
  if (this._remotePlayerApiVersion >= 2) {
530
- message.switchMode = this._isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
534
+ // in background, client expects to get NON_SEAMLESS switch mode.
535
+ message.switchMode = this._isAudioSyncEnabled() && lifecycle.state !== lifecycle.UiState.BACKGROUND ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
531
536
  message.streamType = streamType;
532
537
  waitForResponse = true;
533
538
 
@@ -948,8 +953,13 @@ class RemotePlayer extends RemotePlayerInterface {
948
953
 
949
954
  // If seeking in progress, wait for seek to complete before playing
950
955
  if (this._isSeekingByApplication) {
951
- sdkLogger.info("application requesting play during seek");
952
- this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
956
+ if (this._inTransitionToForeground || lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND) {
957
+ sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_UI");
958
+ this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
959
+ } else {
960
+ sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_ABR");
961
+ this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
962
+ }
953
963
  return Promise.resolve(true);
954
964
  }
955
965
  /*
@@ -1081,30 +1091,24 @@ class RemotePlayer extends RemotePlayerInterface {
1081
1091
  * @throws {RemotePlayerError} If the player is not initialized or not loaded.
1082
1092
  **/
1083
1093
  selectAudioTrack(audioTrackId) {
1084
- return this._selectAudioTrack(audioTrackId, false);
1085
- }
1086
-
1087
- _selectAudioTrack(audioTrackId, force = false) {
1088
1094
  if (!this._isInitialized) {
1089
1095
  throw new RemotePlayerError(6500, "Cannot call selectAudioTrack() if remote player is not initialized");
1090
1096
  }
1091
1097
  if (this._loadMode !== this.LoadMode.LOADED) {
1092
1098
  throw new RemotePlayerError(6001, "Cannot call selectAudioTrack() if remote player is not loaded");
1093
1099
  }
1094
- if (!force) {
1095
- let found = false;
1096
- for (const track of this.getAudioTracks()) {
1097
- if (track.id === audioTrackId) {
1098
- found = true;
1099
- break;
1100
- }
1101
- }
1102
- if (!found) {
1103
- sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
1104
- return Promise.resolve();
1100
+ let found = false;
1101
+ const prevSelectedAudioTrack = this._selectedAudioTrack;
1102
+ for (const track of this.getAudioTracks()) {
1103
+ if (track.id === audioTrackId) {
1104
+ found = true;
1105
+ break;
1105
1106
  }
1106
1107
  }
1107
- const prevSelectedAudioTrack = this._selectedAudioTrack;
1108
+ if (!found) {
1109
+ sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
1110
+ return Promise.resolve();
1111
+ }
1108
1112
  if (this._selectedAudioTrack === audioTrackId) {
1109
1113
  return Promise.resolve(); // Audio language already selected
1110
1114
  }
@@ -1121,12 +1125,6 @@ class RemotePlayer extends RemotePlayerInterface {
1121
1125
  }
1122
1126
  }
1123
1127
 
1124
- async selectAudioLanguage(language, role="", accessibilityPurposeCode=this.AccessibilityPurposeCode.NONE) {
1125
- const trackId = this._generateSenzaTrackId(language, role, accessibilityPurposeCode);
1126
- return this._selectAudioTrack(trackId, true);
1127
- }
1128
-
1129
-
1130
1128
  /**
1131
1129
  * Handles the asynchronous selection of an audio track.
1132
1130
  * If the player is playing, it pauses before changing the track and resumes playback if necessary.
@@ -1220,11 +1218,6 @@ class RemotePlayer extends RemotePlayerInterface {
1220
1218
  return Promise.resolve(undefined);
1221
1219
  }
1222
1220
 
1223
- async selectTextLanguage(language, role="", accessibilityPurposeCode=this.AccessibilityPurposeCode.NONE) {
1224
- const trackId = this._generateSenzaTrackId(language, role, accessibilityPurposeCode);
1225
- return this._selectTextTrack(trackId, true);
1226
- }
1227
-
1228
1221
  /** Select a specific text (subtitle) track.
1229
1222
  * Track id should come from a call to getTextTracks.
1230
1223
  * If no tracks exist - this is a no-op.
@@ -1234,31 +1227,24 @@ class RemotePlayer extends RemotePlayerInterface {
1234
1227
  * @throws {RemotePlayerError} If the player is not initialized or not loaded.
1235
1228
  * */
1236
1229
  selectTextTrack(textTrackId) {
1237
- return this._selectTextTrack(textTrackId, false);
1238
- }
1239
-
1240
- _selectTextTrack(textTrackId, force = false) {
1241
1230
  if (!this._isInitialized) {
1242
1231
  throw new RemotePlayerError(6500, "Cannot call selectTextTrack() if remote player is not initialized");
1243
1232
  }
1244
1233
  if (this._loadMode !== this.LoadMode.LOADED) {
1245
1234
  throw new RemotePlayerError(6001, "Cannot call selectTextTrack() if remote player is not loaded");
1246
1235
  }
1247
- if (!force) {
1248
- let found = false;
1249
- for (const track of this.getTextTracks()) {
1250
- if (track.id === textTrackId) {
1251
- found = true;
1252
- break;
1253
- }
1254
- }
1255
- if (!found) {
1256
- sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
1257
- return Promise.resolve();
1236
+ let found = false;
1237
+ const prevSelectedTextTrack = this._selectedSubtitlesTrack;
1238
+ for (const track of this.getTextTracks()) {
1239
+ if (track.id === textTrackId) {
1240
+ found = true;
1241
+ break;
1258
1242
  }
1259
1243
  }
1260
-
1261
- const prevSelectedTextTrack = this._selectedSubtitlesTrack;
1244
+ if (!found) {
1245
+ sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
1246
+ return Promise.resolve();
1247
+ }
1262
1248
  if (this._selectedSubtitlesTrack === textTrackId) {
1263
1249
  return Promise.resolve(); // Subtitle language already selected
1264
1250
  }
@@ -1534,6 +1520,8 @@ class RemotePlayer extends RemotePlayerInterface {
1534
1520
  * @private
1535
1521
  */
1536
1522
  async _startSeeking(playbackPosition) {
1523
+ const backgroundSeekMinClientAppVersion = "25.27.8";
1524
+
1537
1525
  if (this._isSetAudioInProgress || this._isSetSubtitlesInProgress) {
1538
1526
  sdkLogger.info("Seeking not supported while setAudioLanguage or setSubtitleLanguage are in progress.");
1539
1527
  return;
@@ -1549,10 +1537,12 @@ class RemotePlayer extends RemotePlayerInterface {
1549
1537
  return;
1550
1538
  }
1551
1539
  }
1552
-
1553
- // Only allow seeking in foreground. Still ignore the initialized local player seeking event above
1554
- if (this._remotePlayerApiVersion >= 2 && !this._isSeekingByPlatform && !this._isSeekingByApplication &&
1555
- (lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND)) {
1540
+ const backgroundSeekSupported = isAppVersionAboveOrEqual(this._deviceAppVersion, backgroundSeekMinClientAppVersion);
1541
+ if (!backgroundSeekSupported && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
1542
+ sdkLogger.warn(`Seeking: background seek not supported for device app version: ${this._deviceAppVersion}`);
1543
+ }
1544
+ if (this._remotePlayerApiVersion >= 2 && !this._isSeekingByPlatform && !this._isSeekingByApplication
1545
+ && (lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND || backgroundSeekSupported)) {
1556
1546
  this._atomicSeek();
1557
1547
  } else {
1558
1548
  sdkLogger.info(`Seeking: skipping seeking event to currentTime: ${playbackPosition}, internalSeek: ${this._isSeekingByPlatform}, localPlayerSeek: ${this._isSeekingByApplication}, state: ${lifecycle.state}`);
@@ -1571,16 +1561,16 @@ class RemotePlayer extends RemotePlayerInterface {
1571
1561
  * */
1572
1562
  async _atomicSeek() {
1573
1563
  sdkLogger.info("Seeking: local video element seeking start while isPlaying: ", this._isPlaying);
1574
-
1575
- // Initialize the target playing state unless changed during the seek process
1576
- // In the future, we should allow for seeking in background. Currently, there's no
1577
- // way to know when the web application will call moveToForeground (i.e Before/After seek)
1578
- // Therefore, for now, we will assume the target is either paused or playing in ui unless
1579
- // specifically receiving a moveToBackground during the process.
1580
- // if (this._isPlaying && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
1581
- // this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
1582
- // }
1583
- this._targetSeekPlayingState = this._isPlaying ? TargetPlayingState.PLAYING_UI : TargetPlayingState.PAUSED;
1564
+ if (this._isPlaying) {
1565
+ if (!(lifecycle._inTransitionToForeground || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND) && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
1566
+ sdkLogger.info("seek in background", this._isPlaying);
1567
+ this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
1568
+ } else {
1569
+ this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
1570
+ }
1571
+ } else {
1572
+ this._targetSeekPlayingState = TargetPlayingState.PAUSED;
1573
+ }
1584
1574
 
1585
1575
  // The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
1586
1576
  if (this._videoElement) {
@@ -1647,7 +1637,7 @@ class RemotePlayer extends RemotePlayerInterface {
1647
1637
 
1648
1638
  // If in TargetPlayingState.PAUSE, no need to resume.
1649
1639
  // Resume without awaiting to avoid blocking the seek process anymore
1650
- // In case where we aborted, we don't want to resume playback.
1640
+ // In case where we aborted (new load or unload called), we don't want to resume playback.
1651
1641
  if (!this._abortSeeking) {
1652
1642
  if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_UI) {
1653
1643
  if (!this._isAudioSyncEnabled()) {
@@ -1798,12 +1788,6 @@ class RemotePlayer extends RemotePlayerInterface {
1798
1788
  return setLanguageError ? Promise.reject(setLanguageError) : Promise.resolve();
1799
1789
  }
1800
1790
 
1801
- _generateSenzaTrackId(language, role, accessibilityPurpose) {
1802
- const l = language || "";
1803
- const r = role || "";
1804
- const ap = accessibilityPurpose || "";
1805
- return `${l}:${r}:${ap}`;
1806
- }
1807
1791
  _setScreenBlackout(blackoutTime) {
1808
1792
  if (window.cefQuery) {
1809
1793
  const FCID = getFCID();