@microsoft/live-share-media 1.0.0-preview.1 → 1.0.0-preview.3

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 (96) hide show
  1. package/README.md +1 -1
  2. package/bin/IMediaPlayer.d.ts.map +1 -1
  3. package/bin/LiveMediaSession.d.ts +7 -7
  4. package/bin/LiveMediaSession.d.ts.map +1 -1
  5. package/bin/LiveMediaSession.js +28 -17
  6. package/bin/LiveMediaSession.js.map +1 -1
  7. package/bin/LiveMediaSessionCoordinator.d.ts +2 -2
  8. package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
  9. package/bin/LiveMediaSessionCoordinator.js +34 -22
  10. package/bin/LiveMediaSessionCoordinator.js.map +1 -1
  11. package/bin/LiveMediaSessionCoordinatorSuspension.d.ts +1 -1
  12. package/bin/LiveMediaSessionCoordinatorSuspension.d.ts.map +1 -1
  13. package/bin/LiveMediaSessionCoordinatorSuspension.js.map +1 -1
  14. package/bin/MediaPlayerSynchronizer.d.ts +7 -7
  15. package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
  16. package/bin/MediaPlayerSynchronizer.js +121 -69
  17. package/bin/MediaPlayerSynchronizer.js.map +1 -1
  18. package/bin/MediaSessionActionThrottler.d.ts +1 -1
  19. package/bin/MediaSessionActionThrottler.d.ts.map +1 -1
  20. package/bin/MediaSessionActionThrottler.js.map +1 -1
  21. package/bin/MediaSessionExtensions.d.ts.map +1 -1
  22. package/bin/RepeatedActionThrottler.d.ts +2 -2
  23. package/bin/RepeatedActionThrottler.d.ts.map +1 -1
  24. package/bin/RepeatedActionThrottler.js +9 -3
  25. package/bin/RepeatedActionThrottler.js.map +1 -1
  26. package/bin/VolumeManager.d.ts +1 -1
  27. package/bin/VolumeManager.d.ts.map +1 -1
  28. package/bin/VolumeManager.js +4 -2
  29. package/bin/VolumeManager.js.map +1 -1
  30. package/bin/index.d.ts +9 -9
  31. package/bin/index.d.ts.map +1 -1
  32. package/bin/index.js.map +1 -1
  33. package/bin/internals/GroupCoordinatorState.d.ts +8 -8
  34. package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
  35. package/bin/internals/GroupCoordinatorState.js +91 -53
  36. package/bin/internals/GroupCoordinatorState.js.map +1 -1
  37. package/bin/internals/GroupPlaybackPosition.d.ts +3 -3
  38. package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
  39. package/bin/internals/GroupPlaybackPosition.js +22 -13
  40. package/bin/internals/GroupPlaybackPosition.js.map +1 -1
  41. package/bin/internals/GroupPlaybackTrack.d.ts +4 -4
  42. package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
  43. package/bin/internals/GroupPlaybackTrack.js +14 -4
  44. package/bin/internals/GroupPlaybackTrack.js.map +1 -1
  45. package/bin/internals/GroupPlaybackTrackData.d.ts +3 -3
  46. package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
  47. package/bin/internals/GroupPlaybackTrackData.js +8 -4
  48. package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
  49. package/bin/internals/GroupTransportState.d.ts +5 -5
  50. package/bin/internals/GroupTransportState.d.ts.map +1 -1
  51. package/bin/internals/GroupTransportState.js +27 -12
  52. package/bin/internals/GroupTransportState.js.map +1 -1
  53. package/bin/internals/consts.d.ts.map +1 -1
  54. package/bin/internals/consts.js +54 -54
  55. package/bin/internals/consts.js.map +1 -1
  56. package/bin/internals/index.d.ts +6 -6
  57. package/package.json +3 -3
  58. package/src/IMediaPlayer.ts +10 -2
  59. package/src/LiveMediaSession.ts +133 -71
  60. package/src/LiveMediaSessionCoordinator.ts +302 -142
  61. package/src/LiveMediaSessionCoordinatorSuspension.ts +13 -5
  62. package/src/MediaPlayerSynchronizer.ts +317 -146
  63. package/src/MediaSessionActionThrottler.ts +5 -3
  64. package/src/MediaSessionExtensions.ts +15 -5
  65. package/src/RepeatedActionThrottler.ts +27 -9
  66. package/src/VolumeManager.ts +24 -12
  67. package/src/index.ts +9 -10
  68. package/src/internals/GroupCoordinatorState.ts +274 -114
  69. package/src/internals/GroupPlaybackPosition.ts +61 -27
  70. package/src/internals/GroupPlaybackTrack.ts +51 -22
  71. package/src/internals/GroupPlaybackTrackData.ts +24 -16
  72. package/src/internals/GroupTransportState.ts +53 -23
  73. package/src/internals/consts.ts +73 -55
  74. package/src/internals/index.ts +6 -6
  75. package/src/test/GroupPlaybackPosition.spec.ts +371 -111
  76. package/src/test/GroupPlaybackTrack.spec.ts +91 -35
  77. package/src/test/SharedMediaSession.spec.ts +64 -28
  78. package/src/test/SharedMediaSessionCoordinator.spec.ts +86 -46
  79. package/src/test/VolumeManager.spec.ts +38 -31
  80. package/tsconfig.json +1 -0
  81. package/bin/EphemeralMediaSession.d.ts +0 -102
  82. package/bin/EphemeralMediaSession.d.ts.map +0 -1
  83. package/bin/EphemeralMediaSession.js +0 -226
  84. package/bin/EphemeralMediaSession.js.map +0 -1
  85. package/bin/EphemeralMediaSessionCoordinator.d.ts +0 -228
  86. package/bin/EphemeralMediaSessionCoordinator.d.ts.map +0 -1
  87. package/bin/EphemeralMediaSessionCoordinator.js +0 -396
  88. package/bin/EphemeralMediaSessionCoordinator.js.map +0 -1
  89. package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts +0 -20
  90. package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts.map +0 -1
  91. package/bin/EphemeralMediaSessionCoordinatorSuspension.js +0 -28
  92. package/bin/EphemeralMediaSessionCoordinatorSuspension.js.map +0 -1
  93. package/bin/VolumeLimiter.d.ts +0 -70
  94. package/bin/VolumeLimiter.d.ts.map +0 -1
  95. package/bin/VolumeLimiter.js +0 -170
  96. package/bin/VolumeLimiter.js.map +0 -1
