senza-sdk 4.5.2 → 4.5.4
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/api.js +4 -3
- package/src/implementation/api.js +5 -3
- package/src/implementation/overlay.js +15 -1
- package/src/implementation/remotePlayer.js +54 -12
- package/src/implementation/utils.js +13 -0
- package/src/interface/overlay.js +6 -3
- package/src/interface/version.js +1 -1
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -153,12 +153,13 @@ export const envInfo = {
|
|
|
153
153
|
* @param {ArrayBuffer|string} licenseResponse a license response that was received from the license server to be passed to platform.
|
|
154
154
|
* @param {string} fcid a fcid received with the license request
|
|
155
155
|
* @param {string} sessionId a sessionId received with the license request
|
|
156
|
-
*
|
|
156
|
+
* @param {string} playbackId the current playbackId, propagated to platform for session correlation
|
|
157
|
+
* In case of success licenseResponse is of type @type {ArrayBuffer}
|
|
157
158
|
* In case of error licenseResponse is of type @type {string}
|
|
158
159
|
*/
|
|
159
|
-
export function writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId) {
|
|
160
|
+
export function writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId, playbackId) {
|
|
160
161
|
if (window?.senzaSDKImplementation) {
|
|
161
|
-
return window.senzaSDKImplementation.writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId);
|
|
162
|
+
return window.senzaSDKImplementation.writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId, playbackId);
|
|
162
163
|
}
|
|
163
164
|
sdkLogger.error("writeLicenseResponse: window.cefQuery is undefined");
|
|
164
165
|
}
|
|
@@ -303,10 +303,11 @@ import "./devHelper.js";
|
|
|
303
303
|
* @param {ArrayBuffer|string} licenseResponse a license response that was received from the license server to be passed to platform.
|
|
304
304
|
* @param {string} fcid a fcid received with the license request
|
|
305
305
|
* @param {string} sessionId a sessionId received with the license request
|
|
306
|
-
*
|
|
306
|
+
* @param {string} playbackId the current playbackId, propagated to platform for session correlation
|
|
307
|
+
* In case of success licenseResponse is of type @type {ArrayBuffer}
|
|
307
308
|
* In case of error licenseResponse is of type @type {string}
|
|
308
309
|
*/
|
|
309
|
-
export function writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId) {
|
|
310
|
+
export function writeLicenseResponse(statusCode, licenseResponse, fcid, sessionId, playbackId) {
|
|
310
311
|
|
|
311
312
|
if (statusCode >= 200 && statusCode < 300) {
|
|
312
313
|
licenseResponse = window.btoa(String.fromCharCode.apply(null, new Uint8Array(licenseResponse))); // to base64
|
|
@@ -316,7 +317,8 @@ export function writeLicenseResponse(statusCode, licenseResponse, fcid, sessionI
|
|
|
316
317
|
const message = {
|
|
317
318
|
type: "updateLicense",
|
|
318
319
|
sessionId,
|
|
319
|
-
fcid
|
|
320
|
+
fcid,
|
|
321
|
+
playbackId
|
|
320
322
|
};
|
|
321
323
|
message[statusCode >= 200 && statusCode < 300 ? "response" : "error"] = licenseResponse;
|
|
322
324
|
const request = { target: "TC", waitForResponse: false, message: JSON.stringify(message) };
|
|
@@ -22,6 +22,8 @@ const OVERLAY_CAPTURE_PRESETS = {
|
|
|
22
22
|
|
|
23
23
|
const DEFAULT_CAPTURE_PRESET = "default";
|
|
24
24
|
const VALID_QUALITIES = new Set(["low", "mid", "high"]);
|
|
25
|
+
const DEFAULT_AUTO_HIDE_DURATION_SEC = 600;
|
|
26
|
+
const OVERLAY_DURATION_SEC_MAX = 65535;
|
|
25
27
|
|
|
26
28
|
class Overlay extends OverlayInterface {
|
|
27
29
|
constructor() {
|
|
@@ -31,7 +33,8 @@ class Overlay extends OverlayInterface {
|
|
|
31
33
|
this._configuration = {
|
|
32
34
|
useTransparency: true,
|
|
33
35
|
overlayCapturePreset: DEFAULT_CAPTURE_PRESET,
|
|
34
|
-
overlayCapturePlan: null
|
|
36
|
+
overlayCapturePlan: null,
|
|
37
|
+
autoHideDurationSec: DEFAULT_AUTO_HIDE_DURATION_SEC
|
|
35
38
|
};
|
|
36
39
|
this._activeCapturePlan = OVERLAY_CAPTURE_PRESETS[DEFAULT_CAPTURE_PRESET];
|
|
37
40
|
this._retryGeneration = 0;
|
|
@@ -138,6 +141,7 @@ class Overlay extends OverlayInterface {
|
|
|
138
141
|
quality,
|
|
139
142
|
conditional
|
|
140
143
|
};
|
|
144
|
+
message.overlayDurationSec = this._configuration.autoHideDurationSec;
|
|
141
145
|
sdkLogger.log(`Overlay: rendering frame x=${x} y=${y} width=${width} height=${height} fcid=${FCID} (batchId=${batchId}, quality=${quality}, conditional=${conditional})`);
|
|
142
146
|
const request = { target: "UI-Streamer", waitForResponse: false, message: JSON.stringify(message) };
|
|
143
147
|
window.cefQuery({
|
|
@@ -226,6 +230,16 @@ class Overlay extends OverlayInterface {
|
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
|
|
233
|
+
if (Object.prototype.hasOwnProperty.call(normalizedConfiguration, "autoHideDurationSec")) {
|
|
234
|
+
const duration = normalizedConfiguration.autoHideDurationSec;
|
|
235
|
+
if (Number.isInteger(duration) && duration >= 0 && duration <= OVERLAY_DURATION_SEC_MAX) {
|
|
236
|
+
normalizedConfiguration.autoHideDurationSec = duration;
|
|
237
|
+
} else {
|
|
238
|
+
sdkLogger.warn(`Overlay: invalid autoHideDurationSec "${duration}", keeping previous value`);
|
|
239
|
+
delete normalizedConfiguration.autoHideDurationSec;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
229
243
|
this._configuration = { ...this._configuration, ...normalizedConfiguration };
|
|
230
244
|
this._activeCapturePlan = this._resolveCapturePlan();
|
|
231
245
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { RemotePlayer as RemotePlayerInterface, RemotePlayerError as RemotePlayerErrorInterface, Config } from "../interface/remotePlayer";
|
|
3
3
|
import {
|
|
4
4
|
getFCID,
|
|
5
|
+
generatePlaybackId,
|
|
5
6
|
isAudioSyncConfigured,
|
|
6
7
|
clearTimer,
|
|
7
8
|
sdkLogger,
|
|
@@ -145,6 +146,13 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
145
146
|
* @private
|
|
146
147
|
*/
|
|
147
148
|
this._isPlaying = false;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @type {string}
|
|
152
|
+
* @description Unique identifier generated on every load attempt. Propagated on all API requests and events.
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
this._playbackId = "";
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
/** @private Initialize the remote player
|
|
@@ -219,17 +227,33 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
219
227
|
|
|
220
228
|
}
|
|
221
229
|
|
|
230
|
+
/**
|
|
231
|
+
* @private Returns true and logs an error when an incoming event's playbackId does not match the current one.
|
|
232
|
+
* @param {Object} detail - The event detail object that may contain a playbackId field.
|
|
233
|
+
* @param {string} eventType - Name of the incoming event, used in the log message.
|
|
234
|
+
*/
|
|
235
|
+
_hasPlaybackIdMismatch(detail, eventType) {
|
|
236
|
+
const incomingId = detail?.playbackId;
|
|
237
|
+
if (incomingId && this._playbackId && incomingId !== this._playbackId) {
|
|
238
|
+
sdkLogger.error(`${eventType}: playbackId mismatch. current=${this._playbackId} incoming=${incomingId}. Ignoring.`);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
222
244
|
/**
|
|
223
245
|
* @private Add event listeners for system events
|
|
224
246
|
*/
|
|
225
247
|
_addSenzaEventListeners() {
|
|
226
248
|
|
|
227
249
|
typeof document !== "undefined" && document.addEventListener("hs/remotePlayerEvent", (e) => {
|
|
250
|
+
if (this._hasPlaybackIdMismatch(e?.detail, "hs/remotePlayerEvent")) return;
|
|
228
251
|
sdkLogger.info("Got hs/remotePlayerEvent event with detail", JSON.stringify(e?.detail));
|
|
229
252
|
this.dispatchEvent(new Event(e?.detail?.eventName));
|
|
230
253
|
});
|
|
231
254
|
|
|
232
|
-
typeof document !== "undefined" && document.addEventListener("hs/playbackInfoEvent", () => {
|
|
255
|
+
typeof document !== "undefined" && document.addEventListener("hs/playbackInfoEvent", (e) => {
|
|
256
|
+
if (this._hasPlaybackIdMismatch(e?.detail, "hs/playbackInfoEvent")) return;
|
|
233
257
|
sdkLogger.info("Got hs/playbackInfoEvent");
|
|
234
258
|
// When attached, the sdk controls the synchronization between the local and remote player.
|
|
235
259
|
if (this._videoElement) {
|
|
@@ -243,13 +267,15 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
243
267
|
});
|
|
244
268
|
|
|
245
269
|
typeof document !== "undefined" && document.addEventListener("hs/playback", (e) => {
|
|
270
|
+
if (this._hasPlaybackIdMismatch(e?.detail, "hs/playback")) return;
|
|
246
271
|
sdkLogger.info("Got hs/playback event with detail", JSON.stringify(e?.detail));
|
|
247
272
|
this._availabilityStartTime = e?.detail?.availabilityStartTime;
|
|
248
273
|
this._updateTracks(e?.detail);
|
|
249
274
|
this.dispatchEvent(new Event("tracksupdate"));
|
|
250
275
|
});
|
|
251
276
|
|
|
252
|
-
typeof document !== "undefined" && document.addEventListener("hs/EOS", () => {
|
|
277
|
+
typeof document !== "undefined" && document.addEventListener("hs/EOS", (e) => {
|
|
278
|
+
if (this._hasPlaybackIdMismatch(e?.detail, "hs/EOS")) return;
|
|
253
279
|
sdkLogger.info("Got hs/EOS event");
|
|
254
280
|
this._changePlayMode(false);
|
|
255
281
|
this.dispatchEvent(new Event("ended"));
|
|
@@ -268,6 +294,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
268
294
|
}
|
|
269
295
|
});
|
|
270
296
|
typeof document !== "undefined" && document.addEventListener("hs/senzaPlayerSetRate", (event) => {
|
|
297
|
+
if (this._hasPlaybackIdMismatch(event?.detail, "hs/senzaPlayerSetRate")) return;
|
|
271
298
|
if (!this._videoElement) return;
|
|
272
299
|
if (this._isSeekingByApplication) {
|
|
273
300
|
sdkLogger.info("Skip senzaPlayerSetRate while seek performed");
|
|
@@ -276,6 +303,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
276
303
|
this._videoElement.playbackRate = event.detail.rate;
|
|
277
304
|
});
|
|
278
305
|
typeof document !== "undefined" && document.addEventListener("hs/senzaPlayerSetTime", (event) => {
|
|
306
|
+
if (this._hasPlaybackIdMismatch(event?.detail, "hs/senzaPlayerSetTime")) return;
|
|
279
307
|
// For simplicity, make sure we only accept syncing the current time if audio sync is enabled
|
|
280
308
|
if (!this._isAudioSyncEnabled()) return;
|
|
281
309
|
if (this._isSeekingByApplication) {
|
|
@@ -291,6 +319,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
291
319
|
|
|
292
320
|
|
|
293
321
|
typeof document !== "undefined" && document.addEventListener("hs/ERR", (event) => {
|
|
322
|
+
if (this._hasPlaybackIdMismatch(event?.detail, "hs/ERR")) return;
|
|
294
323
|
sdkLogger.info("Got hs/ERR event");
|
|
295
324
|
delete event?.detail?.type; // type is always videoPlaybackEvent, so no need to pass it
|
|
296
325
|
delete event?.detail?.eventCode; // eventCode is always ERR, so no need to pass it
|
|
@@ -298,6 +327,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
298
327
|
});
|
|
299
328
|
|
|
300
329
|
typeof document !== "undefined" && document.addEventListener("hs/getLicense", (event) => {
|
|
330
|
+
if (this._hasPlaybackIdMismatch(event?.detail, "hs/getLicense")) return;
|
|
301
331
|
sdkLogger.info("Got hs/getLicense event");
|
|
302
332
|
const getLicenseEventData = event?.detail;
|
|
303
333
|
|
|
@@ -308,9 +338,10 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
308
338
|
this.licenseRequest = licenseRequest;
|
|
309
339
|
const fcid = getLicenseEventData.fcid;
|
|
310
340
|
const sessionId = getLicenseEventData.sessionId;
|
|
341
|
+
const playbackId = this._playbackId;
|
|
311
342
|
const licenseRequestEvent = new CustomEvent("license-request", { "detail": { licenseRequest } });
|
|
312
343
|
licenseRequestEvent.writeLicenseResponse = (statusCode, responseBody) => {
|
|
313
|
-
writeLicenseResponse(statusCode, responseBody, fcid, sessionId);
|
|
344
|
+
writeLicenseResponse(statusCode, responseBody, fcid, sessionId, playbackId);
|
|
314
345
|
};
|
|
315
346
|
this.dispatchEvent(licenseRequestEvent);
|
|
316
347
|
});
|
|
@@ -485,7 +516,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
485
516
|
class: "remotePlayer",
|
|
486
517
|
action: "seek",
|
|
487
518
|
playbackPosition,
|
|
488
|
-
fcid: FCID
|
|
519
|
+
fcid: FCID,
|
|
520
|
+
playbackId: this._playbackId
|
|
489
521
|
};
|
|
490
522
|
const request = { target: "TC", waitForResponse: waitForResponse, message: JSON.stringify(message) };
|
|
491
523
|
return new Promise((resolve, reject) => {
|
|
@@ -544,7 +576,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
544
576
|
fcid: FCID,
|
|
545
577
|
audioLanguage,
|
|
546
578
|
subtitlesLanguage,
|
|
547
|
-
playbackPosition: this.currentTime
|
|
579
|
+
playbackPosition: this.currentTime,
|
|
580
|
+
playbackId: this._playbackId
|
|
548
581
|
};
|
|
549
582
|
let waitForResponse = false;
|
|
550
583
|
if (this._remotePlayerApiVersion >= 2) {
|
|
@@ -611,7 +644,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
611
644
|
type: "remotePlayer.pause",
|
|
612
645
|
class: "remotePlayer",
|
|
613
646
|
action: "pause",
|
|
614
|
-
fcid: FCID
|
|
647
|
+
fcid: FCID,
|
|
648
|
+
playbackId: this._playbackId
|
|
615
649
|
};
|
|
616
650
|
if (this._remotePlayerApiVersion >= 2) {
|
|
617
651
|
message.streamType = this._isAudioSyncEnabled() && isForegroundState ? StreamType.AUDIO : StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE;
|
|
@@ -662,7 +696,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
662
696
|
class: "remotePlayer",
|
|
663
697
|
action: "stop",
|
|
664
698
|
streamType: streamType,
|
|
665
|
-
fcid: FCID
|
|
699
|
+
fcid: FCID,
|
|
700
|
+
playbackId: this._playbackId
|
|
666
701
|
};
|
|
667
702
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
668
703
|
return new Promise((resolve, reject) => {
|
|
@@ -784,6 +819,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
784
819
|
this._abortSeeking = true;
|
|
785
820
|
if (reset) {
|
|
786
821
|
this._reset();
|
|
822
|
+
this._playbackId = generatePlaybackId();
|
|
787
823
|
}
|
|
788
824
|
const previousLoadMode = this._loadMode;
|
|
789
825
|
this._changeLoadMode(this.LoadMode.LOADING);
|
|
@@ -804,7 +840,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
804
840
|
timeout: this._remotePlayerConfirmationTimeout,
|
|
805
841
|
autoPlay: false,
|
|
806
842
|
playbackPosition,
|
|
807
|
-
fcid: FCID
|
|
843
|
+
fcid: FCID,
|
|
844
|
+
playbackId: this._playbackId
|
|
808
845
|
};
|
|
809
846
|
if (this._uiAvSyncIntervalMs !== undefined) {
|
|
810
847
|
message.connectorSettings = message.connectorSettings || {};
|
|
@@ -905,7 +942,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
905
942
|
type: "remotePlayer.unload",
|
|
906
943
|
class: "remotePlayer",
|
|
907
944
|
action: "unload",
|
|
908
|
-
fcid: FCID
|
|
945
|
+
fcid: FCID,
|
|
946
|
+
playbackId: this._playbackId
|
|
909
947
|
};
|
|
910
948
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
911
949
|
let timerId = 0;
|
|
@@ -918,6 +956,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
918
956
|
logger.withFields({ duration }).log(`unload completed successfully after ${duration} ms`);
|
|
919
957
|
timerId = clearTimer(timerId);
|
|
920
958
|
this._reset();
|
|
959
|
+
this._playbackId = "";
|
|
921
960
|
this._changeLoadMode(this.LoadMode.NOT_LOADED);
|
|
922
961
|
this._loadedUrl = undefined;
|
|
923
962
|
resolve();
|
|
@@ -1240,7 +1279,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1240
1279
|
class: "remotePlayer",
|
|
1241
1280
|
action: "setAudioLanguage",
|
|
1242
1281
|
fcid: FCID,
|
|
1243
|
-
language: audioTrackId
|
|
1282
|
+
language: audioTrackId,
|
|
1283
|
+
playbackId: this._playbackId
|
|
1244
1284
|
};
|
|
1245
1285
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1246
1286
|
return new Promise((resolve, reject) => {
|
|
@@ -1387,7 +1427,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1387
1427
|
class: "remotePlayer",
|
|
1388
1428
|
action: "setSubtitleLanguage",
|
|
1389
1429
|
fcid: FCID,
|
|
1390
|
-
language: textTrackId
|
|
1430
|
+
language: textTrackId,
|
|
1431
|
+
playbackId: this._playbackId
|
|
1391
1432
|
};
|
|
1392
1433
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1393
1434
|
return new Promise((resolve, reject) => {
|
|
@@ -1889,7 +1930,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1889
1930
|
class: "remotePlayer",
|
|
1890
1931
|
action: "screenBlackout",
|
|
1891
1932
|
fcid: FCID,
|
|
1892
|
-
blackoutTime
|
|
1933
|
+
blackoutTime,
|
|
1934
|
+
playbackId: this._playbackId
|
|
1893
1935
|
};
|
|
1894
1936
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1895
1937
|
return new Promise((resolve, reject) => {
|
|
@@ -14,6 +14,19 @@ export function getFCID() {
|
|
|
14
14
|
return Math.round(Math.random() * 100000) + "-" + getPlatformInfo().sessionInfo?.connectionId;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export function generatePlaybackId() {
|
|
18
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
19
|
+
const size = 5;
|
|
20
|
+
let id = "";
|
|
21
|
+
const bytes = (typeof crypto !== "undefined" && crypto.getRandomValues)
|
|
22
|
+
? crypto.getRandomValues(new Uint8Array(size))
|
|
23
|
+
: Array.from({ length: size }, () => Math.floor(Math.random() * 256));
|
|
24
|
+
for (let i = 0; i < size; i++) {
|
|
25
|
+
id += alphabet[bytes[i] & 63];
|
|
26
|
+
}
|
|
27
|
+
return id;
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
export class SenzaError extends Error {
|
|
18
31
|
constructor(code, message) {
|
|
19
32
|
super(message);
|
package/src/interface/overlay.js
CHANGED
|
@@ -59,8 +59,8 @@ class Overlay extends EventTarget {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Removes all registered elements
|
|
63
|
-
*
|
|
62
|
+
* Removes all registered elements and deactivates the overlay.
|
|
63
|
+
* After calling removeAllElements(), a new element can be registered.
|
|
64
64
|
*
|
|
65
65
|
* @returns {Promise<boolean>} Resolves to true if successful, rejects with error if failed.
|
|
66
66
|
*/
|
|
@@ -82,7 +82,7 @@ class Overlay extends EventTarget {
|
|
|
82
82
|
*
|
|
83
83
|
* @param {Object} configuration - The new configuration to apply.
|
|
84
84
|
* @param {boolean} [configuration.useTransparency=true] - Controls whether the overlay should be rendered with transparency.
|
|
85
|
-
* When set to `true`, the
|
|
85
|
+
* When set to `true`, the overlay is rendered with transparency.
|
|
86
86
|
* @param {string} [configuration.overlayCapturePreset="default"] - Named capture plan preset:
|
|
87
87
|
* `default` (progressive low→high captures) or `once` (one immediate high-quality conditional step).
|
|
88
88
|
* @param {Array<{quality: "low"|"mid"|"high", conditional: boolean, delay: number}>} [configuration.overlayCapturePlan]
|
|
@@ -90,6 +90,9 @@ class Overlay extends EventTarget {
|
|
|
90
90
|
* the previous step in the batch. Set `conditional` per step (the SDK does not derive it):
|
|
91
91
|
* when `true`, capture only if overlay content changed since the last required (non-conditional)
|
|
92
92
|
* capture in this batch; when `false`, always capture on that step.
|
|
93
|
+
* @param {number} [configuration.autoHideDurationSec=600] - Client auto-hide duration in seconds.
|
|
94
|
+
* Defaults to `600` (10 minutes) until configured. Omit the key to keep the current value.
|
|
95
|
+
* `0` means never auto-hide. Valid range: `0`–`65535`.
|
|
93
96
|
*/
|
|
94
97
|
configure(configuration) {
|
|
95
98
|
noop(configuration);
|
package/src/interface/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "4.5.
|
|
1
|
+
export const version = "4.5.4";
|