senza-sdk 4.5.6 → 4.5.8
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/dist/implementation.bundle.js +1 -1
- package/package.json +1 -1
- package/src/implementation/lifecycle.js +3 -0
- package/src/implementation/remotePlayer.js +103 -16
- package/src/interface/lifecycle.js +3 -1
- package/src/interface/remotePlayer.js +3 -3
- package/src/interface/version.js +1 -1
package/package.json
CHANGED
|
@@ -228,6 +228,9 @@ class Lifecycle extends LifecycleInterface {
|
|
|
228
228
|
} else if (triggerEvent.type === "hdmiStatusChanged") {
|
|
229
229
|
sdkLogger.info("The hdmiStatusChanged data is sent in the callback after uiReady is called");
|
|
230
230
|
}
|
|
231
|
+
} else if (this._connectReason === "tc_reconnect") {
|
|
232
|
+
this._triggerEvent.type = "restoreApplication";
|
|
233
|
+
this._connectReason = this.ConnectReason.APPLICATION_RECONNECT;
|
|
231
234
|
}
|
|
232
235
|
if (triggerEvent.fcid) {
|
|
233
236
|
this._triggerEventFcid = triggerEvent.fcid;
|
|
@@ -3,7 +3,6 @@ import { RemotePlayer as RemotePlayerInterface, RemotePlayerError as RemotePlaye
|
|
|
3
3
|
import {
|
|
4
4
|
getFCID,
|
|
5
5
|
generatePlaybackId,
|
|
6
|
-
getRestResponse,
|
|
7
6
|
isAudioSyncConfigured,
|
|
8
7
|
clearTimer,
|
|
9
8
|
sdkLogger,
|
|
@@ -154,6 +153,21 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
154
153
|
* @private
|
|
155
154
|
*/
|
|
156
155
|
this._playbackId = "";
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Anchor position in seconds used by getPlaybackPosition().
|
|
159
|
+
* @type {number}
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
this._playbackPositionAnchor = getPlaybackInfo()?.playbackPosition || 0;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Timestamp in msec from which elapsed play time is accumulated.
|
|
166
|
+
* Undefined means playback clock is currently paused.
|
|
167
|
+
* @type {number | undefined}
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
this._playbackClockStartTimestampMs = undefined;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
/** @private Initialize the remote player
|
|
@@ -199,6 +213,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
199
213
|
this._autoTune = false;
|
|
200
214
|
|
|
201
215
|
this._isPlaying = window?.sessionStorage?.getItem("senzaSdk_isPlaying") === "true";
|
|
216
|
+
if (this._isPlaying) {
|
|
217
|
+
this._playbackPositionAnchor = getPlaybackInfo()?.playbackPosition || 0;
|
|
218
|
+
this._startPlaybackClock();
|
|
219
|
+
} else {
|
|
220
|
+
this._stopPlaybackClock();
|
|
221
|
+
}
|
|
202
222
|
|
|
203
223
|
sdkLogger.info(`RemotePlayer initialized with load mode: ${this._loadMode}, senzaSdk_isPlaying: ${this._isPlaying}`);
|
|
204
224
|
this._loadedUrl = playerState?.playbackUrl || "";
|
|
@@ -267,6 +287,16 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
267
287
|
}
|
|
268
288
|
});
|
|
269
289
|
|
|
290
|
+
typeof document !== "undefined" && document.addEventListener("hs/playbackPosition", (e) => {
|
|
291
|
+
if (this._hasPlaybackIdMismatch(e?.detail, "hs/playbackPosition")) return;
|
|
292
|
+
// while seeking we do not want to update the playback position anchor, as it will cause the playback position to jump back to the previous position
|
|
293
|
+
if (!this._isSeekingByApplication) {
|
|
294
|
+
const detail = e?.detail;
|
|
295
|
+
const playbackPosition = typeof detail === "number" ? detail : detail?.data?.UiExpectedPts;
|
|
296
|
+
this._setPlaybackPositionAnchor(playbackPosition);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
270
300
|
typeof document !== "undefined" && document.addEventListener("hs/playback", (e) => {
|
|
271
301
|
if (this._hasPlaybackIdMismatch(e?.detail, "hs/playback")) return;
|
|
272
302
|
sdkLogger.info("Got hs/playback event with detail", JSON.stringify(e?.detail));
|
|
@@ -422,17 +452,74 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
422
452
|
|
|
423
453
|
/** @private change the internal load mode and store the new mode in session storage */
|
|
424
454
|
_changePlayMode(newPlayMode) {
|
|
425
|
-
|
|
455
|
+
const didPlayModeChange = this._isPlaying !== newPlayMode;
|
|
456
|
+
if (didPlayModeChange) {
|
|
426
457
|
// Create event with play mode detail
|
|
427
458
|
const event = new CustomEvent("playModeChange", {
|
|
428
459
|
detail: { isPlaying: newPlayMode }
|
|
429
460
|
});
|
|
430
461
|
this.dispatchEvent(event);
|
|
462
|
+
if (newPlayMode) {
|
|
463
|
+
this._startPlaybackClock();
|
|
464
|
+
}
|
|
431
465
|
}
|
|
432
466
|
this._isPlaying = newPlayMode;
|
|
467
|
+
|
|
468
|
+
if (!newPlayMode) {
|
|
469
|
+
this._stopPlaybackClock();
|
|
470
|
+
}
|
|
433
471
|
window?.sessionStorage?.setItem("senzaSdk_isPlaying", String(newPlayMode));
|
|
434
472
|
}
|
|
435
473
|
|
|
474
|
+
/**
|
|
475
|
+
* @private
|
|
476
|
+
* @param {number} playbackPosition
|
|
477
|
+
*/
|
|
478
|
+
_setPlaybackPositionAnchor(playbackPosition) {
|
|
479
|
+
if (typeof playbackPosition !== "number" || !Number.isFinite(playbackPosition)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this._playbackPositionAnchor = playbackPosition;
|
|
484
|
+
if (this._playbackClockStartTimestampMs !== undefined) {
|
|
485
|
+
this._playbackClockStartTimestampMs = Date.now();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* @private
|
|
491
|
+
*/
|
|
492
|
+
_startPlaybackClock() {
|
|
493
|
+
if (this._playbackClockStartTimestampMs === undefined) {
|
|
494
|
+
this._playbackClockStartTimestampMs = Date.now();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* @private
|
|
500
|
+
*/
|
|
501
|
+
_stopPlaybackClock() {
|
|
502
|
+
if (this._playbackClockStartTimestampMs === undefined) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const elapsedSec = (Date.now() - this._playbackClockStartTimestampMs) / 1000;
|
|
507
|
+
this._playbackPositionAnchor += Math.max(0, elapsedSec);
|
|
508
|
+
this._playbackClockStartTimestampMs = undefined;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* @private
|
|
513
|
+
* @returns {number}
|
|
514
|
+
*/
|
|
515
|
+
_getSyncedPlaybackPosition() {
|
|
516
|
+
if (this._playbackClockStartTimestampMs === undefined) {
|
|
517
|
+
return this._playbackPositionAnchor;
|
|
518
|
+
}
|
|
519
|
+
const elapsedSec = (Date.now() - this._playbackClockStartTimestampMs) / 1000;
|
|
520
|
+
return this._playbackPositionAnchor + Math.max(0, elapsedSec);
|
|
521
|
+
}
|
|
522
|
+
|
|
436
523
|
|
|
437
524
|
/** @private manage getting the local player current video frame media time and sending to the client
|
|
438
525
|
* */
|
|
@@ -498,12 +585,15 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
498
585
|
this._availabilityStartTime = undefined;
|
|
499
586
|
this._isSeekingByPlatform = false;
|
|
500
587
|
this._isSeekingByApplication = false;
|
|
588
|
+
this._stopPlaybackClock();
|
|
589
|
+
this._playbackPositionAnchor = 0;
|
|
501
590
|
}
|
|
502
591
|
|
|
503
592
|
_seek(playbackPosition, waitForResponse = true) {
|
|
504
593
|
if (!this._isInitialized) {
|
|
505
594
|
throw new RemotePlayerError(6500, "Cannot call seek() if remote player is not initialized");
|
|
506
595
|
}
|
|
596
|
+
this._setPlaybackPositionAnchor(playbackPosition);
|
|
507
597
|
setPlaybackInfo({ playbackPosition });
|
|
508
598
|
if (window.cefQuery) {
|
|
509
599
|
if (this._loadMode !== this.LoadMode.LOADED) {
|
|
@@ -632,6 +722,9 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
632
722
|
}
|
|
633
723
|
|
|
634
724
|
_pause() {
|
|
725
|
+
// Stop the playback clock whenever we pause, including during internal seek operations
|
|
726
|
+
this._stopPlaybackClock();
|
|
727
|
+
|
|
635
728
|
if (window.cefQuery) {
|
|
636
729
|
const isForegroundState = lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND;
|
|
637
730
|
if (this._remotePlayerApiVersion >= 2 && !this._isAudioSyncEnabled() && isForegroundState) {
|
|
@@ -826,12 +919,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
826
919
|
this._changeLoadMode(this.LoadMode.LOADING);
|
|
827
920
|
this._changePlayMode(false);
|
|
828
921
|
return new Promise((resolve, reject) => {
|
|
922
|
+
const playbackPosition = position || 0;
|
|
829
923
|
if (reset) {
|
|
830
924
|
// first, zero playback position
|
|
831
925
|
setPlaybackInfo({ playbackPosition: 0 });
|
|
832
926
|
}
|
|
833
927
|
const FCID = getFCID();
|
|
834
|
-
const playbackPosition = position ?? 0;
|
|
835
928
|
const logger = sdkLogger.withFields({ FCID, loadUrl: url, playbackPosition });
|
|
836
929
|
const audioLanguage = audioTrackId || this._selectedAudioTrack || this._config.preferredAudioLanguage || "";
|
|
837
930
|
const subtitlesLanguage = textTrackId || this._selectedSubtitlesTrack || this._config.preferredSubtitlesLanguage || "";
|
|
@@ -876,8 +969,9 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
876
969
|
onSuccess: () => {
|
|
877
970
|
const duration = Date.now() - timeBeforeSendingRequest;
|
|
878
971
|
logger.withFields({ duration }).log(`${message.type} completed successfully after ${duration} ms`);
|
|
879
|
-
if (
|
|
880
|
-
|
|
972
|
+
if (playbackPosition > 0) {
|
|
973
|
+
this._setPlaybackPositionAnchor(playbackPosition);
|
|
974
|
+
setPlaybackInfo({ playbackPosition });
|
|
881
975
|
}
|
|
882
976
|
timerId = clearTimer(timerId);
|
|
883
977
|
this._changeLoadMode(this.LoadMode.LOADED);
|
|
@@ -1138,7 +1232,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1138
1232
|
* @returns {Promise<number|undefined>} current playback position
|
|
1139
1233
|
* @throws {RemotePlayerError} error object contains code & msg
|
|
1140
1234
|
*/
|
|
1141
|
-
|
|
1235
|
+
getPlaybackPosition() {
|
|
1142
1236
|
if (!this._isInitialized) {
|
|
1143
1237
|
throw new RemotePlayerError(6500, "Cannot call getPlaybackPosition() if remote player is not initialized");
|
|
1144
1238
|
}
|
|
@@ -1146,15 +1240,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1146
1240
|
throw new RemotePlayerError(6001, "Cannot call getPlaybackPosition() if player is not loaded");
|
|
1147
1241
|
}
|
|
1148
1242
|
|
|
1149
|
-
|
|
1150
|
-
const response = await getRestResponse("playback-position");
|
|
1151
|
-
const { currentVideoPosition } = JSON.parse(response || "{}");
|
|
1152
|
-
return currentVideoPosition;
|
|
1153
|
-
} catch (error) {
|
|
1154
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1155
|
-
sdkLogger.error(`remotePlayer getPlaybackPosition failed: ${message}`);
|
|
1156
|
-
throw new RemotePlayerError(6504, `getPlaybackPosition() failed: ${message}`);
|
|
1157
|
-
}
|
|
1243
|
+
return this._getSyncedPlaybackPosition();
|
|
1158
1244
|
}
|
|
1159
1245
|
|
|
1160
1246
|
/**
|
|
@@ -1572,6 +1658,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1572
1658
|
* Getter/Setter for currentTime
|
|
1573
1659
|
*/
|
|
1574
1660
|
get currentTime() {
|
|
1661
|
+
// Todo - this can be replaced with the getPlaybackPosition, but it might cause a regression so we can leave it for now.
|
|
1575
1662
|
return getPlaybackInfo()?.playbackPosition;
|
|
1576
1663
|
}
|
|
1577
1664
|
set currentTime(playbackPosition) {
|
|
@@ -1674,7 +1761,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1674
1761
|
|
|
1675
1762
|
if (!this._isSeekingByPlatform) {
|
|
1676
1763
|
this._pendingSeekPosition = playbackPosition;
|
|
1677
|
-
|
|
1764
|
+
this._setPlaybackPositionAnchor(playbackPosition);
|
|
1678
1765
|
// If the local player is just initializing after loading the manifest, ignore the seeking event.
|
|
1679
1766
|
if (this._localPlayerLoadCurrentTime === 0 && this._videoElement.currentTime > ONE_DAY_SECONDS) {
|
|
1680
1767
|
sdkLogger.info(`Seeking ignored for video currentTime init: currentTime: ${this._videoElement.currentTime}`);
|
|
@@ -83,6 +83,7 @@ class Lifecycle extends EventTarget {
|
|
|
83
83
|
* @property {string} UI_TERMINATION - Indicates that ui app has been reloaded due to ui termination
|
|
84
84
|
* @property {string} WEBRTC_ERROR - Indicates that ui app has been reloaded due to webrtc error
|
|
85
85
|
* @property {string} UI_WATCHDOG - Indicates that ui app has been reloaded due to ui watchdog not receiving ui frames
|
|
86
|
+
* @property {string} APPLICATION_RECONNECT - Indicates that ui app has been reloaded due to connector reconnect (e.g. after connector reconnect) and should be restored
|
|
86
87
|
*/
|
|
87
88
|
ConnectReason = Object.freeze({
|
|
88
89
|
UNKNOWN: "unknown",
|
|
@@ -91,7 +92,8 @@ class Lifecycle extends EventTarget {
|
|
|
91
92
|
UI_RELEASE: "ui_release",
|
|
92
93
|
UI_TERMINATION: "ui_termination",
|
|
93
94
|
WEBRTC_ERROR: "webrtc_error",
|
|
94
|
-
UI_WATCHDOG: "ui_watchdog"
|
|
95
|
+
UI_WATCHDOG: "ui_watchdog",
|
|
96
|
+
APPLICATION_RECONNECT: "reconnect_app"
|
|
95
97
|
});
|
|
96
98
|
|
|
97
99
|
/**
|
|
@@ -357,11 +357,11 @@ class RemotePlayer extends EventTarget {
|
|
|
357
357
|
return this.LoadMode.NOT_LOADED;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
/** Get the playback position
|
|
361
|
-
* @returns {
|
|
360
|
+
/** Get the current playback position.
|
|
361
|
+
* @returns {number|undefined} current playback position
|
|
362
362
|
* @throws {RemotePlayerError} error object contains code & msg
|
|
363
363
|
*/
|
|
364
|
-
|
|
364
|
+
getPlaybackPosition() {
|
|
365
365
|
return 0;
|
|
366
366
|
}
|
|
367
367
|
|
package/src/interface/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "4.5.
|
|
1
|
+
export const version = "4.5.8";
|