@@ -2,14 +2,21 @@
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
-
6
- import { LiveTelemetryLogger, IEvent, TimeInterval } from '@microsoft/live-share';
7
- import EventEmitter from 'events';
8
- import { ExtendedMediaSessionAction, ExtendedMediaSessionPlaybackState, ExtendedMediaMetadata, CoordinationWaitPoint, ExtendedMediaSessionActionDetails, MediaSessionCoordinatorEvents, MediaSessionCoordinatorSuspension } from './MediaSessionExtensions';
9
- import { VolumeManager } from './VolumeManager';
10
- import { LiveMediaSession } from './LiveMediaSession';
11
- import { IMediaPlayer } from './IMediaPlayer';
12
- import { TelemetryEvents } from './internals';
5
+ import { LiveTelemetryLogger, IEvent } from "@microsoft/live-share";
6
+ import EventEmitter from "events";
7
+ import {
8
+ ExtendedMediaSessionAction,
9
+ ExtendedMediaSessionPlaybackState,
10
+ ExtendedMediaMetadata,
11
+ CoordinationWaitPoint,
12
+ ExtendedMediaSessionActionDetails,
13
+ MediaSessionCoordinatorEvents,
14
+ MediaSessionCoordinatorSuspension,
15
+ } from "./MediaSessionExtensions";
16
+ import { VolumeManager } from "./VolumeManager";
17
+ import { LiveMediaSession } from "./LiveMediaSession";
18
+ import { IMediaPlayer } from "./IMediaPlayer";
19
+ import { TelemetryEvents } from "./internals";
13
20
 
14
21
  /**
15
22
  * Event data returned by `MediaPlayerSynchronizer` object.
@@ -30,25 +37,41 @@ export interface IMediaPlayerSynchronizerEvent extends IEvent {
30
37
  */
31
38
  export enum MediaPlayerSynchronizerEvents {
32
39
  /**
33
- *
40
+ *
34
41
  */
35
- coordinatorstatechange = 'coordinatorstatechange',
36
- groupaction = 'groupaction',
37
- useraction = 'useraction'
42
+ coordinatorstatechange = "coordinatorstatechange",
43
+ groupaction = "groupaction",
44
+ useraction = "useraction",
38
45
  }
