senza-sdk 4.2.65-90c49ac.0 → 4.3.1-4a01fcf.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.
Files changed (26) hide show
  1. package/dist/bundle.js +1 -1
  2. package/package.json +17 -8
  3. package/src/api.js +248 -329
  4. package/src/{alarmManager.js → implementation/alarmManager.js} +15 -52
  5. package/src/implementation/api.js +367 -0
  6. package/src/{deviceManager.js → implementation/deviceManager.js} +6 -78
  7. package/src/{lifecycle.js → implementation/lifecycle.js} +28 -215
  8. package/src/{messageManager.js → implementation/messageManager.js} +6 -6
  9. package/src/{platformManager.js → implementation/platformManager.js} +5 -4
  10. package/src/{remotePlayer.js → implementation/remotePlayer.js} +33 -27
  11. package/src/{senzaShakaPlayer.js → implementation/senzaShakaPlayer.js} +91 -16
  12. package/src/{utils.js → implementation/utils.js} +15 -6
  13. package/src/interface/alarmManager.js +69 -0
  14. package/src/interface/api.js +8 -0
  15. package/src/{devSequence.js → interface/devSequence.js} +35 -0
  16. package/src/interface/deviceManager.js +133 -0
  17. package/src/interface/lifecycle.js +278 -0
  18. package/src/interface/messageManager.js +46 -0
  19. package/src/interface/platformManager.js +35 -0
  20. package/src/interface/remotePlayer.js +441 -0
  21. package/src/interface/senzaShakaPlayer.js +171 -0
  22. package/src/interface/utils.js +45 -0
  23. /package/src/{SessionInfo.js → implementation/SessionInfo.js} +0 -0
  24. /package/src/{devHelper.js → implementation/devHelper.js} +0 -0
  25. /package/src/{eventListenersManager.js → implementation/eventListenersManager.js} +0 -0
  26. /package/src/{subtitlesUtils.js → implementation/subtitlesUtils.js} +0 -0
