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

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 +2 -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 +127 -72
  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 +329 -149
  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,201 @@ 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
- // block play if player state is playing when expected synced state is paused.
170
+ // block play if player state is playing when expected synced state is paused and coordinator is not suspended.
128
171
  // needed because cannot tell if its a user initiated event, so disallow play
129
- if (this._expectedPlaybackState === 'paused') {
172
+ if (
173
+ this._expectedPlaybackState === "paused" &&
174
+ !this._mediaSession.coordinator.isSuspended
175
+ ) {
130
176
  this._player.pause();
131
177
  }
132
178
 
133
- // block play if player state is playing when expected synced state is none.
179
+ // block play if player state is playing when expected synced state is none and coordinator is not suspended.
134
180
  // needed because user who is not in control should not be able to start, so disallow play
135
- if (this._expectedPlaybackState === 'none') {
181
+ if (
182
+ this._expectedPlaybackState === "none" &&
183
+ !this._mediaSession.coordinator.isSuspended
184
+ ) {
136
185
  this._player.pause();
137
186
  }
138
187
  break;
139
- case 'pause':
140
- // block pause if player state is paused when expected synced state is playing
188
+ case "pause":
189
+ // block pause if player state is paused when expected synced state is playing and coordinator is not suspended.
141
190
  // needed because cannot tell if its a user initiated event, so disallow pause
142
- if (this._expectedPlaybackState === 'playing') {
191
+ if (
192
+ this._expectedPlaybackState === "playing" &&
193
+ !this._mediaSession.coordinator.isSuspended
194
+ ) {
143
195
  this._player.play();
144
196
  }
145
197
  break;
146
- case 'ratechange':
198
+ case "ratechange":
147
199
  // Block rate changes unless suspended.
148
- if (this._player.playbackRate != 1.0 && !this._mediaSession.coordinator.isSuspended) {
149
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlaybackRateChangeBlocked);
200
+ if (
201
+ this._player.playbackRate != 1.0 &&
202
+ !this._mediaSession.coordinator.isSuspended
203
+ ) {
204
+ this._logger.sendTelemetryEvent(
205
+ TelemetryEvents.MediaPlayerSynchronizer
206
+ .PlaybackRateChangeBlocked
207
+ );
150
208
  this._player.playbackRate = 1.0;
151
209
  }
152
210
  break;
153
- case 'blocked':
154
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlaybackRateChangeBlocked);
211
+ case "blocked":
212
+ this._logger.sendTelemetryEvent(
213
+ TelemetryEvents.MediaPlayerSynchronizer
214
+ .PlaybackRateChangeBlocked
215
+ );
155
216
  break;
156
217
  }
157
218
  };
158
219
 
159
220
  // Register for coordinator state changes
160
- this._mediaSession.coordinator.on(MediaSessionCoordinatorEvents.coordinatorstatechange, evt => this.emit(evt.type, evt));
221
+ this._mediaSession.coordinator.on(
222
+ MediaSessionCoordinatorEvents.coordinatorstatechange,
223
+ (evt) => this.emit(evt.type, evt)
224
+ );
161
225
 
162
226
  // Register media session actions
163
227
  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!;