39
46
 
40
47
  /**
41
48
  * Synchronizes a local HTML Media Element with a group of remote HTML Media Elements.
42
- *
49
+ *
43
50
  * @remarks
44
51
  * All of an apps transport control commands should be routed through the synchronizer. If the
45
- * app is not currently joined to the group media session, the commands will be applied directly
52
+ * app is not currently joined to the group media session, the commands will be applied directly
46
53
  * to the local player. When the group session is joined the commands will be broadcast to the
47
- * group in addition to being applied to the local player.
54
+ * group in addition to being applied to the local player.
48
55
  */
49
56
  export class MediaPlayerSynchronizer extends EventEmitter {
50
- private static SESSION_ACTIONS: ExtendedMediaSessionAction[] = ['play', 'pause', 'seekto', 'settrack', 'datachange', 'catchup', 'wait'];
51
- private static PLAYER_EVENTS: string[] = ['playing', 'pause', 'ratechange', 'timeupdate', 'ended', 'loadedmetadata', 'blocked'];
57
+ private static SESSION_ACTIONS: ExtendedMediaSessionAction[] = [
58
+ "play",
59
+ "pause",
60
+ "seekto",
61
+ "settrack",
62
+ "datachange",
63
+ "catchup",
64
+ "wait",
65
+ ];
66
+ private static PLAYER_EVENTS: string[] = [
67
+ "playing",
68
+ "pause",
69
+ "ratechange",
70
+ "timeupdate",
71
+ "ended",
72
+ "loadedmetadata",
73
+ "blocked",
74
+ ];
52
75
 
53
76
  private _logger: LiveTelemetryLogger;
54
77
  private _player: IMediaPlayer;
@@ -58,8 +81,8 @@ export class MediaPlayerSynchronizer extends EventEmitter {
58
81
  private _onPlayerEvent: EventListener;
59
82
  private _seekSuspension?: MediaSessionCoordinatorSuspension;
60
83
  private _viewOnly = false;
61
- private _expectedPlaybackState: ExtendedMediaSessionPlaybackState = 'none';
62
- private _trackData: object|null = null;
84
+ private _expectedPlaybackState: ExtendedMediaSessionPlaybackState = "none";
85
+ private _trackData: object | null = null;
63
86
 
64
87
  /**
65
88
  * Creates a new `MediaElementSynchronizer` instance.
@@ -67,7 +90,11 @@ export class MediaPlayerSynchronizer extends EventEmitter {
67
90
  * @param mediaSession Group MediaSession object being used.
68
91
  * @param onEnd Optional. Function to call when synchronizers `end()` method is called.
69
92
  */
70
- constructor(player: IMediaPlayer, mediaSession: LiveMediaSession, onEnd: () => void) {
93
+ constructor(
94
+ player: IMediaPlayer,
95
+ mediaSession: LiveMediaSession,
96
+ onEnd: () => void
97
+ ) {
71
98
  super();
72
99
  this._player = player;
73
100
  this._mediaSession = mediaSession;
@@ -81,13 +108,16 @@ export class MediaPlayerSynchronizer extends EventEmitter {
81
108
  let playbackState: ExtendedMediaSessionPlaybackState;
82
109
  const src = this._player.currentSrc || this._player.src;
83
110
  if (this._player.ended) {
84
- playbackState = 'ended';
85
- } else if (!src || (this._player.paused && this._player.currentTime < 1.0)) {
86
- playbackState = 'none';
111
+ playbackState = "ended";
112
+ } else if (
113
+ !src ||
114
+ (this._player.paused && this._player.currentTime < 1.0)
115
+ ) {
116
+ playbackState = "none";
87
117
  } else if (this._player.paused) {
88
- playbackState = 'paused';
118
+ playbackState = "paused";
89
119
  } else {
90
- playbackState = 'playing';
120
+ playbackState = "playing";
91
121
  }
92
122
 
93
123
  const state = {
@@ -96,9 +126,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
96
126
  positionState: {
97
127
  position: this._player.currentTime,
98
128
  playbackRate: this._player.playbackRate,
99
- duration: this._player.duration
129
+ duration: this._player.duration,
100
130
  },
101
- trackData: this._trackData
131
+ trackData: this._trackData,
102
132
  };
103
133
 
104
134
  return state;
@@ -106,118 +136,192 @@ export class MediaPlayerSynchronizer extends EventEmitter {
106
136
 
107
137
  // Listen for player events
108
138
  this._onPlayerEvent = (evt: Event) => {
109
- if (evt.type != 'timeupdate') {
110
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayerEvent, null, {type: evt.type});
139
+ if (evt.type != "timeupdate") {
140
+ this._logger.sendTelemetryEvent(
141
+ TelemetryEvents.MediaPlayerSynchronizer.PlayerEvent,
142
+ null,
143
+ {
144
+ type: evt.type,
145
+ }
146
+ );
111
147
  }
112
148
 
113
149
  switch (evt.type) {
114
- case 'loadedmetadata':
150
+ case "loadedmetadata":
151
+ // eslint-disable-next-line no-case-declarations
115
152
  const src = this._player.currentSrc || this._player.src;
116
153
  this._trackData = null;
117
154
  break;
118
- case 'playing':
155
+ case "playing":
119
156
  // Handle case for YouTube player where user can pause/play video by clicking on it.
120
157
  // - Videos don't always start at 0.0 seconds.
121
- if (this._expectedPlaybackState != 'playing') {
122
- if (this._mediaSession.coordinator.canPlayPause && this._player.currentTime < 1.0) {
123
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.UserTappedVideoToPlay);
158
+ if (this._expectedPlaybackState != "playing") {
159
+ if (
160
+ this._mediaSession.coordinator.canPlayPause &&
161
+ this._player.currentTime < 1.0
162
+ ) {
163
+ this._logger.sendTelemetryEvent(
164
+ TelemetryEvents.MediaPlayerSynchronizer
165
+ .UserTappedVideoToPlay
166
+ );
124
167
  this.play();
125
168
  }
126
169
  }
127
170
  // block play if player state is playing when expected synced state is paused.
128
171
  // needed because cannot tell if its a user initiated event, so disallow play
129
- if (this._expectedPlaybackState === 'paused') {
172
+ if (this._expectedPlaybackState === "paused") {
130
173
  this._player.pause();
131
174
  }
132
175
 
133
176
  // block play if player state is playing when expected synced state is none.
134
177
  // needed because user who is not in control should not be able to start, so disallow play
135
- if (this._expectedPlaybackState === 'none') {
178
+ if (this._expectedPlaybackState === "none") {
136
179
  this._player.pause();
137
180
  }
138
181
  break;
139
- case 'pause':
182
+ case "pause":
140
183
  // block pause if player state is paused when expected synced state is playing
141
184
  // needed because cannot tell if its a user initiated event, so disallow pause
142
- if (this._expectedPlaybackState === 'playing') {
185
+ if (this._expectedPlaybackState === "playing") {
143
186
  this._player.play();
144
187
  }
145
188
  break;
146
- case 'ratechange':
189
+ case "ratechange":
147
190
  // Block rate changes unless suspended.
148
- if (this._player.playbackRate != 1.0 && !this._mediaSession.coordinator.isSuspended) {
149
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlaybackRateChangeBlocked);
191
+ if (
192
+ this._player.playbackRate != 1.0 &&
193
+ !this._mediaSession.coordinator.isSuspended
194
+ ) {
195
+ this._logger.sendTelemetryEvent(
196
+ TelemetryEvents.MediaPlayerSynchronizer
197
+ .PlaybackRateChangeBlocked
198
+ );
150
199
  this._player.playbackRate = 1.0;
151
200
  }
152
201
  break;
153
- case 'blocked':
154
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlaybackRateChangeBlocked);
202
+ case "blocked":
203
+ this._logger.sendTelemetryEvent(
204
+ TelemetryEvents.MediaPlayerSynchronizer
205
+ .PlaybackRateChangeBlocked
206
+ );
155
207
  break;
156
208
  }
157
209
  };
158
210
 
159
211
  // Register for coordinator state changes
160
- this._mediaSession.coordinator.on(MediaSessionCoordinatorEvents.coordinatorstatechange, evt => this.emit(evt.type, evt));
212
+ this._mediaSession.coordinator.on(
213
+ MediaSessionCoordinatorEvents.coordinatorstatechange,
214
+ (evt) => this.emit(evt.type, evt)
215
+ );
161
216
 
162
217
  // Register media session actions
163
218
  for (const action of MediaPlayerSynchronizer.SESSION_ACTIONS) {
164
- this._mediaSession.setActionHandler(action, async (details: ExtendedMediaSessionActionDetails) => {
165
- let error: Error | undefined;
166
- try {
167
- switch (details.action) {
168
- case 'play':
169
- this._expectedPlaybackState = 'playing';
170
- if (this._player.paused) {
171
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayAction);
172
- if (typeof details.seekTime == 'number' && this._player.currentTime < 1.0) {
173
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SeekingPlayerToStartPosition, null, {position: details.seekTime})
174
- this._player.currentTime = details.seekTime!;
219
+ this._mediaSession.setActionHandler(
220
+ action,
221
+ async (details: ExtendedMediaSessionActionDetails) => {
222
+ let error: Error | undefined;
223
+ try {
224
+ switch (details.action) {
225
+ case "play":
226
+ this._expectedPlaybackState = "playing";
227
+ if (this._player.paused) {
228
+ this._logger.sendTelemetryEvent(
229
+ TelemetryEvents.MediaPlayerSynchronizer
230
+ .PlayAction
231
+ );
232
+ if (
233
+ typeof details.seekTime == "number" &&
234
+ this._player.currentTime < 1.0
235
+ ) {
236
+ this._logger.sendTelemetryEvent(
237
+ TelemetryEvents
238
+ .MediaPlayerSynchronizer
239
+ .SeekingPlayerToStartPosition,
240
+ null,
241
+ { position: details.seekTime }
242
+ );
243
+ this._player.currentTime =
244
+ details.seekTime!;
245
+ }
246
+ // Reference: https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/play#exceptions
247
+ await this._player.play();
175
248
  }
176
- // Reference: https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/play#exceptions
177
- await this._player.play();
178
- }
179
- break;
180
- case 'pause':
181
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PauseAction);
182
- this._expectedPlaybackState = 'paused';
183
- if (typeof details.seekTime == 'number' && this._player.currentTime < 1.0) {
184
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SeekingPlayerToStartPosition, null, {position: details.seekTime})
185
- this._player.currentTime = details.seekTime!;
186
- }
187
- this._player.pause();
188
- break;
189
- case 'seekto':
190
- if (typeof details.seekTime == 'number') {
191
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SeekToAction, null, {position: details.seekTime});
192
- this._player.currentTime = details.seekTime!;
193
- }
194
- break;
195
- case 'settrack':
196
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackAction);
197
- this._expectedPlaybackState = 'none';
198
- this._player.src = details.metadata!.trackIdentifier;
199
- this._player.load();
200
- break;
201
- case 'datachange':
202
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.DataChangeAction);
203
- break;
204
- case 'catchup':
205
- if (typeof details.seekTime == 'number') {
206
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.CatchupAction, null, {position: details.seekTime});
207
- this.catchupPlayer(details.seekTime!);
208
- }
209
- break;
210
- }
211
- } catch (err: any) {
212
- if (err instanceof Error) {
213
- error = err;
214
- } else {
215
- error = new Error(err?.message ?? "An unknown error occurred after processing the group session action.");
249
+ break;
250
+ case "pause":
251
+ this._logger.sendTelemetryEvent(
252
+ TelemetryEvents.MediaPlayerSynchronizer
253
+ .PauseAction
254
+ );
255
+ this._expectedPlaybackState = "paused";
256
+ if (
257
+ typeof details.seekTime == "number" &&
258
+ this._player.currentTime < 1.0
259
+ ) {
260
+ this._logger.sendTelemetryEvent(
261
+ TelemetryEvents.MediaPlayerSynchronizer
262
+ .SeekingPlayerToStartPosition,
263
+ null,
264
+ { position: details.seekTime }
265
+ );
266
+ this._player.currentTime =
267
+ details.seekTime!;
268
+ }
269
+ this._player.pause();
270
+ break;
271
+ case "seekto":
272
+ if (typeof details.seekTime == "number") {
273
+ this._logger.sendTelemetryEvent(
274
+ TelemetryEvents.MediaPlayerSynchronizer
275
+ .SeekToAction,
276
+ null,
277
+ { position: details.seekTime }
278
+ );
279
+ this._player.currentTime =
280
+ details.seekTime!;
281
+ }
282
+ break;
283
+ case "settrack":
284
+ this._logger.sendTelemetryEvent(
285
+ TelemetryEvents.MediaPlayerSynchronizer
286
+ .SetTrackAction
287
+ );
288
+ this._expectedPlaybackState = "none";
289
+ this._player.src =
290
+ details.metadata!.trackIdentifier;
291
+ this._player.load();
292
+ break;
293
+ case "datachange":
294
+ this._logger.sendTelemetryEvent(
295
+ TelemetryEvents.MediaPlayerSynchronizer
296
+ .DataChangeAction
297
+ );
298
+ break;
299
+ case "catchup":
300
+ if (typeof details.seekTime == "number") {
301
+ this._logger.sendTelemetryEvent(
302
+ TelemetryEvents.MediaPlayerSynchronizer
303
+ .CatchupAction,
304
+ null,
305
+ { position: details.seekTime }
306
+ );
307
+ this.catchupPlayer(details.seekTime!);
308
+ }
309
+ break;
310
+ }
311
+ } catch (err: any) {
312
+ if (err instanceof Error) {
313
+ error = err;
314
+ } else {
315
+ error = new Error(
316
+ err?.message ??
317
+ "An unknown error occurred after processing the group session action."
318
+ );
319
+ }
216
320
  }
217
- }
218
321
 
219
- this.dispatchGroupAction(details, false, error);
220
- });
322
+ this.dispatchGroupAction(details, false, error);
323
+ }
324
+ );
221
325
  }
222
326
 
223
327
  // Subscribe to player events
@@ -225,7 +329,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
225
329
  this._player.addEventListener(event, this._onPlayerEvent);
226
330
  }
227
331
 
228
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted);
332
+ this._logger.sendTelemetryEvent(
333
+ TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted
334
+ );
229
335
  }
