senza-sdk 4.2.59 → 4.2.62
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 +3 -2
- package/src/deviceManager.js +3 -3
- package/src/lifecycle.js +7 -3
- package/src/remotePlayer.js +37 -23
- package/src/senzaShakaPlayer.js +218 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "senza-sdk",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.62",
|
|
4
4
|
"main": "./src/api.js",
|
|
5
5
|
"description": "API for Senza application",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"prepublish": "npm run build",
|
|
15
15
|
"eslint": "eslint --max-warnings 0 src test",
|
|
16
16
|
"build": "npx webpack --config webpack.config.js",
|
|
17
|
-
"test": "jest --coverage"
|
|
17
|
+
"test": "jest --coverage --verbose",
|
|
18
|
+
"testall" : "jest --coverage --verbose && npm run eslint --fix"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@babel/cli": "^7.13.16",
|
package/src/deviceManager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getFCID, sdkLogger, getRestResponse } from "./utils";
|
|
2
|
-
import {isRunningE2E} from "./api";
|
|
3
|
-
import {sessionInfo} from "./SessionInfo";
|
|
2
|
+
import { isRunningE2E } from "./api";
|
|
3
|
+
import { sessionInfo } from "./SessionInfo";
|
|
4
4
|
|
|
5
5
|
const wifiInfo = {};
|
|
6
6
|
let wifi_ap_data;
|
|
@@ -290,7 +290,7 @@ class DeviceManager extends EventTarget {
|
|
|
290
290
|
// This api is part of epic HSDEV-4185
|
|
291
291
|
async getWifiInfo() {
|
|
292
292
|
await Promise.all([getWifiApData(), getWifiStatus()]);
|
|
293
|
-
return {...wifi_ap_data, ...wifi_status};
|
|
293
|
+
return { ...wifi_ap_data, ...wifi_status };
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
package/src/lifecycle.js
CHANGED
|
@@ -670,10 +670,10 @@ class Lifecycle extends EventTarget {
|
|
|
670
670
|
return new Promise((resolve, reject) => {
|
|
671
671
|
const FCID = getFCID();
|
|
672
672
|
const logger = sdkLogger.withFields({ FCID });
|
|
673
|
-
logger.log("lifecycle moveToBackground: sending play action");
|
|
674
673
|
const configuration = remotePlayer.getConfiguration();
|
|
675
674
|
const audioLanguage = remotePlayer._selectedAudioTrack || configuration.preferredAudioLanguage || "";
|
|
676
|
-
const subtitlesLanguage = remotePlayer.
|
|
675
|
+
const subtitlesLanguage = remotePlayer._selectedSubtitlesTrack || configuration.preferredSubtitlesLanguage || "";
|
|
676
|
+
|
|
677
677
|
let request;
|
|
678
678
|
const message = {
|
|
679
679
|
action: "play",
|
|
@@ -685,7 +685,7 @@ class Lifecycle extends EventTarget {
|
|
|
685
685
|
message.type = "remotePlayer.play";
|
|
686
686
|
message.class = "remotePlayer";
|
|
687
687
|
message.switchMode = remotePlayer._isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
|
|
688
|
-
message.streamType = StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE;
|
|
688
|
+
message.streamType = remotePlayer.textTrackVisibility ? (StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE) : (StreamType.AUDIO | StreamType.VIDEO);
|
|
689
689
|
request = {
|
|
690
690
|
target: "TC",
|
|
691
691
|
waitForResponse: true,
|
|
@@ -693,8 +693,12 @@ class Lifecycle extends EventTarget {
|
|
|
693
693
|
message: JSON.stringify(message)
|
|
694
694
|
};
|
|
695
695
|
} else {
|
|
696
|
+
if (!remotePlayer.textTrackVisibility) {
|
|
697
|
+
message.subtitlesLanguage = "";
|
|
698
|
+
}
|
|
696
699
|
request = message;
|
|
697
700
|
}
|
|
701
|
+
logger.log(`lifecycle moveToBackground: sending play action audioLanguage=${message.audioLanguage} subtitlesLanguage=${message.subtitlesLanguage} textTrackVisibility=${remotePlayer.textTrackVisibility}`);
|
|
698
702
|
let timerId = 0;
|
|
699
703
|
const timeBeforeSendingRequest = Date.now();
|
|
700
704
|
const queryId = window.cefQuery({
|
package/src/remotePlayer.js
CHANGED
|
@@ -171,6 +171,15 @@ class RemotePlayer extends EventTarget {
|
|
|
171
171
|
* });
|
|
172
172
|
* */
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* @event RemotePlayer#playing
|
|
176
|
+
* @description playing event will be dispatched when the remote player started playback of the first audio frame. This event can be used to synchronize the local player with the remote player.
|
|
177
|
+
* @example
|
|
178
|
+
* remotePlayer.addEventListener("playing", () => {
|
|
179
|
+
* console.info("remotePlayer playing");
|
|
180
|
+
* });
|
|
181
|
+
* */
|
|
182
|
+
|
|
174
183
|
/**
|
|
175
184
|
* @event RemotePlayer#loadedmetadata
|
|
176
185
|
* @description loadedmetadata event will be dispatched when the remote player metadata is loaded
|
|
@@ -495,13 +504,7 @@ class RemotePlayer extends EventTarget {
|
|
|
495
504
|
}
|
|
496
505
|
if (this._availableTextTracks) {
|
|
497
506
|
const selectedTrack = this._availableTextTracks.find((track) => track.selected === true);
|
|
498
|
-
|
|
499
|
-
this._selectedSubtitlesTrack = selectedTrack.id;
|
|
500
|
-
this._textTrackVisibility = true;
|
|
501
|
-
} else {
|
|
502
|
-
this._selectedSubtitlesTrack = "";
|
|
503
|
-
this._textTrackVisibility = false;
|
|
504
|
-
}
|
|
507
|
+
this._selectedSubtitlesTrack = selectedTrack?.id || "";
|
|
505
508
|
}
|
|
506
509
|
}
|
|
507
510
|
|
|
@@ -698,12 +701,14 @@ class RemotePlayer extends EventTarget {
|
|
|
698
701
|
if (window.cefQuery) {
|
|
699
702
|
const FCID = getFCID();
|
|
700
703
|
const logger = sdkLogger.withFields({ FCID });
|
|
701
|
-
logger.log("remotePlayer play: sending play action");
|
|
702
704
|
const audioLanguage = this._selectedAudioTrack || this._config.preferredAudioLanguage || "";
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
705
|
+
const subtitlesLanguage = this._selectedSubtitlesTrack || this._config.preferredSubtitlesLanguage || "";
|
|
706
|
+
|
|
707
|
+
if (this._remotePlayerApiVersion >= 2 && !this._textTrackVisibility && streamType === StreamType.SUBTITLE) {
|
|
708
|
+
logger.log("remotePlayer play: text track visibility is disabled and streamType is only SUBTITLE. returning early with no action.");
|
|
709
|
+
return Promise.resolve(undefined); // nothing to do
|
|
706
710
|
}
|
|
711
|
+
|
|
707
712
|
const message = {
|
|
708
713
|
type: "remotePlayer.play",
|
|
709
714
|
class: "remotePlayer",
|
|
@@ -718,7 +723,16 @@ class RemotePlayer extends EventTarget {
|
|
|
718
723
|
message.switchMode = this._isAudioSyncEnabled() ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
|
|
719
724
|
message.streamType = streamType;
|
|
720
725
|
waitForResponse = true;
|
|
726
|
+
|
|
727
|
+
if (!this._textTrackVisibility && (message.streamType & StreamType.SUBTITLE) !== 0) {
|
|
728
|
+
// remove SUBTITLE
|
|
729
|
+
message.streamType = message.streamType & ~StreamType.SUBTITLE;
|
|
730
|
+
logger.log("remotePlayer play: text track visibility is disabled. Removed SUBTITLE from streamType.");
|
|
731
|
+
}
|
|
732
|
+
} else if (!this.textTrackVisibility) {
|
|
733
|
+
message.subtitlesLanguage = "";
|
|
721
734
|
}
|
|
735
|
+
logger.log(`remotePlayer play: sending play action remotePlayer._isPlaying: ${this._isPlaying} audioLanguage=${message.audioLanguage} subtitlesLanguage=${message.subtitlesLanguage} textTrackVisibility=${this.textTrackVisibility}`);
|
|
722
736
|
const request = { target: "TC", waitForResponse: waitForResponse, message: JSON.stringify(message) };
|
|
723
737
|
return new Promise((resolve, reject) => {
|
|
724
738
|
let timerId = 0;
|
|
@@ -947,10 +961,7 @@ class RemotePlayer extends EventTarget {
|
|
|
947
961
|
const playbackPosition = position ?? 0;
|
|
948
962
|
const logger = sdkLogger.withFields({ FCID, loadUrl: url, playbackPosition });
|
|
949
963
|
const audioLanguage = audioTrackId || this._selectedAudioTrack || this._config.preferredAudioLanguage || "";
|
|
950
|
-
|
|
951
|
-
if (this._textTrackVisibility) {
|
|
952
|
-
subtitlesLanguage = textTrackId || this._selectedSubtitlesTrack || this._config.preferredSubtitlesLanguage || "";
|
|
953
|
-
}
|
|
964
|
+
const subtitlesLanguage = textTrackId || this._selectedSubtitlesTrack || this._config.preferredSubtitlesLanguage || "";
|
|
954
965
|
|
|
955
966
|
const message = {
|
|
956
967
|
url,
|
|
@@ -968,7 +979,7 @@ class RemotePlayer extends EventTarget {
|
|
|
968
979
|
} else {
|
|
969
980
|
message.type = "setPlayableUri";
|
|
970
981
|
}
|
|
971
|
-
logger.log(`remotePlayer load: sending ${message.type} request. remotePlayer._isPlaying: ${this._isPlaying}`);
|
|
982
|
+
logger.log(`remotePlayer load: sending ${message.type} request. remotePlayer._isPlaying: ${this._isPlaying} audioLanguage=${audioLanguage} subtitlesLanguage=${subtitlesLanguage}`);
|
|
972
983
|
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
973
984
|
let timerId = 0;
|
|
974
985
|
const timeBeforeSendingRequest = Date.now();
|
|
@@ -1519,20 +1530,23 @@ class RemotePlayer extends EventTarget {
|
|
|
1519
1530
|
/**
|
|
1520
1531
|
* Enable or disable the subtitles.
|
|
1521
1532
|
* If the player is in an unloaded state, the request will be applied next time content is played.
|
|
1522
|
-
* @param {boolean} visible whether the subtitles are visible or not
|
|
1523
|
-
* @throws {TypeError} if visible is not a boolean variable
|
|
1533
|
+
* @param {boolean|0|1} visible whether the subtitles are visible or not
|
|
1534
|
+
* @throws {TypeError} if visible is not a boolean variable or 0/1
|
|
1524
1535
|
*/
|
|
1525
1536
|
setTextTrackVisibility(visible) {
|
|
1526
1537
|
const oldVisibility = this._textTrackVisibility;
|
|
1527
|
-
if (typeof visible !== "boolean") {
|
|
1528
|
-
throw new TypeError("visible parameter must be a boolean");
|
|
1538
|
+
if (typeof visible !== "boolean" && !(visible === 0 || visible === 1)) {
|
|
1539
|
+
throw new TypeError("visible parameter must be a boolean or 0/1");
|
|
1529
1540
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1541
|
+
// Convert to boolean in case apps pass 0/1 instead false/true.
|
|
1542
|
+
const newVisibility = !!visible;
|
|
1543
|
+
|
|
1544
|
+
if (oldVisibility === newVisibility) {
|
|
1532
1545
|
return;
|
|
1533
1546
|
}
|
|
1547
|
+
|
|
1534
1548
|
this._textTrackVisibility = newVisibility;
|
|
1535
|
-
if (!
|
|
1549
|
+
if (!this._textTrackVisibility) {
|
|
1536
1550
|
// Setting the visibility to false clears any previous selections user has done
|
|
1537
1551
|
this._selectedSubtitlesTrack = "";
|
|
1538
1552
|
}
|
package/src/senzaShakaPlayer.js
CHANGED
|
@@ -55,35 +55,92 @@ const senzaShaka = { ...shaka };
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
export class SenzaShakaPlayer extends shaka.Player {
|
|
58
|
+
/** @private {SenzaShakaPlayer|null} Previous instance of the player */
|
|
58
59
|
static _prevInstance = null;
|
|
60
|
+
|
|
59
61
|
/**
|
|
60
62
|
* @private
|
|
61
63
|
* @type {Object.<string, string>}
|
|
62
|
-
* @description Map
|
|
64
|
+
* @description Map of audio track languages to their IDs
|
|
63
65
|
*/
|
|
64
66
|
_audioTracksMap = {};
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
69
|
* @private
|
|
68
70
|
* @type {Object.<string, string>}
|
|
69
|
-
* @description Map
|
|
71
|
+
* @description Map of text track languages to their IDs
|
|
70
72
|
*/
|
|
71
73
|
_textTracksMap = {};
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @private
|
|
77
|
+
* @type {number}
|
|
78
|
+
* @description Timeout in milliseconds to wait for playing event
|
|
79
|
+
* @default 3000
|
|
80
|
+
*/
|
|
81
|
+
_playingTimeout = 3000;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @private
|
|
85
|
+
* @type {number|null}
|
|
86
|
+
* @description Timestamp when play was called
|
|
87
|
+
*/
|
|
88
|
+
_playStartTime = null;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @private
|
|
92
|
+
* @type {boolean}
|
|
93
|
+
* @description Whether to stop remote player on error
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
_shouldStopRemotePlayerOnError = false;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @private
|
|
100
|
+
* @type {Function|null}
|
|
101
|
+
* @description Original play function from video element
|
|
102
|
+
*/
|
|
103
|
+
_originalPlay = null;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @private
|
|
107
|
+
* @type {Function|null}
|
|
108
|
+
* @description Resolve function for play promise
|
|
109
|
+
*/
|
|
110
|
+
_playPromiseResolve = null;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @private
|
|
114
|
+
* @type {Function|null}
|
|
115
|
+
* @description Reject function for play promise
|
|
116
|
+
*/
|
|
117
|
+
_playPromiseReject = null;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @private
|
|
121
|
+
* @type {number|null}
|
|
122
|
+
* @description Timer ID for play timeout
|
|
123
|
+
*/
|
|
124
|
+
_playTimeoutId = null;
|
|
125
|
+
|
|
73
126
|
/**
|
|
74
127
|
* @private
|
|
75
128
|
* @type {Object.<string, Function>}
|
|
76
|
-
* @description
|
|
129
|
+
* @description Event listeners for video element
|
|
77
130
|
*/
|
|
78
131
|
_videoEventListeners = {
|
|
79
132
|
"play": () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
133
|
+
// keep old play behavior, in case playing timeout is defined as 0
|
|
134
|
+
if (this._playingTimeout === 0) {
|
|
135
|
+
this.remotePlayer.play()
|
|
136
|
+
.catch(error => {
|
|
137
|
+
sdkLogger.error("Failed to play remote player:", error);
|
|
138
|
+
this.handleSenzaError(error.code, error.message || "Unknown play error");
|
|
139
|
+
});
|
|
140
|
+
}
|
|
85
141
|
},
|
|
86
|
-
"pause": () => {
|
|
142
|
+
"pause" : () => {
|
|
143
|
+
this._resetPlayPromise();
|
|
87
144
|
this.remotePlayer.pause()
|
|
88
145
|
.catch(error => {
|
|
89
146
|
sdkLogger.error("Failed to pause remote player:", error);
|
|
@@ -97,7 +154,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
97
154
|
/**
|
|
98
155
|
* @private
|
|
99
156
|
* @type {Object.<string, Function>}
|
|
100
|
-
* @description
|
|
157
|
+
* @description Event listeners for remote player
|
|
101
158
|
*/
|
|
102
159
|
_remotePlayerEventListeners = {
|
|
103
160
|
"ended": () => {
|
|
@@ -109,7 +166,6 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
109
166
|
},
|
|
110
167
|
"error": (event) => {
|
|
111
168
|
sdkLogger.log("remotePlayer error:", event.detail.errorCode, event.detail.message);
|
|
112
|
-
// we need to move to foreground here for the auto background mode to work
|
|
113
169
|
lifecycle.moveToForeground();
|
|
114
170
|
this.handleSenzaError(event.detail.errorCode, event.detail.message);
|
|
115
171
|
},
|
|
@@ -166,17 +222,85 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
166
222
|
this._textTracksMap[lang] = track.id;
|
|
167
223
|
}
|
|
168
224
|
}
|
|
225
|
+
},
|
|
226
|
+
"playing": async () => {
|
|
227
|
+
sdkLogger.info("remotePlayer playing event received");
|
|
228
|
+
// If playing timeout was not expored, and the feature is set, handle the playing event
|
|
229
|
+
if (this._playingTimeout > 0 && this._playTimeoutId !== null) {
|
|
230
|
+
this._handlePlayingEvent();
|
|
231
|
+
}
|
|
169
232
|
}
|
|
170
233
|
};
|
|
171
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Clears the play timeout and resets the timer ID
|
|
237
|
+
* @private
|
|
238
|
+
* @description Cancels any pending play timeout and nullifies the timeout ID
|
|
239
|
+
*/
|
|
240
|
+
_clearPlayTimeout = () => {
|
|
241
|
+
if (this._playTimeoutId) {
|
|
242
|
+
sdkLogger.info("Clearing play timeout");
|
|
243
|
+
clearTimeout(this._playTimeoutId);
|
|
244
|
+
this._playTimeoutId = null;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* @private
|
|
249
|
+
* @description Handles the playing event from the remote player.
|
|
250
|
+
*/
|
|
251
|
+
_handlePlayingEvent = async () => {
|
|
172
252
|
|
|
253
|
+
this._clearPlayTimeout();
|
|
254
|
+
|
|
255
|
+
if (this.videoElement && this._originalPlay && this._playPromiseResolve) {
|
|
256
|
+
try {
|
|
257
|
+
const elapsedTime = Date.now() - this._playStartTime;
|
|
258
|
+
sdkLogger.info(`Time for playback start ${elapsedTime}ms`);
|
|
259
|
+
await this._originalPlay.call(this.videoElement);
|
|
260
|
+
sdkLogger.info("Video element play resolved successfully");
|
|
261
|
+
this._playPromiseResolve();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
this._handlePlayPromiseError(error);
|
|
264
|
+
return;
|
|
265
|
+
} finally {
|
|
266
|
+
this._playPromiseResolve = null;
|
|
267
|
+
this._playPromiseReject = null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
173
271
|
/**
|
|
174
|
-
* Flag indicating whether the remote player should be stopped when an error occurs
|
|
175
272
|
* @private
|
|
176
|
-
* @
|
|
177
|
-
* @default false
|
|
273
|
+
* @description Resets the play promise state, clearing any existing resolve/reject functions and timeouts.
|
|
178
274
|
*/
|
|
179
|
-
|
|
275
|
+
_resetPlayPromise = () => {
|
|
276
|
+
if (this._playPromiseResolve) {
|
|
277
|
+
sdkLogger.info("Resolving play promise");
|
|
278
|
+
this._playPromiseResolve();
|
|
279
|
+
}
|
|
280
|
+
this._playPromiseResolve = null;
|
|
281
|
+
this._playPromiseReject = null;
|
|
282
|
+
this._clearPlayTimeout();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Handles errors for play promises and remote player events.
|
|
288
|
+
* @private
|
|
289
|
+
* @param {Error} error - The error object.
|
|
290
|
+
*/
|
|
291
|
+
_handlePlayPromiseError(error) {
|
|
292
|
+
|
|
293
|
+
sdkLogger.error("Error while waiting for playing event:", error);
|
|
294
|
+
if (this._playPromiseReject) {
|
|
295
|
+
this._playPromiseReject(error);
|
|
296
|
+
this._playPromiseResolve = null;
|
|
297
|
+
this._playPromiseReject = null;
|
|
298
|
+
}
|
|
299
|
+
if (this._playTimeoutId) {
|
|
300
|
+
clearTimeout(this._playTimeoutId);
|
|
301
|
+
this._playTimeoutId = null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
180
304
|
|
|
181
305
|
/**
|
|
182
306
|
* Creates an instance of SenzaShakaPlayer, which is a subclass of shaka.Player.
|
|
@@ -201,15 +325,62 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
201
325
|
|
|
202
326
|
this.remotePlayer = remotePlayer;
|
|
203
327
|
this._addRemotePlayerEventListeners();
|
|
328
|
+
SenzaShakaPlayer._prevInstance = this;
|
|
329
|
+
const playTimeout = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"]?.playingEventTimeout;
|
|
330
|
+
this._playingTimeout = (playTimeout >= 0) ? playTimeout*1000 : this._playingTimeout;
|
|
331
|
+
|
|
204
332
|
// if video element is provided, add the listeres here. In this case ,there is no need to call attach.
|
|
205
333
|
if (videoElement) {
|
|
206
|
-
this.videoElement
|
|
207
|
-
this._attachVideoElementToRemotePlayer();
|
|
334
|
+
this._attach(videoElement);
|
|
208
335
|
sdkLogger.warn("SenzaShakaPlayer constructor Adding videoElement in the constructor is going to be deprecated in the future. Please use attach method instead.");
|
|
209
336
|
}
|
|
210
|
-
SenzaShakaPlayer._prevInstance = this;
|
|
211
337
|
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
_attach(videoElement) {
|
|
341
|
+
this.videoElement = videoElement;
|
|
342
|
+
|
|
343
|
+
// Store original play and replace with our implementation.
|
|
344
|
+
if (this._playingTimeout > 0) {
|
|
345
|
+
this._originalPlay = this.videoElement.play;
|
|
346
|
+
this.videoElement.play = async () => {
|
|
347
|
+
if (this._playPromiseResolve || this._playPromiseReject) {
|
|
348
|
+
sdkLogger.warn("play() was called while a play promise is already pending. Ignoring play call.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Clear any existing timeout
|
|
353
|
+
if (this._playTimeoutId) {
|
|
354
|
+
clearTimeout(this._playTimeoutId);
|
|
355
|
+
}
|
|
356
|
+
// Store the timestamp of the play call
|
|
357
|
+
this._playStartTime = Date.now();
|
|
358
|
+
|
|
359
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
360
|
+
this._playPromiseResolve = resolve;
|
|
361
|
+
this._playPromiseReject = reject;
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
});
|
|
365
|
+
// Set timeout to reject if playing event doesn't arrive
|
|
366
|
+
this._playTimeoutId = setTimeout(() => {
|
|
367
|
+
sdkLogger.error("Playing event timeout reached");
|
|
368
|
+
// when timeout reached start the local playback anyway
|
|
369
|
+
this._handlePlayingEvent();
|
|
370
|
+
}, this._playingTimeout);
|
|
371
|
+
await this.remotePlayer.play()
|
|
372
|
+
.catch(error => {
|
|
373
|
+
sdkLogger.error("Failed to play remote player:", error);
|
|
374
|
+
this.handleSenzaError(error.code, error.message || "Unknown play error");
|
|
375
|
+
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Create a new promise that will resolve when the real play succeeds
|
|
379
|
+
return returnPromise;
|
|
380
|
+
};
|
|
381
|
+
};
|
|
212
382
|
|
|
383
|
+
this._attachVideoElementToRemotePlayer();
|
|
213
384
|
}
|
|
214
385
|
|
|
215
386
|
/**
|
|
@@ -220,8 +391,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
220
391
|
*/
|
|
221
392
|
async attach(videoElement, initializeMediaSource = true) {
|
|
222
393
|
await super.attach(videoElement, initializeMediaSource);
|
|
223
|
-
this.videoElement
|
|
224
|
-
this._attachVideoElementToRemotePlayer();
|
|
394
|
+
this._attach(videoElement);
|
|
225
395
|
}
|
|
226
396
|
|
|
227
397
|
/**
|
|
@@ -235,6 +405,17 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
235
405
|
* @export
|
|
236
406
|
*/
|
|
237
407
|
async detach(keepAdManager = false) {
|
|
408
|
+
// Clear any pending timeout
|
|
409
|
+
this._resetPlayPromise();
|
|
410
|
+
|
|
411
|
+
if (this.videoElement && this._originalPlay) {
|
|
412
|
+
// Clear any pending play promise
|
|
413
|
+
this._playPromiseResolve = null;
|
|
414
|
+
this._playPromiseReject = null;
|
|
415
|
+
// Restore original play function before detaching
|
|
416
|
+
this.videoElement.play = this._originalPlay;
|
|
417
|
+
this._originalPlay = null;
|
|
418
|
+
}
|
|
238
419
|
|
|
239
420
|
await super.detach(keepAdManager);
|
|
240
421
|
this._audioTracksMap = {};
|
|
@@ -265,6 +446,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
265
446
|
// Call the remote player's unload method
|
|
266
447
|
try {
|
|
267
448
|
await lifecycle.moveToForeground();
|
|
449
|
+
this._clearPlayTimeout();
|
|
268
450
|
await remotePlayer.unload();
|
|
269
451
|
} catch (error) {
|
|
270
452
|
sdkLogger.error("Failed to unload remote player:", error);
|
|
@@ -391,7 +573,8 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
391
573
|
sdkLogger.error("Error while trying to stop video element playback:", stopError);
|
|
392
574
|
}
|
|
393
575
|
}
|
|
394
|
-
|
|
576
|
+
// Handle error while waiting for play event
|
|
577
|
+
this._handlePlayPromiseError(error);
|
|
395
578
|
this.dispatchEvent(new shaka.util.FakeEvent("error", errorMap));
|
|
396
579
|
}
|
|
397
580
|
|
|
@@ -487,6 +670,17 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
487
670
|
return super.destroy();
|
|
488
671
|
}
|
|
489
672
|
|
|
673
|
+
/**
|
|
674
|
+
* A temporary override for older versions of Shaka.
|
|
675
|
+
* Senza doesn't support out-of-band subtitles
|
|
676
|
+
*/
|
|
677
|
+
addTextTrack(uri, language, kind, mimeType, codec, label, forced = false) {
|
|
678
|
+
sdkLogger.warn("addTextTrack is deprecated, please use addTextTrackAsync");
|
|
679
|
+
super.addTextTrackAsync(uri, language, kind, mimeType, codec, label, forced).then(subs => {
|
|
680
|
+
sdkLogger.warn("addTextTrackAsync Done" + subs);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
490
684
|
/**
|
|
491
685
|
* Override the configure method to add custom configuration handling
|
|
492
686
|
* Supports the following additional configuration options:
|
|
@@ -502,6 +696,8 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
502
696
|
* });
|
|
503
697
|
*/
|
|
504
698
|
configure(config) {
|
|
699
|
+
sdkLogger.log("configure player with: ", JSON.stringify(config));
|
|
700
|
+
|
|
505
701
|
// Handle custom configuration
|
|
506
702
|
if (config.shouldStopRemotePlayerOnError !== undefined) {
|
|
507
703
|
this._shouldStopRemotePlayerOnError = !!config.shouldStopRemotePlayerOnError;
|
|
@@ -522,6 +718,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
522
718
|
remoteConfiguration["preferredSubtitlesLanguage"] = config["preferredTextLanguage"];
|
|
523
719
|
}
|
|
524
720
|
|
|
721
|
+
sdkLogger.log("configure remote player with: ", JSON.stringify(remoteConfiguration));
|
|
525
722
|
remotePlayer.configure(remoteConfiguration);
|
|
526
723
|
}
|
|
527
724
|
|