senza-sdk 4.6.0 → 4.6.1

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.6.0",
3
+ "version": "4.6.1",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
@@ -143,7 +143,7 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
143
143
  await Promise.all([
144
144
  lifecycle._init(sessionInfoObj?.settings?.["ui-streamer"], triggerEvent),
145
145
  remotePlayer._init(sessionInfoObj, triggerEvent),
146
- displayManager._init()
146
+ displayManager._init(triggerEvent)
147
147
  ]);
148
148
 
149
149
  alarmManager._init();
@@ -38,13 +38,20 @@ export class DisplayManager extends DisplayManagerInterface {
38
38
  }
39
39
  }
40
40
 
41
- async _init() {
41
+ async _init(triggerEvent) {
42
42
  const enableCec = sessionInfo?.sessionInfoObj?.settings?.client?.hdmi?.cec?.enable_cec;
43
43
  if (enableCec === false) {
44
44
  sdkLogger.log("CEC disabled via config (client.hdmi.cec.enable_cec=false), skipping DisplayManager init");
45
45
  this._setDisplayProperties({ connection: this.DisplayConnectionStatus.UNKNOWN });
46
46
  return;
47
47
  }
48
+ // An endOfSession session runs after the connector is gone and never gets another one, so
49
+ // the display-info request could only be answered by something that is no longer there.
50
+ if (triggerEvent?.type === "endOfSession") {
51
+ sdkLogger.log("Session was triggered by endOfSession, no connector to query, skipping DisplayManager init");
52
+ this._setDisplayProperties({ connection: this.DisplayConnectionStatus.UNKNOWN });
53
+ return;
54
+ }
48
55
  sdkLogger.log("Initializing DisplayManager");
49
56
  await this._updateDisplayInfo();
50
57
  if (!this._isInitialized) {
@@ -1254,6 +1254,67 @@ class RemotePlayer extends RemotePlayerInterface {
1254
1254
  return this._pause();
1255
1255
  }
1256
1256
 
1257
+ /**
1258
+ * Mute or unmute the remote player audio stream.
1259
+ * This API can be called in any player load state (including not loaded).
1260
+ * Muting sets the remote player volume to zero while keeping audio download/sync behavior intact.
1261
+ * The mute state persists across content changes and remains in effect until unmute is called.
1262
+ * @param {boolean} muted true to mute, false to unmute
1263
+ * @returns {Promise}
1264
+ * @throws {RemotePlayerError} error object contains code & msg
1265
+ */
1266
+ mute(muted) {
1267
+ if (!this._isInitialized) {
1268
+ throw new RemotePlayerError(6500, "Cannot call mute() if remote player is not initialized");
1269
+ }
1270
+ if (typeof muted !== "boolean") {
1271
+ throw new RemotePlayerError(6503, `mute() expected boolean muted parameter but got ${typeof muted}`);
1272
+ }
1273
+
1274
+ if (window.cefQuery) {
1275
+ const FCID = getFCID();
1276
+ const logger = sdkLogger.withFields({ FCID });
1277
+ logger.log(`remotePlayer mute: sending mute request muted=${muted}`);
1278
+ const message = {
1279
+ type: "remotePlayer.mute",
1280
+ class: "remotePlayer",
1281
+ action: "mute",
1282
+ fcid: FCID,
1283
+ muted
1284
+ };
1285
+ const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
1286
+ return new Promise((resolve, reject) => {
1287
+ let timerId = 0;
1288
+ const timeBeforeSendingRequest = Date.now();
1289
+ const queryId = window.cefQuery({
1290
+ request: JSON.stringify(request),
1291
+ persistent: false,
1292
+ onSuccess: () => {
1293
+ const duration = Date.now() - timeBeforeSendingRequest;
1294
+ logger.withFields({ duration }).log(`mute completed successfully after ${duration} ms`);
1295
+ timerId = clearTimer(timerId);
1296
+ resolve();
1297
+ },
1298
+ onFailure: (code, msg) => {
1299
+ const duration = Date.now() - timeBeforeSendingRequest;
1300
+ logger.withFields({ duration }).log(`mute failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
1301
+ timerId = clearTimer(timerId);
1302
+ reject(new RemotePlayerError(code, msg));
1303
+ }
1304
+ });
1305
+ logger.log(`window.cefQuery for mute returned query id ${queryId}`);
1306
+ const timeout = this._remotePlayerConfirmationTimeout + 1000;
1307
+ timerId = setTimeout(() => {
1308
+ logger.log(`mute reached timeout of ${timeout} ms, canceling query id ${queryId}`);
1309
+ window.cefQueryCancel(queryId);
1310
+ reject(new RemotePlayerError(6000, `mute reached timeout of ${timeout} ms`));
1311
+ }, timeout, queryId);
1312
+ });
1313
+ }
1314
+ sdkLogger.error("remotePlayer mute: window.cefQuery is undefined");
1315
+ return Promise.resolve(undefined);
1316
+ }
1317
+
1257
1318
  /** Stop playback of all streams (audio, video and subtitles).
1258
1319
  * @returns {Promise}
1259
1320
  * @throws {RemotePlayerError} error object contains code & msg
@@ -577,12 +577,13 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
577
577
  // when timeout reached start the local playback anyway
578
578
  this._handlePlayingEvent();
579
579
  }, this._playingTimeout);
580
- await this.remotePlayer.play(autoTune, switchMode)
581
- .catch(error => {
582
- sdkLogger.error("Failed to play remote player:", error);
583
- this.handleSenzaError(error.code, error.message || "Unknown play error");
584
580
 
585
- });
581
+ try {
582
+ await this.remotePlayer.play(autoTune, switchMode);
583
+ } catch (error) {
584
+ sdkLogger.error("Failed to play remote player:", error);
585
+ this.handleSenzaError(error.code, error.message || "Unknown play error");
586
+ }
586
587
 
587
588
  // Create a new promise that will resolve when the real play succeeds
588
589
  return returnPromise;
@@ -801,8 +802,9 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
801
802
  } catch (stopError) {
802
803
  sdkLogger.error("Error while trying to stop video element playback:", stopError);
803
804
  }
804
- // Handle error while waiting for play event. If the error is not critical or the system is configured not to stop remote player on error,
805
- // the playback timeout will start the local playback
805
+ }
806
+
807
+ if (isCritical) {
806
808
  this._handlePlayPromiseError(error);
807
809
  }
808
810
 
@@ -340,6 +340,18 @@ class RemotePlayer extends EventTarget {
340
340
  return noop("RemotePlayer.pause");
341
341
  }
342
342
 
343
+ /** Mute or unmute the remote player audio stream.
344
+ * This API can be called in any player load state (including not loaded).
345
+ * Muting sets the remote player volume to zero while keeping audio download/sync behavior intact.
346
+ * The mute state persists across content changes and remains in effect until unmute is called.
347
+ * @param {boolean} muted true to mute, false to unmute
348
+ * @returns {Promise}
349
+ * @throws {RemotePlayerError} error object contains code & msg
350
+ */
351
+ async mute(muted) {
352
+ return noop("RemotePlayer.mute", muted);
353
+ }
354
+
343
355
  /** Stop playback of all streams (audio, video and subtitles).
344
356
  * @returns {Promise}
345
357
  * @throws {RemotePlayerError} error object contains code & msg
@@ -1 +1 @@
1
- export const version = "4.6.0";
1
+ export const version = "4.6.1";