230
336
 
231
337
  /**
@@ -244,7 +350,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
244
350
 
245
351
  /**
246
352
  * If true the client is in a view only mode.
247
- *
353
+ *
248
354
  * @remarks
249
355
  * Toggling this value to true results in `mediaSession.coordinator.canPlayPause`,
250
356
  * `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`, and
@@ -276,7 +382,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
276
382
  * @param event Name of the event to add.
277
383
  * @param listener Function to call when the event is triggered.
278
384
  */
279
- public addEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
385
+ public addEventListener(
386
+ event: MediaPlayerSynchronizerEvents,
387
+ listener: (evt: IMediaPlayerSynchronizerEvent) => void
388
+ ): this {
280
389
  this.on(event, listener);
281
390
  return this;
282
391
  }
@@ -286,7 +395,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
286
395
  * @param event Name of the event to remove.
287
396
  * @param listener Function that was registered in call to `addEventListener()`.
288
397
  */
289
- public removeEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
398
+ public removeEventListener(
399
+ event: MediaPlayerSynchronizerEvents,
400
+ listener: (evt: IMediaPlayerSynchronizerEvent) => void
401
+ ): this {
290
402
  this.off(event, listener);
291
403
  return this;
292
404
  }
@@ -304,7 +416,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
304
416
 