228
+ this._mediaSession.setActionHandler(
229
+ action,
230
+ async (details: ExtendedMediaSessionActionDetails) => {
231
+ let error: Error | undefined;
232
+ try {
233
+ switch (details.action) {
234
+ case "play":
235
+ this._expectedPlaybackState = "playing";
236
+ if (this._player.paused) {
237
+ this._logger.sendTelemetryEvent(
238
+ TelemetryEvents.MediaPlayerSynchronizer
239
+ .PlayAction
240
+ );
241
+ if (
242
+ typeof details.seekTime == "number" &&
243
+ this._player.currentTime < 1.0
244
+ ) {
245
+ this._logger.sendTelemetryEvent(
246
+ TelemetryEvents
247
+ .MediaPlayerSynchronizer
248
+ .SeekingPlayerToStartPosition,
249
+ null,
250
+ { position: details.seekTime }
251
+ );
252
+ this._player.currentTime =
253
+ details.seekTime!;
254
+ }
255
+ // Reference: https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/play#exceptions
256
+ await this._player.play();
175
257
  }
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.");
258
+ break;
259
+ case "pause":
260
+ this._logger.sendTelemetryEvent(
261
+ TelemetryEvents.MediaPlayerSynchronizer
262
+ .PauseAction
263
+ );
264
+ this._expectedPlaybackState = "paused";
265
+ if (
266
+ typeof details.seekTime == "number" &&
267
+ this._player.currentTime < 1.0
268
+ ) {
269
+ this._logger.sendTelemetryEvent(
270
+ TelemetryEvents.MediaPlayerSynchronizer
271
+ .SeekingPlayerToStartPosition,
272
+ null,
273
+ { position: details.seekTime }
274
+ );
275
+ this._player.currentTime =
276
+ details.seekTime!;
277
+ }
278
+ this._player.pause();
279
+ break;
280
+ case "seekto":
281
+ if (typeof details.seekTime == "number") {
282
+ this._logger.sendTelemetryEvent(
283
+ TelemetryEvents.MediaPlayerSynchronizer
284
+ .SeekToAction,
285
+ null,
286
+ { position: details.seekTime }
287
+ );
288
+ this._player.currentTime =
289
+ details.seekTime!;
290
+ }
291
+ break;
292
+ case "settrack":
293
+ this._logger.sendTelemetryEvent(
294
+ TelemetryEvents.MediaPlayerSynchronizer
295
+ .SetTrackAction
296
+ );
297
+ this._expectedPlaybackState = "none";
298
+ this._player.src =
299
+ details.metadata!.trackIdentifier;
300
+ this._player.load();
301
+ break;
302
+ case "datachange":
303
+ this._logger.sendTelemetryEvent(
304
+ TelemetryEvents.MediaPlayerSynchronizer
305
+ .DataChangeAction
306
+ );
307
+ break;
308
+ case "catchup":
309
+ if (typeof details.seekTime == "number") {
310
+ this._logger.sendTelemetryEvent(
311
+ TelemetryEvents.MediaPlayerSynchronizer
312
+ .CatchupAction,
313
+ null,
314
+ { position: details.seekTime }
315
+ );
316
+ this.catchupPlayer(details.seekTime!);
317
+ }
318
+ break;
319
+ }
320
+ } catch (err: any) {
321
+ if (err instanceof Error) {
322
+ error = err;
323
+ } else {
324
+ error = new Error(
325
+ err?.message ??
326
+ "An unknown error occurred after processing the group session action."
327
+ );
328
+ }
216
329
  }
217
- }
218
330
 
219
- this.dispatchGroupAction(details, false, error);
220
- });
331
+ this.dispatchGroupAction(details, false, error);
332
+ }
333
+ );
221
334
  }
222
335
 
223
336
  // Subscribe to player events
@@ -225,7 +338,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
225
338
  this._player.addEventListener(event, this._onPlayerEvent);
226
339
  }
227
340
 
228
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted);
341
+ this._logger.sendTelemetryEvent(
342
+ TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted
343
+ );
229
344
  }
230
345
 
231
346
  /**
@@ -244,7 +359,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
244
359
 
245
360
  /**
246
361
  * If true the client is in a view only mode.
247
- *
362
+ *
248
363
  * @remarks
249
364
  * Toggling this value to true results in `mediaSession.coordinator.canPlayPause`,
250
365
  * `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`, and
@@ -276,7 +391,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
276
391
  * @param event Name of the event to add.
277
392
  * @param listener Function to call when the event is triggered.
278
393
  */
279
- public addEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
394
+ public addEventListener(
395
+ event: MediaPlayerSynchronizerEvents,
396
+ listener: (evt: IMediaPlayerSynchronizerEvent) => void
397
+ ): this {
280
398
  this.on(event, listener);
281
399
  return this;
282
400
  }
@@ -286,7 +404,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
286
404
  * @param event Name of the event to remove.
287
405
  * @param listener Function that was registered in call to `addEventListener()`.
288
406
  */
289
- public removeEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
407
+ public removeEventListener(
408
+ event: MediaPlayerSynchronizerEvents,
409
+ listener: (evt: IMediaPlayerSynchronizerEvent) => void
410
+ ): this {
290
411
  this.off(event, listener);
291
412
  return this;
292
413
  }
@@ -304,7 +425,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
304
425
 
305
426
  // Subscribe to player events
306
427
  for (const event of MediaPlayerSynchronizer.PLAYER_EVENTS) {
307
- this._player.removeEventListener(event, this._onPlayerEvent);
428
+ this._player.removeEventListener(
429
+ event,
430
+ this._onPlayerEvent
431
+ );
308
432
  }
309
433
 
310
434
  // Notify parent