@@ -1,3 +1,4 @@
1
+ import { RemotePlayer as RemotePlayerInterface } from "../interface/remotePlayer";
1
2
  import {
2
3
  getFCID,
3
4
  isAudioSyncConfigured,
@@ -68,6 +69,7 @@ function setPlaybackInfo(playbackInfo) {
68
69
  * @typedef {Object} Config
69
70
  * @property {string} preferredAudioLanguage
70
71
  * @property {string} preferredSubtitlesLanguage
72
+ * @property {number} minSuggestedPresentationDelay - minimal delay allowed for live playback in seconds
71
73
  * @property {boolean} autoPlay - (Not implemented yet) upon loading start playing automatically
72
74
  */
73
75
 
@@ -78,11 +80,11 @@ function setPlaybackInfo(playbackInfo) {
78
80
  * @fires ended
79
81
  * @fires error
80
82
  * @fires onloadmodechange
83
+ * @fires playing
81
84
  * @fires seeking (Not implemented yet)
82
85
  * @fires seeked (Not implemented yet)
83
- * @fires loadedmetadata (Not implemented yet)
84
86
  */
85
- class RemotePlayer extends EventTarget {
87
+ class RemotePlayer extends RemotePlayerInterface {
86
88
  constructor() {
87
89
  super();
88
90
  /**
@@ -91,7 +93,8 @@ class RemotePlayer extends EventTarget {
91
93
  */
92
94
  this._config = {
93
95
  preferredAudioLanguage: "",
94
- preferredSubtitlesLanguage: ""
96
+ preferredSubtitlesLanguage: "",
97
+ minSuggestedPresentationDelay: 0
95
98
  };
96
99
  /**
97
100
  * @type {string}
@@ -524,10 +527,10 @@ class RemotePlayer extends EventTarget {
524
527
 
525
528
  /** setting values for properties in the player configuration using an object.
526
529
  * If the config does not support a property this is a no-op.
527
- * @param {Object} props the object with all the different properties to change
530
+ * @param {Config} props the object with all the different properties to change.
528
531
  * @example
529
532
  * remotePlayer.configure({ preferredAudioLanguage: 'en-US' })
530
- *
533
+ * remotePlayer.configure({ minSuggestedPresentationDelay: 6 })
531
534
  * */
532
535
  configure(props) {
533
536
  Object.entries(props).forEach(([key, value]) => {
@@ -896,6 +899,9 @@ class RemotePlayer extends EventTarget {
896
899
  this._updateSeekListeners(video);
897
900
  }
898
901
  this._videoElement = video;
902
+
903
+ // Emit a custom event to notify about the attachment of the video element
904
+ this.dispatchEvent(new Event("videoelementattached"));
899
905
  }
900
906
  }
901
907
 
@@ -911,7 +917,7 @@ class RemotePlayer extends EventTarget {
911
917
  /** Tell the remote player to load the given URL.
912
918
  * @param {string} url url to load
913
919
  * @param {number} [position] start position in seconds (if not provided, start from beginning (VOD) or current time (LTV))
914
- * @returns {Promise}
920
+ * @returns {Promise}
915
921
  * @throws {RemotePlayerError} error object contains code & msg
916
922
  *
917
923
  * */
@@ -976,6 +982,11 @@ class RemotePlayer extends EventTarget {
976
982
  message.action = "load";
977
983
  message.audioLanguage = audioLanguage;
978
984
  message.subtitlesLanguage = subtitlesLanguage;
985
+ if (this.getConfiguration().minSuggestedPresentationDelay > 0) {
986
+ message.cloudPlayerParams = {
987
+ "mspd": this.getConfiguration().minSuggestedPresentationDelay
988
+ };
989
+ }
979
990
  } else {
980
991
  message.type = "setPlayableUri";
981
992
  }
@@ -1133,13 +1144,8 @@ class RemotePlayer extends EventTarget {
1133
1144
 
1134
1145
  // If seeking in progress, wait for seek to complete before playing
1135
1146
  if (this._isSeekingByApplication) {
1136
- if (lifecycle.state === lifecycle.UiState.FOREGROUND) {
1137
- sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_UI");
1138
- this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
1139
- } else {
1140
- sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_ABR");
1141
- this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
1142
- }
1147
+ sdkLogger.info("application requesting play during seek");
1148
+ this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
1143
1149
  return Promise.resolve(true);
1144
1150
  }
1145
1151
  /*
@@ -1661,7 +1667,9 @@ class RemotePlayer extends EventTarget {
1661
1667
  }
1662
1668
  }
1663
1669
 
1664
- if (this._remotePlayerApiVersion >= 2 && !this._isSeekingByPlatform && !this._isSeekingByApplication) {
1670
+ // Only allow seeking in foreground. Still ignore the initialized local player seeking event above
1671
+ if (this._remotePlayerApiVersion >= 2 && !this._isSeekingByPlatform && !this._isSeekingByApplication &&
1672
+ (lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND)) {
1665
1673
  this._atomicSeek();
1666
1674
  } else {
1667
1675
  sdkLogger.info(`Seeking: skipping seeking event to currentTime: ${playbackPosition}, internalSeek: ${this._isSeekingByPlatform}, localPlayerSeek: ${this._isSeekingByApplication}, state: ${lifecycle.state}`);
@@ -1680,16 +1688,16 @@ class RemotePlayer extends EventTarget {
1680
1688
  * */
1681
1689
  async _atomicSeek() {
1682
1690
  sdkLogger.info("Seeking: local video element seeking start while isPlaying: ", this._isPlaying);
1683
- if (this._isPlaying) {
1684
- if (!lifecycle._inTransitionToForeground && (lifecycle._inTransitionToBackground || lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
1685
- sdkLogger.info("seek in background", this._isPlaying);
1686
- this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
1687
- } else {
1688
- this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
1689
- }
1690
- } else {
1691
- this._targetSeekPlayingState = TargetPlayingState.PAUSED;
1692
- }
1691
+
1692
+ // Initialize the target playing state unless changed during the seek process
1693
+ // In the future, we should allow for seeking in background. Currently, there's no
1694
+ // way to know when the web application will call moveToForeground (i.e Before/After seek)
1695
+ // Therefore, for now, we will assume the target is either paused or playing in ui unless
1696
+ // specifically receiving a moveToBackground during the process.
1697
+ // if (this._isPlaying && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
1698
+ // this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
1699
+ // }
1700
+ this._targetSeekPlayingState = this._isPlaying ? TargetPlayingState.PLAYING_UI : TargetPlayingState.PAUSED;
1693
1701
 
1694
1702
  // The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
1695
1703
  if (this._videoElement) {
@@ -1756,7 +1764,7 @@ class RemotePlayer extends EventTarget {
1756
1764
 
1757
1765
  // If in TargetPlayingState.PAUSE, no need to resume.
1758
1766
  // Resume without awaiting to avoid blocking the seek process anymore
1759
- // In case where we aborted (new load or unload called), we don't want to resume playback.
1767
+ // In case where we aborted, we don't want to resume playback.
1760
1768
  if (!this._abortSeeking) {
1761
1769
  if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_UI) {
1762
1770
  if (!this._isAudioSyncEnabled()) {
@@ -1765,8 +1773,6 @@ class RemotePlayer extends EventTarget {
1765
1773
  // resume audio play only if _isAudioSyncEnabled
1766
1774
  this._play(StreamType.AUDIO);
1767
1775
  } else if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_ABR) {
1768
- // When moving back to background, we need to put the remote player back into play mode
1769
- this._changePlayMode(true);
1770
1776
  lifecycle._moveToBackground();
1771
1777
  }
1772
1778
  }
@@ -1,6 +1,9 @@
1
- import * as shaka from "shaka-player";
1
+ import { shaka } from "../interface/senzaShakaPlayer";
2
+
2
3
  import { remotePlayer, lifecycle, getPlatformInfo } from "./api";
3
4
  import { sdkLogger, iso6393to1 } from "./utils";
5
+ import moment from "moment";
6
+
4
7
 
5
8
  // Define custom error category
6
9
  shaka.util.Error.Category.SENZA_PLAYER_ERROR = 50;
@@ -30,11 +33,6 @@ class SenzaError extends shaka.util.Error {
30
33
  }
31
34
 
32
35
 
33
- // Copy the shaka module and replace the Player class with SenzaShakaPlayer
34
- // if we don't Copy the shaka module, the Player class will be replaced for all the other modules that import shaka
35
- const senzaShaka = { ...shaka };
36
-
37
-
38
36
  /**
39
37
  * SenzaShakaPlayer subclass of Shaka that handles both local and remote playback.
40
38
  *
@@ -76,7 +74,7 @@ export class SenzaShakaPlayer extends shaka.Player {
76
74
  * @private
77
75
  * @type {number}
78
76
  * @description Timeout in milliseconds to wait for playing event
79
- * @default 3000
77
+ * @default 4000
80
78
  */
81
79
  _playingTimeout = 4000;
82
80
 
@@ -185,7 +183,7 @@ export class SenzaShakaPlayer extends shaka.Player {
185
183
  originatesFromRemotePlayer: true
186
184
  };
187
185
 
188
- const response = await this.getNetworkingEngine().request(senzaShaka.net.NetworkingEngine.RequestType.LICENSE, request).promise;
186
+ const response = await this.getNetworkingEngine().request(shaka.net.NetworkingEngine.RequestType.LICENSE, request).promise;
189
187
 
190
188
  let responseBody = response.data;
191
189
  if (response.status < 200 || response.status >= 300) {
@@ -305,6 +303,45 @@ export class SenzaShakaPlayer extends shaka.Player {
305
303
 
306
304
  }
307
305
 
306
+ /**
307
+ * @private
308
+ * @type {number}
309
+ * @description Minimum suggested presentation delay in seconds
310
+ * @default 15
311
+ */
312
+ _minSuggestedPresentationDelay = 15;
313
+
314
+ /**
315
+ * Modifies the suggestedPresentationDelay in the manifest text
316
+ * @private
317
+ * @param {string} manifestText - The MPD manifest text
318
+ * @returns {string} - Modified manifest text , or undefined if no modification was done
319
+ */
320
+ _updateManifestDelayIfBelowMinimum(manifestText) {
321
+ // Look for suggestedPresentationDelay attribute
322
+ const match = manifestText.match(/suggestedPresentationDelay="([^"]+)"/);
323
+ if (match) {
324
+ const durationString = match[1];
325
+ const duration = moment.duration(durationString);
326
+ const currentDelay = duration.asSeconds();
327
+
328
+ sdkLogger.info(`Found suggestedPresentationDelay in manifest: ${currentDelay.toFixed(3)}s`);
329
+
330
+ if (currentDelay < this._minSuggestedPresentationDelay) {
331
+ // Replace the value in the manifest text with 3 decimal places
332
+ manifestText = manifestText.replace(
333
+ /suggestedPresentationDelay="[^"]+"/,
334
+ `suggestedPresentationDelay="PT${this._minSuggestedPresentationDelay.toFixed(3)}S"`
335
+ );
336
+ sdkLogger.info(`Updated manifest suggestedPresentationDelay to ${this._minSuggestedPresentationDelay.toFixed(3)}s`);
337
+ return manifestText;
338
+ }
339
+ } else {
340
+ sdkLogger.info("suggestedPresentationDelay is not defined at the manifest");
341
+ }
342
+ return undefined;
343
+ }
344
+
308
345
  /**
309
346
  * Creates an instance of SenzaShakaPlayer, which is a subclass of shaka.Player.
310
347
  *
@@ -332,14 +369,38 @@ export class SenzaShakaPlayer extends shaka.Player {
332
369
  const playTimeout = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"]?.playingEventTimeout;
333
370
  this._playingTimeout = (playTimeout >= 0) ? playTimeout*1000 : this._playingTimeout;
334
371
 
372
+ // Initialize minSuggestedPresentationDelay from UI settings or use default
373
+ const uiSettings = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"];
374
+ if (uiSettings?.minSuggestedPresentationDelay !== undefined) {
375
+ this._minSuggestedPresentationDelay = uiSettings.minSuggestedPresentationDelay;
376
+ sdkLogger.info(`Using configured minSuggestedPresentationDelay: ${this._minSuggestedPresentationDelay}s`);
377
+ }
378
+
335
379
  // if video element is provided, add the listeres here. In this case ,there is no need to call attach.
336
380
  if (videoElement) {
337
381
  this._attach(videoElement);
338
382
  sdkLogger.warn("SenzaShakaPlayer constructor Adding videoElement in the constructor is going to be deprecated in the future. Please use attach method instead.");
339
383
  }
340
384
 
385
+ this.configure({
386
+ manifest: {
387
+ defaultPresentationDelay: this._minSuggestedPresentationDelay // in seconds
388
+ }
389
+ });
390
+
391
+ remotePlayer.configure({
392
+ minSuggestedPresentationDelay: this._minSuggestedPresentationDelay
393
+ });
394
+
395
+ this.addEventListener("buffering", () => {
396
+ if (this.videoElement) {
397
+ sdkLogger.info("Buffering at time:", this.videoElement.currentTime);
398
+ }
399
+ });
400
+
341
401
  }
342
402
 
403
+
343
404
  _attach(videoElement) {
344
405
  this.videoElement = videoElement;
345
406
 
@@ -618,16 +679,30 @@ export class SenzaShakaPlayer extends shaka.Player {
618
679
 
619
680
  // This callbakc will be activated when the manifest is loaded. It will trigger load of the remote player.
620
681
  // This will ensure that the remote player is loaded only after the manifest is loaded by local player.
621
- const responseFilterCallback = async (type) => {
622
- if (type === shaka.net.NetworkingEngine.RequestType.MANIFEST && !manifestLoadHandled) {
623
- manifestLoadHandled = true;
682
+ const responseFilterCallback = async (type, response) => {
683
+ if (type === shaka.net.NetworkingEngine.RequestType.MANIFEST) {
624
684
  try {
625
- await this._remotePlayerLoad(url, startTime);
626
- remoteLoadResolver();
685
+ if (response.data && this._minSuggestedPresentationDelay > 0) {
686
+ const manifestText = new TextDecoder().decode(response.data);
687
+ const modifiedText = this._updateManifestDelayIfBelowMinimum(manifestText);
688
+ if (modifiedText) {
689
+ const responseData = new TextEncoder().encode(modifiedText).buffer;
690
+ response.data = responseData;
691
+ }
692
+ }
627
693
  } catch (error) {
628
- remoteLoadRejecter(error);
694
+ sdkLogger.error("Error processing manifest:", error);
629
695
  }
630
696
 
697
+ if (!manifestLoadHandled) {
698
+ manifestLoadHandled = true;
699
+ try {
700
+ await this._remotePlayerLoad(url, startTime);
701
+ remoteLoadResolver();
702
+ } catch (error) {
703
+ remoteLoadRejecter(error);
704
+ }
705
+ }
631
706
  }
632
707
  };
633
708
 
@@ -810,5 +885,5 @@ export class SenzaShakaPlayer extends shaka.Player {
810
885
 
811
886
  }
812
887
 
813
- senzaShaka.Player = SenzaShakaPlayer;
814
- export { senzaShaka as shaka };
888
+ shaka.Player = SenzaShakaPlayer;
889
+ export { shaka };
@@ -1,10 +1,15 @@
1
1
  import { getPlatformInfo } from "./api";
2
- import pack from "../package.json";
3
2
  import { sessionInfo } from "./SessionInfo";
4
- const { version } = pack;
5
3
 
6
4
  const REST_RESPONSE_TIMEOUT_SECONDS = 5;
7
5
 
6
+ export function getVersion() {
7
+ return typeof IMPLEMENTATION_VERSION !== "undefined"
8
+ // eslint-disable-next-line no-undef
9
+ ? IMPLEMENTATION_VERSION
10
+ : "unknown";
11
+ };
12
+
8
13
  export function getFCID() {
9
14
  return Math.round(Math.random() * 100000) + "-" + getPlatformInfo().sessionInfo?.connectionId;
10
15
  }
@@ -42,7 +47,7 @@ class SdkLogger {
42
47
  console.info(`[hs-sdk] [metrics] ${JSON.stringify(metricObj)}`);
43
48
  }
44
49
  withFields(logFields) {
45
- return new SdkLogger({...this.logFields, ...logFields});
50
+ return new SdkLogger({ ...this.logFields, ...logFields });
46
51
  }
47
52
  formatLogString(data) {
48
53
  let logString = "[hs-sdk] " + data.join(" ");
@@ -51,9 +56,13 @@ class SdkLogger {
51
56
  }
52
57
  return logString;
53
58
  }
59
+ addfields(fields) {
60
+ this.logFields = { ...this.logFields, ...fields };
61
+ return this;
62
+ }
54
63
  }
55
64
 
56
- export const sdkLogger = new SdkLogger({sdkVersion: version, url: window?.location?.href ?? ""});
65
+ export const sdkLogger = new SdkLogger({ sdkVersion: getVersion(), url: window?.location?.href ?? "" });
57
66
 
58
67
  export async function getRestResponse(messageName) {
59
68
 
@@ -64,14 +73,14 @@ export async function getRestResponse(messageName) {
64
73
 
65
74
  return new Promise((resolve, reject) => {
66
75
  const FCID = getFCID();
67
- const logger = sdkLogger.withFields({FCID});
76
+ const logger = sdkLogger.withFields({ FCID });
68
77
  const message = {
69
78
  type: "restRequest",
70
79
  name: messageName,
71
80
  method: "GET",
72
81
  fcid: FCID
73
82
  };
74
- const request = {target: "TC", waitForResponse: true, message: JSON.stringify(message)};
83
+ const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
75
84
  let timeoutHandler = 0;
76
85
  const queryId = window.cefQuery({
77
86
  request: JSON.stringify(request),
@@ -0,0 +1,69 @@
1
+ import { noop } from "./utils";
2
+ /**
3
+ * @class AlarmManager
4
+ * AlarmManager is a singleton class that manages the alarms in the application. It fires events whose types are the names of the alarms.
5
+ * @fires MyAlarm
6
+ */
7
+ export class AlarmManager extends EventTarget {
8
+
9
+ /**
10
+ * alarm event
11
+ *
12
+ * @event AlarmManager#MyAlarm
13
+ * @description Fired when time of 'MyAlarm' arrives. If this alarm triggers the application load and the application doesn't call
14
+ * lifecycle.moveToForeground() in the alarm callback (i.e. the application remains in the background after the callback is completed),
15
+ * the application will be unloaded.
16
+ * NOTE: If you perform async operations in the callback (without moving to foreground), you must wait for the async
17
+ * operation to finish before returning from the callback, otherwise the application will be unloaded before the async operation is finished.<br>
18
+ * @example
19
+ * alarmManager.addEventListener("MyAlarm", async (e) => {
20
+ * console.log("alarm MyAlarm arrived with data", e.detail);
21
+ * await fetch("http://www.example.com");
22
+ * });
23
+ * alarmManager.addAlarm("MyAlarm", Date.now() + 60*60*1000, "MyData");
24
+ */
25
+ constructor() {
26
+ super();
27
+ }
28
+
29
+ addEventListener(type, callback) {
30
+ noop("AlarmManager.addEventListener", type, callback);
31
+ }
32
+
33
+ removeEventListener(type, callback) {
34
+ noop("AlarmManager.removeEventListener", type, callback);
35
+ }
36
+
37
+ /** Set alarm to be fired at the specified time, event when ui is released
38
+ * @param {string} alarmName unique name for the alarm
39
+ * @param {number} alarmTime target time for the alarm to be fired, represented by the number of milliseconds elapsed since the epoch
40
+ * @param {string} [data] data to be passed back when the alarm is fired
41
+ * */
42
+ addAlarm(alarmName, alarmTime, data = "", toleranceBefore = 0, toleranceAfter = 0) {
43
+ noop("AlarmManager.addAlarm", alarmName, alarmTime, data, toleranceBefore, toleranceAfter);
44
+ }
45
+
46
+ /** Delete the specified alarm
47
+ * @param {string} alarmName name of alarm to be deleted
48
+ *
49
+ * */
50
+ deleteAlarm(alarmName) {
51
+ noop("AlarmManager.deleteAlarm", alarmName);
52
+ }
53
+
54
+ /** Delete all alarms
55
+ *
56
+ * */
57
+ deleteAllAlarms() {
58
+ noop("AlarmManager.deleteAllAlarms");
59
+ }
60
+
61
+ /** Async function that asks for all active alarms
62
+ * @returns {Promise} when resolved, returns an array of objects containing alarmName and alarmTime fields
63
+ * @throws {string} error string in case of an error
64
+ *
65
+ * */
66
+ getActiveAlarms() {
67
+ return noop("AlarmManager.getActiveAlarms");
68
+ }
69
+ }
@@ -0,0 +1,8 @@
1
+ export { Lifecycle } from "./lifecycle.js";
2
+ export { DeviceManager } from "./deviceManager.js";
3
+ export { PlatformManager } from "./platformManager.js";
4
+ export { AlarmManager } from "./alarmManager.js";
5
+ export { MessageManager } from "./messageManager.js";
6
+ export { RemotePlayer } from "./remotePlayer.js";
7
+ export { SenzaShakaPlayer as ShakaPlayer, shaka } from "./senzaShakaPlayer.js";
8
+ export { showSequence, initSequence } from "./devSequence.js";
@@ -204,6 +204,7 @@ const setupSequence = (components, items) => {
204
204
  };
205
205
  methodInject(components, inject, (name) => name.startsWith("_") || name.startsWith("get"));
206
206
  handleKeys(items);
207
+ playersEvents(components, items);
207
208
  sdkLogger.log("Sequence initialized.");
208
209
  };
209
210
 
@@ -257,3 +258,37 @@ export const showSequence = (visible = true) => {
257
258
  }
258
259
  container.style.visibility = visible ? "visible" : "hidden";
259
260
  };
261
+ function playersEvents(components, items) {
262
+ components.remotePlayer.addEventListener("videoelementattached", () => {
263
+ const video = components.remotePlayer._videoElement;
264
+ if (video && !video._devSequenceRateChangeHandler) {
265
+ video._devSequenceRateChangeHandler = () => {
266
+ items.push({
267
+ component: "localPlayer",
268
+ id: "ratechange=" + video.playbackRate,
269
+ time: performance.now() - currentTime
270
+ });
271
+ };
272
+ video.addEventListener("ratechange", video._devSequenceRateChangeHandler);
273
+ }
274
+
275
+ if (video && !video._devSequencePlayingHandler) {
276
+ video._devSequencePlayingHandler = () => {
277
+ items.push({
278
+ component: "localPlayer",
279
+ id: "playing event",
280
+ time: performance.now() - currentTime
281
+ });
282
+ };
283
+ video.addEventListener("playing", video._devSequencePlayingHandler);
284
+ }
285
+ });
286
+ components.remotePlayer.addEventListener("playing", () => {
287
+ items.push({
288
+ component: "remotePlayer",
289
+ id: "playing event",
290
+ time: performance.now() - currentTime
291
+ });
292
+ });
293
+ }
294
+
@@ -0,0 +1,133 @@
1
+ import { sdkLogger, noop } from "./utils.js";
2
+
3
+ const wifiInfo = {};
4
+
5
+ /**
6
+ * @class DeviceManager
7
+ * DeviceManager is a singleton class that manages the device
8
+ */
9
+ export class DeviceManager extends EventTarget {
10
+
11
+ constructor() {
12
+ super();
13
+ wifiInfo.level = 0;
14
+ wifiInfo.quality = 0;
15
+ wifiInfo.ssid = "unknown";
16
+ wifiInfo.bssid = "unknown";
17
+ /**
18
+ * @deprecated Instead, call deviceManager.getWifiInfo() periodically
19
+ * @event DeviceManager#wifiInfoUpdated
20
+ * @example
21
+ * deviceManager.addEventListener("wifiInfoUpdated", () => {
22
+ * console.info("Wifi info has been updated to", deviceManager.wifiInfo);
23
+ * });
24
+ *
25
+ */
26
+ }
27
+
28
+ /**
29
+ * @property {object} DeviceInfo
30
+ * @property {string} DeviceInfo.deviceId
31
+ * @property {string} DeviceInfo.modelNumber
32
+ * @property {string} DeviceInfo.connectionId
33
+ * @property {string} DeviceInfo.community
34
+ * @property {string} DeviceInfo.tenant
35
+ * @property {string} DeviceInfo.clientIp
36
+ * @property {string} DeviceInfo.countryCode A 2-letter code as defined in ISO_3166-1
37
+ * @property {string} DeviceInfo.connectionType The type of device used during the current connection. Possible values are "device" which mean real device, "simulator" - simulated device - that used during development.
38
+ */
39
+ get deviceInfo() {
40
+ sdkLogger.log("getDeviceInfo running locally, returning dummy info");
41
+ return {
42
+ deviceId: "123456789",
43
+ modelNumber: "ABC",
44
+ connectionId: "dummy",
45
+ community: "LocalDev",
46
+ tenant: "XXXXXX",
47
+ clientIp: "0.0.0.0",
48
+ countryCode: "XX",
49
+ connectionType: "simulator"
50
+ };
51
+
52
+ }
53
+
54
+ /**
55
+ * @deprecated use deviceManager.getWifiInfo() instead
56
+ * @property {object} WifiInfo
57
+ * @property {number} WifiInfo.level
58
+ * @property {number} WifiInfo.quality
59
+ * @property {string} WifiInfo.ssid
60
+ * @property {string} WifiInfo.bssid
61
+ */
62
+ get wifiInfo() {
63
+ return wifiInfo;
64
+ }
65
+
66
+ /**
67
+ * Reboot the device
68
+ * @return {Promise} Promise which is resolved when the reboot command has been successfully processed.
69
+ * Failure to process the reboot command will result in the promise being rejected.
70
+ */
71
+ reboot() {
72
+ return noop("DeviceManager.reboot");
73
+ }
74
+
75
+ /**
76
+ * Delete current wifi configuration and forget network
77
+ * @return {Promise} Promise which is resolved when the clearWifi command has been successfully processed.
78
+ * Failure to process the clearWifi command will result in the promise being rejected.
79
+ */
80
+ clearWifi() {
81
+ return noop("DeviceManager.clearWifi");
82
+ }
83
+
84
+ /**
85
+ * Send raw data directly to a customer's device via the USB serial connection of a Senza device.
86
+ * This function is specifically designed for customers who have their devices connected to a Senza device.
87
+ * Using this API, these customers can transmit messages or data directly to their connected devices.
88
+ * The transmission occurs through the Senza device's USB serial interface, facilitating direct communication
89
+ * between the customer's web application and their device.
90
+ * @param {String} data raw data to be passed to the device
91
+ * @return {Promise} Promise which is resolved when the command has been successfully processed.
92
+ * Failure to process the command will result in the promise being rejected.
93
+ */
94
+ sendDataToDevice(data) {
95
+ return noop("DeviceManager.sendDataToDevice", data);
96
+ }
97
+
98
+ /**
99
+ * Perform device factory reset.
100
+ * @param {Boolean} [reboot=true] a flag that is passed to the device to indicate whether it should reboot after the factory reset. defaults to true.
101
+ * @return {Promise} Promise which is resolved when factoryReset has been successfully performed
102
+ * Failure to factoryReset for any reason, result in the promise being rejected.
103
+ */
104
+ async factoryReset(reboot = true) {
105
+ return noop("DeviceManager.factoryReset", reboot);
106
+ }
107
+
108
+ /**
109
+ * @typedef {Object} WiFiInfo
110
+ * @property {string} ssid the name of the Wi-Fi network that the device is connected to
111
+ * @property {string} bssid the unique identifier of the Wi-Fi access point
112
+ * @property {string} standard the Wi-Fi standard in use, such as 802.11a/b/g/n/ac/ax
113
+ * @property {string} security the type of security protocol used by the Wi-Fi network, such as WEP, WPA, WPA2, or WPA3
114
+ * @property {string} device-mac the MAC address of the device
115
+ * @property {string} device-ip4 the IPv4 address assigned to the device on the Wi-Fi network
116
+ * @property {string} dhcp-server the IP address of the DHCP server that assigned the device's network configuration
117
+ * @property {string[]} dns-server array of IP addresses of the DNS servers the device uses to resolve domain names
118
+ * @property {number} channel the number of the Wi-Fi channel currently being used
119
+ * @property {number} width width of the Wi-Fi channel in megahertz, e.g. 20MHz or 40 MHz channel
120
+ * @property {number} level a measure of the received signal strength, in the range 0 to 100 (the higher, the better). The level value is 100+RSSI (RSSI is the signal strength, measured in decibels)
121
+ * @property {number} quality a measure of the signal quality, in the range 0 to 100 (the higher, the better). The quality value is derived from the signal EVM.
122
+ * */
123
+
124
+ /**
125
+ * Get Wi-Fi info - access point data and status (the status is cached for 5 seconds)
126
+ * @returns {WiFiInfo} An object containing the Wi-Fi info
127
+ * @alpha API has not yet been released
128
+ * */
129
+ // This api is part of epic HSDEV-4185
130
+ async getWifiInfo() {
131
+ return Promise.resolve({});
132
+ }
133
+ }