305
417
  // Subscribe to player events
306
418
  for (const event of MediaPlayerSynchronizer.PLAYER_EVENTS) {
307
- this._player.removeEventListener(event, this._onPlayerEvent);
419
+ this._player.removeEventListener(
420
+ event,
421
+ this._onPlayerEvent
422
+ );
308
423
  }
309
424
 
310
425
  // Notify parent
@@ -317,19 +432,23 @@ export class MediaPlayerSynchronizer extends EventEmitter {
317
432
 
318
433
  /**
319
434
  * Begin a local seek operation.
320
- *
435
+ *
321
436
  * @remarks
322
- * UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
437
+ * UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
323
438
  * new playback position. The synchronizer will being a new suspension which temporarily
324
439
  * disconnects the client for the rest of the group for synchronization purposes. Calling
325
440
  * `endSeek()` will end the suspension and seek the group to the users final seek position.
326
441
  */
327
442
  public beginSeek(): void {
328
443
  if (this._seekSuspension) {
329
- throw new Error(`MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`);
444
+ throw new Error(
445
+ `MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`
446
+ );
330
447
  }
331
448
 
332
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.BeginSeekCalled);
449
+ this._logger.sendTelemetryEvent(
450
+ TelemetryEvents.MediaPlayerSynchronizer.BeginSeekCalled
451
+ );
333
452
  this._seekSuspension = this._mediaSession.coordinator.beginSuspension();
334
453
  }