@@ -317,19 +441,23 @@ export class MediaPlayerSynchronizer extends EventEmitter {
317
441
 
318
442
  /**
319
443
  * Begin a local seek operation.
320
- *
444
+ *
321
445
  * @remarks
322
- * UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
446
+ * UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
323
447
  * new playback position. The synchronizer will being a new suspension which temporarily
324
448
  * disconnects the client for the rest of the group for synchronization purposes. Calling
325
449
  * `endSeek()` will end the suspension and seek the group to the users final seek position.
326
450
  */
327
451
  public beginSeek(): void {
328
452
  if (this._seekSuspension) {
329
- throw new Error(`MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`);
453
+ throw new Error(
454
+ `MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`
455
+ );
330
456
  }
331
457
 
332
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.BeginSeekCalled);
458
+ this._logger.sendTelemetryEvent(
459
+ TelemetryEvents.MediaPlayerSynchronizer.BeginSeekCalled
460
+ );
333
461
  this._seekSuspension = this._mediaSession.coordinator.beginSuspension();
334
462
  }
335
463
 
@@ -339,7 +467,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
339
467
  */
340
468
  public endSeek(seekTo: number): void {
341
469
  if (!this._seekSuspension) {
342
- throw new Error(`MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`);
470
+ throw new Error(
471
+ `MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`
472
+ );
343
473
  }
344
474
 
345
475
  // Seek player to new time immediately.
@@ -350,48 +480,58 @@ export class MediaPlayerSynchronizer extends EventEmitter {
350
480
  const suspension = this._seekSuspension;
351
481
  this._seekSuspension = undefined;
352
482
 
353
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.EndSeekCalled, null, {position: seekTo});
483
+ this._logger.sendTelemetryEvent(
484
+ TelemetryEvents.MediaPlayerSynchronizer.EndSeekCalled,
485
+ null,
486
+ {
487
+ position: seekTo,
488
+ }
489
+ );
354
490
  suspension.end(seekTo);
355
491
 
356
- this.dispatchUserAction({action: 'seekto', seekTime: seekTo});
492
+ this.dispatchUserAction({ action: "seekto", seekTime: seekTo });
357
493
  }
358
494
 
359
495
  /**
360
496
  * Tells the group to begin playing the current video.
361
- *
497
+ *
362
498
  * @remarks
363
- * For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
364
- * and instead use the synchronizers `play()` method.
499
+ * For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
500
+ * and instead use the synchronizers `play()` method.
365
501
  */
366
502
  public play(): void {
367
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayCalled);
368
- this._expectedPlaybackState = 'playing';
503
+ this._logger.sendTelemetryEvent(
504
+ TelemetryEvents.MediaPlayerSynchronizer.PlayCalled
505
+ );
506
+ this._expectedPlaybackState = "playing";
369
507
  this._mediaSession.coordinator.play();
370
508
 
371
- this.dispatchUserAction({action: 'play'});
509
+ this.dispatchUserAction({ action: "play" });
372
510
  }
373
511
 
374
512
  /**
375
513
  * Tells the group to pause the current video.
376
- *
514
+ *
377
515
  * @remarks
378
- * For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
379
- * and instead use the synchronizers `pause()` method.
516
+ * For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
517
+ * and instead use the synchronizers `pause()` method.
380
518
  */
381
519
  public pause(): void {
382
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PauseCalled);
383
- this._expectedPlaybackState = 'paused';
520
+ this._logger.sendTelemetryEvent(
521
+ TelemetryEvents.MediaPlayerSynchronizer.PauseCalled
522
+ );
523
+ this._expectedPlaybackState = "paused";
384
524
  this._mediaSession.coordinator.pause();
385
525
 
386
- this.dispatchUserAction({action: 'pause'});
526
+ this.dispatchUserAction({ action: "pause" });
387
527
  }
388
528
 
389
529
  /**
390
530
  * Tells the group to seek the current video to a new playback position.
391
- *
531
+ *
392
532
  * @remarks
393
- * For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
394
- * and instead use the synchronizers `seekTo()` method.
533
+ * For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
534
+ * and instead use the synchronizers `seekTo()` method.
395
535
  */
396
536
  public seekTo(time: number): void {
397
537
  // Always seek player to new time.
@@ -399,58 +539,98 @@ export class MediaPlayerSynchronizer extends EventEmitter {
399
539
  // location.
400
540
  this._player.currentTime = time;
401
541
 
402
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SeekToCalled, null, {position: time});
542
+ this._logger.sendTelemetryEvent(
543
+ TelemetryEvents.MediaPlayerSynchronizer.SeekToCalled,
544
+ null,
545
+ { position: time }
546
+ );
403
547
  this._mediaSession.coordinator.seekTo(time);
404
548
 
405
- this.dispatchUserAction({action: 'seekto', seekTime: time});
549
+ this.dispatchUserAction({ action: "seekto", seekTime: time });
406
550
  }
