senza-sdk 4.2.49 → 4.2.51-44b32dd.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.
- package/dist/bundle.js +1 -1
- package/package.json +1 -1
- package/src/remotePlayer.js +155 -21
- package/src/senzaShakaPlayer.js +13 -1
package/package.json
CHANGED
package/src/remotePlayer.js
CHANGED
|
@@ -586,7 +586,7 @@ class RemotePlayer extends EventTarget {
|
|
|
586
586
|
currentFramePTS: metadata.mediaTime.toString(),
|
|
587
587
|
ptsSessionId: this._ptsSessionId
|
|
588
588
|
};
|
|
589
|
-
const request = {target: "UI-Streamer", waitForResponse: false, message: JSON.stringify(message)};
|
|
589
|
+
const request = { target: "UI-Streamer", waitForResponse: false, message: JSON.stringify(message) };
|
|
590
590
|
window.cefQuery({
|
|
591
591
|
request: JSON.stringify(request),
|
|
592
592
|
persistent: false
|
|
@@ -1189,7 +1189,6 @@ class RemotePlayer extends EventTarget {
|
|
|
1189
1189
|
const prevSelectedAudioTrack = this._selectedAudioTrack;
|
|
1190
1190
|
for (const track of this.getAudioTracks()) {
|
|
1191
1191
|
if (track.id === audioTrackId) {
|
|
1192
|
-
this._selectedAudioTrack = audioTrackId;
|
|
1193
1192
|
found = true;
|
|
1194
1193
|
break;
|
|
1195
1194
|
}
|
|
@@ -1198,19 +1197,33 @@ class RemotePlayer extends EventTarget {
|
|
|
1198
1197
|
sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
|
|
1199
1198
|
return Promise.resolve();
|
|
1200
1199
|
}
|
|
1201
|
-
if (this.
|
|
1202
|
-
return Promise.resolve(); // Resolve immediately for older versions
|
|
1203
|
-
}
|
|
1204
|
-
if (prevSelectedAudioTrack === this._selectedAudioTrack) {
|
|
1200
|
+
if (this._selectedAudioTrack === audioTrackId) {
|
|
1205
1201
|
return Promise.resolve(); // Audio language already selected
|
|
1206
1202
|
}
|
|
1207
1203
|
|
|
1208
|
-
|
|
1204
|
+
switch (this._remotePlayerApiVersion) {
|
|
1205
|
+
case 0:
|
|
1206
|
+
case 1:
|
|
1207
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1208
|
+
return Promise.resolve(); // Resolve immediately for older versions
|
|
1209
|
+
case 2:
|
|
1210
|
+
return this._selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack);
|
|
1211
|
+
default:
|
|
1212
|
+
return this._selectAudioTrackV3(audioTrackId, prevSelectedAudioTrack);
|
|
1213
|
+
}
|
|
1209
1214
|
}
|
|
1210
1215
|
|
|
1211
|
-
|
|
1216
|
+
/**
|
|
1217
|
+
* Handles the asynchronous selection of an audio track.
|
|
1218
|
+
* If the player is playing, it pauses before changing the track and resumes playback if necessary.
|
|
1219
|
+
*
|
|
1220
|
+
* @param {string} audioTrackId - The ID of the audio track to select.
|
|
1221
|
+
* @param {string} prevSelectedAudioTrack - The previously selected audio track ID.
|
|
1222
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1223
|
+
* */
|
|
1224
|
+
async _selectAudioTrackV2(audioTrackId, prevSelectedAudioTrack) {
|
|
1212
1225
|
const prevIsPlaying = this._isPlaying;
|
|
1213
|
-
sdkLogger.log(`remotePlayer
|
|
1226
|
+
sdkLogger.log(`remotePlayer _selectAudioTrackV2: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
|
|
1214
1227
|
try {
|
|
1215
1228
|
if (this._isPlaying) await this.pause();
|
|
1216
1229
|
let position = this.currentTime;
|
|
@@ -1218,9 +1231,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1218
1231
|
position = this._videoElement.currentTime;
|
|
1219
1232
|
}
|
|
1220
1233
|
await this._load(this._loadedUrl, position, audioTrackId, undefined, false);
|
|
1234
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1221
1235
|
} catch (e) {
|
|
1222
|
-
// Do NOT reject - just log
|
|
1223
|
-
this._selectedAudioTrack = prevSelectedAudioTrack;
|
|
1236
|
+
// Do NOT reject - just log
|
|
1224
1237
|
sdkLogger.warn(`Failed to select audio track ${audioTrackId}: ${e.message}`);
|
|
1225
1238
|
return;
|
|
1226
1239
|
}
|
|
@@ -1237,6 +1250,63 @@ class RemotePlayer extends EventTarget {
|
|
|
1237
1250
|
}
|
|
1238
1251
|
}
|
|
1239
1252
|
|
|
1253
|
+
/**
|
|
1254
|
+
* Handles the asynchronous selection of an audio track.
|
|
1255
|
+
* If the player is playing, it stops subtitle streamType before changing the track and resumes subtitle streamType playback if necessary.
|
|
1256
|
+
* Available only from v3 and on
|
|
1257
|
+
*
|
|
1258
|
+
* @param {string} audioTrackId - The ID of the audio track to select.
|
|
1259
|
+
* @param {string} prevSelectedAudioTrack - The previously selected audio track ID.
|
|
1260
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1261
|
+
* */
|
|
1262
|
+
async _selectAudioTrackV3(audioTrackId, prevSelectedAudioTrack) {
|
|
1263
|
+
sdkLogger.log(`remotePlayer _selectAudioTrackV3: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying}`);
|
|
1264
|
+
if (window.cefQuery) {
|
|
1265
|
+
const FCID = getFCID();
|
|
1266
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1267
|
+
logger.log("remotePlayer _selectAudioTrackV3: sending setAudioLanguage action");
|
|
1268
|
+
const message = {
|
|
1269
|
+
type: "remotePlayer.setAudioLanguage",
|
|
1270
|
+
class: "remotePlayer",
|
|
1271
|
+
action: "setAudioLanguage",
|
|
1272
|
+
fcid: FCID,
|
|
1273
|
+
language: audioTrackId,
|
|
1274
|
+
};
|
|
1275
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1276
|
+
return new Promise((resolve, reject) => {
|
|
1277
|
+
let timerId = 0;
|
|
1278
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1279
|
+
const queryId = window.cefQuery({
|
|
1280
|
+
request: JSON.stringify(request),
|
|
1281
|
+
persistent: false,
|
|
1282
|
+
onSuccess: () => {
|
|
1283
|
+
this._selectedAudioTrack = audioTrackId;
|
|
1284
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1285
|
+
logger.withFields({ duration }).log(`setAudioLanguage completed successfully after ${duration} ms`);
|
|
1286
|
+
timerId = clearTimer(timerId);
|
|
1287
|
+
resolve();
|
|
1288
|
+
},
|
|
1289
|
+
onFailure: (code, msg) => {
|
|
1290
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1291
|
+
logger.withFields({ duration }).log(`setAudioLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1292
|
+
timerId = clearTimer(timerId);
|
|
1293
|
+
reject(new RemotePlayerError(code, msg));
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
logger.log(`window.cefQuery for setAudioLanguage returned query id ${queryId}`);
|
|
1297
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1298
|
+
timerId = setTimeout(() => {
|
|
1299
|
+
logger.log(`setAudioLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1300
|
+
window.cefQueryCancel(queryId);
|
|
1301
|
+
reject(new RemotePlayerError(6000, `setAudioLanguage reached timeout of ${timeout} ms`));
|
|
1302
|
+
}, timeout, queryId);
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
sdkLogger.error("remotePlayer _selectAudioTrackV3: window.cefQuery is undefined");
|
|
1307
|
+
return Promise.resolve(undefined);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1240
1310
|
/** Select a specific text (subtitle) track.
|
|
1241
1311
|
* Track id should come from a call to getTextTracks.
|
|
1242
1312
|
* If no tracks exist - this is a no-op.
|
|
@@ -1256,7 +1326,6 @@ class RemotePlayer extends EventTarget {
|
|
|
1256
1326
|
const prevSelectedTextTrack = this._selectedSubtitlesTrack;
|
|
1257
1327
|
for (const track of this.getTextTracks()) {
|
|
1258
1328
|
if (track.id === textTrackId) {
|
|
1259
|
-
this._selectedSubtitlesTrack = textTrackId;
|
|
1260
1329
|
found = true;
|
|
1261
1330
|
break;
|
|
1262
1331
|
}
|
|
@@ -1265,14 +1334,20 @@ class RemotePlayer extends EventTarget {
|
|
|
1265
1334
|
sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
|
|
1266
1335
|
return Promise.resolve();
|
|
1267
1336
|
}
|
|
1268
|
-
if (this.
|
|
1269
|
-
return Promise.resolve(); // Resolve immediately for older versions
|
|
1270
|
-
}
|
|
1271
|
-
if (prevSelectedTextTrack === this._selectedSubtitlesTrack) {
|
|
1337
|
+
if (this._selectedSubtitlesTrack === textTrackId) {
|
|
1272
1338
|
return Promise.resolve(); // Subtitle language already selected
|
|
1273
1339
|
}
|
|
1274
1340
|
|
|
1275
|
-
|
|
1341
|
+
switch (this._remotePlayerApiVersion) {
|
|
1342
|
+
case 0:
|
|
1343
|
+
case 1:
|
|
1344
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1345
|
+
return Promise.resolve(); // Resolve immediately for older versions
|
|
1346
|
+
case 2:
|
|
1347
|
+
return this._selectTextTrackV2(textTrackId, prevSelectedTextTrack);
|
|
1348
|
+
default:
|
|
1349
|
+
return this._selectTextTrackV3(textTrackId, prevSelectedTextTrack);
|
|
1350
|
+
}
|
|
1276
1351
|
}
|
|
1277
1352
|
|
|
1278
1353
|
/**
|
|
@@ -1283,9 +1358,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1283
1358
|
* @param {string} prevSelectedTextTrack - The previously selected text track ID.
|
|
1284
1359
|
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1285
1360
|
* */
|
|
1286
|
-
async
|
|
1361
|
+
async _selectTextTrackV2(textTrackId, prevSelectedTextTrack) {
|
|
1287
1362
|
const prevIsPlaying = this._isPlaying;
|
|
1288
|
-
sdkLogger.log(`remotePlayer
|
|
1363
|
+
sdkLogger.log(`remotePlayer _selectTextTrackV2: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
|
|
1289
1364
|
try {
|
|
1290
1365
|
if (this._isPlaying) await this.pause();
|
|
1291
1366
|
let position = this.currentTime;
|
|
@@ -1293,9 +1368,9 @@ class RemotePlayer extends EventTarget {
|
|
|
1293
1368
|
position = this._videoElement.currentTime;
|
|
1294
1369
|
}
|
|
1295
1370
|
await this._load(this._loadedUrl, position, undefined, textTrackId, false);
|
|
1371
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1296
1372
|
} catch (e) {
|
|
1297
|
-
// Do NOT reject - just log
|
|
1298
|
-
this._selectedSubtitlesTrack = prevSelectedTextTrack;
|
|
1373
|
+
// Do NOT reject - just log
|
|
1299
1374
|
sdkLogger.warn(`Failed to select text track ${textTrackId}: ${e.message}`);
|
|
1300
1375
|
return;
|
|
1301
1376
|
}
|
|
@@ -1312,6 +1387,65 @@ class RemotePlayer extends EventTarget {
|
|
|
1312
1387
|
}
|
|
1313
1388
|
}
|
|
1314
1389
|
|
|
1390
|
+
/**
|
|
1391
|
+
* Handles the asynchronous selection of a text track.
|
|
1392
|
+
* If the player is playing, it stops subtitle streamType before changing the track and resumes subtitle streamType playback if necessary.
|
|
1393
|
+
* Available only from v3 and on
|
|
1394
|
+
*
|
|
1395
|
+
* @param {string} textTrackId - The ID of the text track to select.
|
|
1396
|
+
* @param {string} prevSelectedTextTrack - The previously selected text track ID.
|
|
1397
|
+
* @returns {Promise<void>} Resolves when the operation is complete.
|
|
1398
|
+
* */
|
|
1399
|
+
async _selectTextTrackV3(textTrackId, prevSelectedTextTrack) {
|
|
1400
|
+
sdkLogger.log(`remotePlayer _selectTextTrackV3: prevAudioTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying}`);
|
|
1401
|
+
if (window.cefQuery) {
|
|
1402
|
+
const FCID = getFCID();
|
|
1403
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1404
|
+
logger.log("remotePlayer _selectTextTrackV3: sending setSubtitleLanguage action");
|
|
1405
|
+
const message = {
|
|
1406
|
+
type: "remotePlayer.setSubtitleLanguage",
|
|
1407
|
+
class: "remotePlayer",
|
|
1408
|
+
action: "setSubtitleLanguage",
|
|
1409
|
+
fcid: FCID,
|
|
1410
|
+
language: textTrackId,
|
|
1411
|
+
};
|
|
1412
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1413
|
+
return new Promise((resolve, reject) => {
|
|
1414
|
+
let timerId = 0;
|
|
1415
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1416
|
+
const queryId = window.cefQuery({
|
|
1417
|
+
request: JSON.stringify(request),
|
|
1418
|
+
persistent: false,
|
|
1419
|
+
onSuccess: () => {
|
|
1420
|
+
this._selectedSubtitlesTrack = textTrackId;
|
|
1421
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1422
|
+
logger.withFields({ duration }).log(`setSubtitleLanguage completed successfully after ${duration} ms`);
|
|
1423
|
+
timerId = clearTimer(timerId);
|
|
1424
|
+
resolve();
|
|
1425
|
+
},
|
|
1426
|
+
onFailure: (code, msg) => {
|
|
1427
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1428
|
+
logger.withFields({ duration }).log(`setSubtitleLanguage failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1429
|
+
timerId = clearTimer(timerId);
|
|
1430
|
+
reject(new RemotePlayerError(code, msg));
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
logger.log(`window.cefQuery for setSubtitleLanguage returned query id ${queryId}`);
|
|
1434
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1435
|
+
timerId = setTimeout(() => {
|
|
1436
|
+
logger.log(`setSubtitleLanguage reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1437
|
+
window.cefQueryCancel(queryId);
|
|
1438
|
+
reject(new RemotePlayerError(6000, `setSubtitleLanguage reached timeout of ${timeout} ms`));
|
|
1439
|
+
}, timeout, queryId);
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
sdkLogger.error("remotePlayer _selectAudioTrackV3: window.cefQuery is undefined");
|
|
1443
|
+
return Promise.resolve(undefined);
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
|
|
1315
1449
|
/**
|
|
1316
1450
|
* Enable or disable the subtitles.
|
|
1317
1451
|
* If the player is in an unloaded state, the request will be applied next time content is played.
|
package/src/senzaShakaPlayer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as shaka from "shaka-player";
|
|
2
|
-
import { remotePlayer, lifecycle } from "./api";
|
|
2
|
+
import { remotePlayer, lifecycle, getPlatformInfo } from "./api";
|
|
3
3
|
import { sdkLogger, iso6393to1 } from "./utils";
|
|
4
4
|
|
|
5
5
|
// Define custom error category
|
|
@@ -386,6 +386,18 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
386
386
|
]);
|
|
387
387
|
};
|
|
388
388
|
|
|
389
|
+
// For live streams, we can set a default offset from the live edge
|
|
390
|
+
// This allows for synchronizing the start position for both local and remote players
|
|
391
|
+
// Note: For VOD content, negative start times are treated as 0
|
|
392
|
+
const { defaultInitialLiveOffset, minInitialLiveOffset } = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"] || {};
|
|
393
|
+
if ((startTime === undefined || startTime === 0) && defaultInitialLiveOffset !== undefined) {
|
|
394
|
+
sdkLogger.debug(`load() was called with startTime=${startTime}, setting startTime to ${-defaultInitialLiveOffset}`);
|
|
395
|
+
startTime = -defaultInitialLiveOffset;
|
|
396
|
+
} else if (startTime <= 0 && minInitialLiveOffset !== undefined && startTime > -minInitialLiveOffset) {
|
|
397
|
+
sdkLogger.debug(`load() was called with startTime=${startTime}, setting startTime to ${-minInitialLiveOffset}`);
|
|
398
|
+
startTime = -minInitialLiveOffset;
|
|
399
|
+
}
|
|
400
|
+
|
|
389
401
|
if (!this.isInRemotePlayback || remotePlayer.getAssetUri() !== url) {
|
|
390
402
|
this._audioTracksMap = {};
|
|
391
403
|
this._videoTracksMap = {};
|