335
454
 
@@ -339,7 +458,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
339
458
  */
340
459
  public endSeek(seekTo: number): void {
341
460
  if (!this._seekSuspension) {
342
- throw new Error(`MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`);
461
+ throw new Error(
462
+ `MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`
463
+ );
343
464
  }
344
465
 
345
466
  // Seek player to new time immediately.
@@ -350,48 +471,58 @@ export class MediaPlayerSynchronizer extends EventEmitter {
350
471
  const suspension = this._seekSuspension;
351
472
  this._seekSuspension = undefined;
352
473
 
353
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.EndSeekCalled, null, {position: seekTo});
474
+ this._logger.sendTelemetryEvent(
475
+ TelemetryEvents.MediaPlayerSynchronizer.EndSeekCalled,
476
+ null,
477
+ {
478
+ position: seekTo,
479
+ }
480
+ );
354
481
  suspension.end(seekTo);
355
482
 
356
- this.dispatchUserAction({action: 'seekto', seekTime: seekTo});
483
+ this.dispatchUserAction({ action: "seekto", seekTime: seekTo });
357
484
  }
358
485
 
359
486
  /**
360
487
  * Tells the group to begin playing the current video.
361
- *
488
+ *
362
489
  * @remarks
363
- * For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
364
- * and instead use the synchronizers `play()` method.
490
+ * For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
491
+ * and instead use the synchronizers `play()` method.
365
492
  */