407
551
 
408
552
  /**
409
553
  * Tells the group to change to a new track.
410
- *
554
+ *
411
555
  * @remarks
412
- * For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
413
- * and instead use the synchronizers `setTrack()` method.
556
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
557
+ * and instead use the synchronizers `setTrack()` method.
414
558
  */
415
- public setTrack(track: ExtendedMediaMetadata, waitPoints?: CoordinationWaitPoint[]): void {
416
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled);
559
+ public setTrack(
560
+ track: ExtendedMediaMetadata,
561
+ waitPoints?: CoordinationWaitPoint[]
562
+ ): void {
563
+ this._logger.sendTelemetryEvent(
564
+ TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled
565
+ );
417
566
  this._mediaSession.coordinator.setTrack(track, waitPoints);
418
567
 
419
- this.dispatchUserAction({action: 'settrack', metadata: track});
568
+ this.dispatchUserAction({ action: "settrack", metadata: track });
420
569
  }
421
570
 
422
571
  /**
423
572
  * Updates the current tracks data object.
424
- *
573
+ *
425
574
  * @remarks
426
- * For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
427
- * and instead use the synchronizers `setTrackData()` method.
575
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
576
+ * and instead use the synchronizers `setTrackData()` method.
428
577
  */
429
- public setTrackData(data: object|null): void {
430
- this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled);
578
+ public setTrackData(data: object | null): void {
579
+ this._logger.sendTelemetryEvent(
580
+ TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled
581
+ );
431
582
  this._trackData = data;
432
583
  this._mediaSession.coordinator.setTrackData(data);
433
584
 
434
- this.dispatchUserAction({action: 'datachange', data: data});
585
+ this.dispatchUserAction({ action: "datachange", data: data });
435
586
  }
436
587
 
437
- private dispatchGroupAction(details: ExtendedMediaSessionActionDetails, delay = false, error: Error | undefined): void {
588
+ private dispatchGroupAction(
589
+ details: ExtendedMediaSessionActionDetails,
590
+ delay = false,
591
+ error: Error | undefined
592
+ ): void {
438
593
  if (delay) {
439
- setTimeout(() => this.emit(MediaPlayerSynchronizerEvents.groupaction, {type: MediaPlayerSynchronizerEvents.groupaction, details: details, playerError: error}), 50);
594
+ setTimeout(
595
+ () =>
596
+ this.emit(MediaPlayerSynchronizerEvents.groupaction, {
597
+ type: MediaPlayerSynchronizerEvents.groupaction,
598
+ details: details,
599
+ playerError: error,
600
+ }),
601
+ 50
602
+ );
440
603
  } else {
441
- this.emit(MediaPlayerSynchronizerEvents.groupaction, {type: MediaPlayerSynchronizerEvents.groupaction, details: details, error});
604
+ this.emit(MediaPlayerSynchronizerEvents.groupaction, {
605
+ type: MediaPlayerSynchronizerEvents.groupaction,
606
+ details: details,
607
+ error,
608
+ });
442
609
  }
443
610
  }
444
611
 
445
- private dispatchUserAction(details: ExtendedMediaSessionActionDetails, delay = false): void {
612
+ private dispatchUserAction(
613
+ details: ExtendedMediaSessionActionDetails,
614
+ delay = false
615
+ ): void {
446
616
  if (delay) {
447
- setTimeout(() => this.emit(MediaPlayerSynchronizerEvents.useraction, {type: MediaPlayerSynchronizerEvents.useraction, details: details}), 50);
617
+ setTimeout(
618
+ () =>
619
+ this.emit(MediaPlayerSynchronizerEvents.useraction, {
620
+ type: MediaPlayerSynchronizerEvents.useraction,
621
+ details: details,
622
+ }),
623
+ 50
624
+ );
448
625
  } else {
449
- this.emit(MediaPlayerSynchronizerEvents.useraction, {type: MediaPlayerSynchronizerEvents.useraction, details: details});
626
+ this.emit(MediaPlayerSynchronizerEvents.useraction, {
627
+ type: MediaPlayerSynchronizerEvents.useraction,
628
+ details: details,
629
+ });
450
630
  }
451
631
  }
452
632
 
453
633
  private async catchupPlayer(time: number): Promise<void> {
454
634
  this._player.currentTime = time;
455
635
  }
456
- }
636
+ }