366
493
  public play(): void {
367
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayCalled);
368
- this._expectedPlaybackState = 'playing';
494
+ this._logger.sendTelemetryEvent(
495
+ TelemetryEvents.MediaPlayerSynchronizer.PlayCalled
496
+ );
497
+ this._expectedPlaybackState = "playing";
369
498
  this._mediaSession.coordinator.play();
370
499
 
371
- this.dispatchUserAction({action: 'play'});
500
+ this.dispatchUserAction({ action: "play" });
372
501
  }
373
502
 
374
503
  /**
375
504
  * Tells the group to pause the current video.
376
- *
505
+ *
377
506
  * @remarks
378
- * For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
379
- * and instead use the synchronizers `pause()` method.
507
+ * For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
508
+ * and instead use the synchronizers `pause()` method.
380
509
  */
381
510
  public pause(): void {
382
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PauseCalled);
383
- this._expectedPlaybackState = 'paused';
511
+ this._logger.sendTelemetryEvent(
512
+ TelemetryEvents.MediaPlayerSynchronizer.PauseCalled
513
+ );
514
+ this._expectedPlaybackState = "paused";
384
515
  this._mediaSession.coordinator.pause();
385
516
 
386
- this.dispatchUserAction({action: 'pause'});
517
+ this.dispatchUserAction({ action: "pause" });
387
518
  }
388
519
 
389
520
  /**
390
521
  * Tells the group to seek the current video to a new playback position.
391
- *
522
+ *
392
523
  * @remarks
393
- * For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
394
- * and instead use the synchronizers `seekTo()` method.
524
+ * For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
525
+ * and instead use the synchronizers `seekTo()` method.
395
526
  */
396
527
  public seekTo(time: number): void {
397
528
  // Always seek player to new time.
@@ -399,58 +530,98 @@ export class MediaPlayerSynchronizer extends EventEmitter {
399
530
  // location.
400
531
  this._player.currentTime = time;
401
532
 
402
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SeekToCalled, null, {position: time});
533
+ this._logger.sendTelemetryEvent(
534
+ TelemetryEvents.MediaPlayerSynchronizer.SeekToCalled,
535
+ null,
536
+ { position: time }
537
+ );
403
538
  this._mediaSession.coordinator.seekTo(time);
404
539
 
405
- this.dispatchUserAction({action: 'seekto', seekTime: time});
540
+ this.dispatchUserAction({ action: "seekto", seekTime: time });
406
541
  }
407
542
 
408
543
  /**
409
544
  * Tells the group to change to a new track.
410
- *
545
+ *
411
546
  * @remarks
412
- * For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
413
- * and instead use the synchronizers `setTrack()` method.
547
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
548
+ * and instead use the synchronizers `setTrack()` method.
414
549
  */
415
- public setTrack(track: ExtendedMediaMetadata, waitPoints?: CoordinationWaitPoint[]): void {
416
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled);
550
+ public setTrack(
551
+ track: ExtendedMediaMetadata,
552
+ waitPoints?: CoordinationWaitPoint[]
553
+ ): void {
554
+ this._logger.sendTelemetryEvent(
555
+ TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled
556
+ );
417
557
  this._mediaSession.coordinator.setTrack(track, waitPoints);
418
558
 
419
- this.dispatchUserAction({action: 'settrack', metadata: track});
559
+ this.dispatchUserAction({ action: "settrack", metadata: track });
420
560
  }
421
561
 
422
562
  /**
423
563
  * Updates the current tracks data object.
424
- *
564
+ *
425
565
  * @remarks
426
- * For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
427
- * and instead use the synchronizers `setTrackData()` method.
566
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
567
+ * and instead use the synchronizers `setTrackData()` method.
428
568
  */
429
- public setTrackData(data: object|null): void {
430
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled);
569
+ public setTrackData(data: object | null): void {
570
+ this._logger.sendTelemetryEvent(
571
+ TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled
572
+ );
431
573
  this._trackData = data;
432
574
  this._mediaSession.coordinator.setTrackData(data);
433
575
 
434
- this.dispatchUserAction({action: 'datachange', data: data});
576
+ this.dispatchUserAction({ action: "datachange", data: data });
435
577
  }
436
578
 
437
- private dispatchGroupAction(details: ExtendedMediaSessionActionDetails, delay = false, error: Error | undefined): void {
579
+ private dispatchGroupAction(
580
+ details: ExtendedMediaSessionActionDetails,
581
+ delay = false,
582
+ error: Error | undefined
583
+ ): void {
438
584
  if (delay) {
439
- setTimeout(() => this.emit(MediaPlayerSynchronizerEvents.groupaction, {type: MediaPlayerSynchronizerEvents.groupaction, details: details, playerError: error}), 50);
585
+ setTimeout(
586
+ () =>
587
+ this.emit(MediaPlayerSynchronizerEvents.groupaction, {
588
+ type: MediaPlayerSynchronizerEvents.groupaction,
589
+ details: details,
590
+ playerError: error,
591
+ }),
592
+ 50
593
+ );
440
594
  } else {
441
- this.emit(MediaPlayerSynchronizerEvents.groupaction, {type: MediaPlayerSynchronizerEvents.groupaction, details: details, error});
595
+ this.emit(MediaPlayerSynchronizerEvents.groupaction, {
596
+ type: MediaPlayerSynchronizerEvents.groupaction,
597
+ details: details,
598
+ error,
599
+ });
442
600
  }
443
601
  }
444
602
 
445
- private dispatchUserAction(details: ExtendedMediaSessionActionDetails, delay = false): void {
603
+ private dispatchUserAction(
604
+ details: ExtendedMediaSessionActionDetails,
605
+ delay = false
606
+ ): void {
446
607
  if (delay) {
447
- setTimeout(() => this.emit(MediaPlayerSynchronizerEvents.useraction, {type: MediaPlayerSynchronizerEvents.useraction, details: details}), 50);
608
+ setTimeout(
609
+ () =>
610
+ this.emit(MediaPlayerSynchronizerEvents.useraction, {
611
+ type: MediaPlayerSynchronizerEvents.useraction,
612
+ details: details,
613
+ }),
614
+ 50
615
+ );
448
616
  } else {
449
- this.emit(MediaPlayerSynchronizerEvents.useraction, {type: MediaPlayerSynchronizerEvents.useraction, details: details});
617
+ this.emit(MediaPlayerSynchronizerEvents.useraction, {
618
+ type: MediaPlayerSynchronizerEvents.useraction,
619
+ details: details,
620
+ });
450
621
  }
451
622
  }
452
623
 
453
624
  private async catchupPlayer(time: number): Promise<void> {
454
625
  this._player.currentTime = time;
455
626
  }
456
- }
627
+ }