@gcorevideo/player 2.1.10 → 2.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Player.ts CHANGED
@@ -5,36 +5,41 @@ import {
5
5
  Player as PlayerClappr,
6
6
  $,
7
7
  Loader,
8
- } from '@clappr/core';
9
- import assert from 'assert';
10
- import EventLite from "event-lite";
8
+ } from '@clappr/core'
9
+ import assert from 'assert'
10
+ import EventLite from 'event-lite'
11
11
 
12
12
  import type {
13
13
  CorePlayerEvents,
14
14
  CoreOptions,
15
15
  CorePluginOptions,
16
- } from "./internal.types.js";
16
+ } from './internal.types.js'
17
17
  import type {
18
18
  BitrateInfo,
19
19
  PlaybackType,
20
20
  PlayerPlugin,
21
21
  StreamMediaSource,
22
- } from "./types.js";
23
- import { reportError, trace } from "./trace/index.js";
22
+ } from './types.js'
23
+ import {
24
+ reportError,
25
+ trace,
26
+ } from './trace/index.js'
24
27
  import {
25
28
  PlayerConfig,
26
29
  PlayerEvent,
27
- } from "./types.js";
28
- import DashPlayback from './plugins/dash-playback/DashPlayback.js';
29
- import HlsPlayback from './plugins/hls-playback/HlsPlayback.js';
30
+ } from './types.js'
31
+ import DashPlayback from './plugins/dash-playback/DashPlayback.js'
32
+ import HlsPlayback from './plugins/hls-playback/HlsPlayback.js'
30
33
 
31
- import '../assets/style/main.scss'; // TODO check if needed
34
+ import '../assets/style/main.scss' // TODO check if needed
32
35
 
33
36
  // TODO implement transport retry/failover and fallback logic
34
37
 
35
- type PlayerEventHandler<T extends PlayerEvent> = () => void;
38
+ type PlayerEventHandler<
39
+ T extends PlayerEvent,
40
+ > = () => void
36
41
 
37
- const T = "GPlayer";
42
+ const T = 'GPlayer'
38
43
 
39
44
  const DEFAULT_OPTIONS: PlayerConfig = {
40
45
  autoPlay: false,
@@ -49,186 +54,272 @@ const DEFAULT_OPTIONS: PlayerConfig = {
49
54
  poster: '',
50
55
  }
51
56
 
52
- export type PlaybackModule = 'dash' | 'hls' | 'native';
57
+ export type PlaybackModule =
58
+ | 'dash'
59
+ | 'hls'
60
+ | 'native'
53
61
 
54
- type PluginOptions = Record<string, unknown>;
62
+ type PluginOptions = Record<
63
+ string,
64
+ unknown
65
+ >
55
66
 
56
67
  /**
57
68
  * @beta
58
69
  */
59
70
  export class Player {
60
- private bitrateInfo: BitrateInfo | null = null;
71
+ private bitrateInfo: BitrateInfo | null =
72
+ null
61
73
 
62
- private config: PlayerConfig = DEFAULT_OPTIONS;
74
+ private config: PlayerConfig =
75
+ DEFAULT_OPTIONS
63
76
 
64
- private emitter = new EventLite();
77
+ private emitter = new EventLite()
65
78
 
66
- private player: PlayerClappr | null = null;
79
+ private player: PlayerClappr | null =
80
+ null
67
81
 
68
- private ready = false;
82
+ private ready = false
69
83
 
70
- private tuneInTimerId: ReturnType<typeof setTimeout> | null = null;
84
+ private tuneInTimerId: ReturnType<
85
+ typeof setTimeout
86
+ > | null = null
71
87
 
72
- private tunedIn = false;
88
+ private tunedIn = false
73
89
 
74
90
  get activePlayback(): PlaybackModule | null {
75
- if (!this.player?.core.activePlayback) {
76
- return null;
91
+ if (
92
+ !this.player?.core.activePlayback
93
+ ) {
94
+ return null
77
95
  }
78
- switch (this.player.core.activePlayback.name) {
96
+ switch (
97
+ this.player.core.activePlayback
98
+ .name
99
+ ) {
79
100
  case 'dash':
80
- return 'dash';
101
+ return 'dash'
81
102
  case 'hls':
82
- return 'hls';
103
+ return 'hls'
83
104
  default:
84
- return 'native';
105
+ return 'native'
85
106
  }
86
107
  }
87
108
 
88
109
  get bitrate(): BitrateInfo | null {
89
- return this.bitrateInfo;
110
+ return this.bitrateInfo
90
111
  }
91
112
 
92
113
  get hd() {
93
- return this.player?.core.activePlayback?.isHighDefinitionInUse || false;
114
+ return (
115
+ this.player?.core.activePlayback
116
+ ?.isHighDefinitionInUse || false
117
+ )
94
118
  }
95
119
 
96
- get playbackType(): PlaybackType | undefined {
97
- return this.player?.core.activePlayback?.getPlaybackType();
120
+ get playbackType():
121
+ | PlaybackType
122
+ | undefined {
123
+ return this.player?.core.activePlayback?.getPlaybackType()
98
124
  }
99
125
 
100
- constructor(
101
- config: PlayerConfig,
102
- ) {
103
- this.setConfig(config);
126
+ constructor(config: PlayerConfig) {
127
+ this.setConfig(config)
104
128
  }
105
129
 
106
- on<T extends PlayerEvent>(event: T, handler: PlayerEventHandler<T>) {
107
- this.emitter.on(event, handler);
130
+ on<T extends PlayerEvent>(
131
+ event: T,
132
+ handler: PlayerEventHandler<T>,
133
+ ) {
134
+ this.emitter.on(event, handler)
108
135
  }
109
136
 
110
- off<T extends PlayerEvent>(event: T, handler: PlayerEventHandler<T>) {
111
- this.emitter.off(event, handler);
137
+ off<T extends PlayerEvent>(
138
+ event: T,
139
+ handler: PlayerEventHandler<T>,
140
+ ) {
141
+ this.emitter.off(event, handler)
112
142
  }
113
143
 
114
- configure(config: Partial<PlayerConfig>) {
115
- this.setConfig(config);
144
+ configure(
145
+ config: Partial<PlayerConfig>,
146
+ ) {
147
+ this.setConfig(config)
116
148
  }
117
149
 
118
- private setConfig(config: Partial<PlayerConfig>) {
119
- this.config = $.extend(true, this.config, config);
150
+ private setConfig(
151
+ config: Partial<PlayerConfig>,
152
+ ) {
153
+ this.config = $.extend(
154
+ true,
155
+ this.config,
156
+ config,
157
+ )
120
158
  }
121
159
 
122
- async init(playerElement: HTMLElement) {
123
- assert.ok(!this.player, 'Player already initialized');
124
- assert.ok(playerElement, 'Player container element is required');
160
+ async init(
161
+ playerElement: HTMLElement,
162
+ ) {
163
+ assert.ok(
164
+ !this.player,
165
+ 'Player already initialized',
166
+ )
167
+ assert.ok(
168
+ playerElement,
169
+ 'Player container element is required',
170
+ )
125
171
  if (
126
172
  this.config.debug === 'all' ||
127
173
  this.config.debug === 'clappr'
128
174
  ) {
129
- Log.setLevel(0);
175
+ Log.setLevel(0)
130
176
  }
131
177
 
132
- trace(`${T} init`, {config: this.config});
133
-
134
- this.configurePlaybacks();
135
- const coreOpts = this.buildCoreOptions(playerElement);
136
- const {
137
- core,
138
- container,
139
- } = Loader.registeredPlugins;
140
- trace(`${T} init`, { registeredPlaybacks: Loader.registeredPlaybacks.map(p => p.name) });
178
+ trace(`${T} init`, {
179
+ config: this.config,
180
+ })
181
+
182
+ this.configurePlaybacks()
183
+ const coreOpts =
184
+ this.buildCoreOptions(
185
+ playerElement,
186
+ )
187
+ const { core, container } =
188
+ Loader.registeredPlugins
189
+ trace(`${T} init`, {
190
+ registeredPlaybacks:
191
+ Loader.registeredPlaybacks.map(
192
+ (p) => p.name,
193
+ ),
194
+ })
141
195
  coreOpts.plugins = {
142
196
  core: Object.values(core),
143
- container: Object.values(container),
144
- playback: Loader.registeredPlaybacks,
145
- } as CorePluginOptions;
146
- return this.initPlayer(coreOpts);
197
+ container:
198
+ Object.values(container),
199
+ playback:
200
+ Loader.registeredPlaybacks,
201
+ } as CorePluginOptions
202
+ return this.initPlayer(coreOpts)
147
203
  }
148
204
 
149
205
  destroy() {
150
- trace(`${T} destroy`, { player: !!this.player });
206
+ trace(`${T} destroy`, {
207
+ player: !!this.player,
208
+ })
151
209
  if (this.player) {
152
- this.player.destroy();
153
- this.player = null;
210
+ this.player.destroy()
211
+ this.player = null
154
212
  }
155
- this.ready = false;
156
- this.tunedIn = false;
213
+ this.ready = false
214
+ this.tunedIn = false
157
215
  if (this.tuneInTimerId) {
158
- clearTimeout(this.tuneInTimerId);
159
- this.tuneInTimerId = null;
216
+ clearTimeout(this.tuneInTimerId)
217
+ this.tuneInTimerId = null
160
218
  }
161
- this.bitrateInfo = null;
219
+ this.bitrateInfo = null
162
220
  }
163
221
 
164
222
  pause() {
165
- assert.ok(this.player, 'Player not initialized');
166
- this.player.pause();
223
+ assert.ok(
224
+ this.player,
225
+ 'Player not initialized',
226
+ )
227
+ this.player.pause()
167
228
  }
168
229
 
169
230
  play() {
170
- assert.ok(this.player, 'Player not initialized');
171
- this.player.play();
231
+ assert.ok(
232
+ this.player,
233
+ 'Player not initialized',
234
+ )
235
+ this.player.play()
172
236
  }
173
237
 
174
238
  seekTo(time: number) {
175
- assert.ok(this.player, 'Player not initialized');
176
- this.player.seek(time);
239
+ assert.ok(
240
+ this.player,
241
+ 'Player not initialized',
242
+ )
243
+ this.player.seek(time)
177
244
  }
178
245
 
179
246
  stop() {
180
- assert.ok(this.player, 'Player not initialized');
181
- this.player.stop();
247
+ assert.ok(
248
+ this.player,
249
+ 'Player not initialized',
250
+ )
251
+ this.player.stop()
182
252
  }
183
253
 
184
- static registerPlugin(plugin: PlayerPlugin) {
185
- Loader.registerPlugin(plugin);
254
+ static registerPlugin(
255
+ plugin: PlayerPlugin,
256
+ ) {
257
+ Loader.registerPlugin(plugin)
186
258
  }
187
259
 
188
- static unregisterPlugin(plugin: PlayerPlugin) {
189
- Loader.unregisterPlugin(plugin);
260
+ static unregisterPlugin(
261
+ plugin: PlayerPlugin,
262
+ ) {
263
+ Loader.unregisterPlugin(plugin)
190
264
  }
191
265
 
192
- private initPlayer(coreOptions: CoreOptions) {
193
- trace(`${T} initPlayer`, {coreOptions});
266
+ private initPlayer(
267
+ coreOptions: CoreOptions,
268
+ ) {
269
+ trace(`${T} initPlayer`, {
270
+ coreOptions,
271
+ })
194
272
 
195
- assert.ok(!this.player, 'Player already initialized');
273
+ assert.ok(
274
+ !this.player,
275
+ 'Player already initialized',
276
+ )
196
277
 
197
278
  const player = new PlayerClappr(
198
- coreOptions
199
- );
200
- this.player = player;
279
+ coreOptions,
280
+ )
281
+ this.player = player
201
282
 
202
283
  // TODO checks if the whole thing is necessary
203
- this.tuneInTimerId = globalThis.setTimeout(() => {
204
- trace(`${T} tuneInTimer`, { ready: this.ready, tunedIn: this.tunedIn });
205
- this.tuneInTimerId = null;
206
- this.tuneIn();
207
- }, 4000);
284
+ this.tuneInTimerId =
285
+ globalThis.setTimeout(() => {
286
+ trace(`${T} tuneInTimer`, {
287
+ ready: this.ready,
288
+ tunedIn: this.tunedIn,
289
+ })
290
+ this.tuneInTimerId = null
291
+ this.tuneIn()
292
+ }, 4000)
208
293
  }
209
294
 
210
295
  private async tuneIn() {
211
- assert.ok(this.player);
296
+ assert.ok(this.player)
212
297
  trace(`${T} tuneIn`, {
213
298
  ready: this.ready,
214
299
  tunedIn: this.tunedIn,
215
- });
300
+ })
216
301
  if (this.tunedIn) {
217
- return;
302
+ return
218
303
  }
219
- this.tunedIn = true;
220
- const player = this.player;
304
+ this.tunedIn = true
305
+ const player = this.player
221
306
  try {
222
- this.emitter.emit(PlayerEvent.Ready);
307
+ this.emitter.emit(
308
+ PlayerEvent.Ready,
309
+ )
223
310
  } catch (e) {
224
- reportError(e);
311
+ reportError(e)
225
312
  }
226
313
  if (player.core.activeContainer) {
227
- this.bindBitrateChangeHandler();
314
+ this.bindBitrateChangeHandler()
228
315
  }
229
- player.core.on(ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED, () => {
230
- this.bindBitrateChangeHandler();
231
- }, null);
316
+ player.core.on(
317
+ ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED,
318
+ () => {
319
+ this.bindBitrateChangeHandler()
320
+ },
321
+ null,
322
+ )
232
323
  if (
233
324
  Browser.isiOS &&
234
325
  player.core.activePlayback
@@ -237,18 +328,44 @@ export class Player {
237
328
  'webkitendfullscreen',
238
329
  () => {
239
330
  try {
240
- player.core.handleFullscreenChange();
331
+ player.core.handleFullscreenChange()
241
332
  } catch (e) {
242
- reportError(e);
333
+ reportError(e)
243
334
  }
244
335
  },
245
- );
336
+ )
246
337
  }
338
+ player.core.on(
339
+ ClapprEvents.CORE_SCREEN_ORIENTATION_CHANGED,
340
+ ({
341
+ orientation,
342
+ }: {
343
+ orientation:
344
+ | 'landscape'
345
+ | 'portrait'
346
+ }) => {
347
+ trace(
348
+ `${T} CORE_SCREEN_ORIENTATION_CHANGED`,
349
+ { orientation },
350
+ )
351
+ },
352
+ null,
353
+ )
247
354
  if (this.config.autoPlay) {
248
355
  setTimeout(() => {
249
- trace(`${T} autoPlay`, { player: !!this.player, container: !!this.player?.core.activeContainer, playback: this.player?.core.activePlayback.name });
250
- assert(this.player);
251
- this.player.play({ autoPlay: true });
356
+ trace(`${T} autoPlay`, {
357
+ player: !!this.player,
358
+ container:
359
+ !!this.player?.core
360
+ .activeContainer,
361
+ playback:
362
+ this.player?.core
363
+ .activePlayback.name,
364
+ })
365
+ assert(this.player)
366
+ this.player.play({
367
+ autoPlay: true,
368
+ })
252
369
  }, 0)
253
370
  }
254
371
  }
@@ -257,133 +374,221 @@ export class Player {
257
374
  onReady: () => {
258
375
  trace(`${T} onReady`, {
259
376
  ready: this.ready,
260
- });
377
+ })
261
378
  if (this.ready) {
262
- return;
379
+ return
263
380
  }
264
- this.ready = true;
381
+ this.ready = true
265
382
  if (this.tuneInTimerId) {
266
- clearTimeout(this.tuneInTimerId);
267
- this.tuneInTimerId = null;
383
+ clearTimeout(this.tuneInTimerId)
384
+ this.tuneInTimerId = null
268
385
  }
269
- setTimeout(() => this.tuneIn(), 0);
386
+ setTimeout(() => this.tuneIn(), 0)
387
+ },
388
+ onResize: (newSize: {
389
+ width: number
390
+ height: number
391
+ }) => {
392
+ trace(`${T} CORE_RESIZE`, {
393
+ newSize,
394
+ })
270
395
  },
271
396
  onPlay: () => {
272
397
  try {
273
- this.emitter.emit(PlayerEvent.Play);
398
+ this.emitter.emit(
399
+ PlayerEvent.Play,
400
+ )
274
401
  } catch (e) {
275
- reportError(e);
402
+ reportError(e)
276
403
  }
277
404
  },
278
405
  onPause: () => {
279
406
  try {
280
- this.emitter.emit(PlayerEvent.Pause);
407
+ this.emitter.emit(
408
+ PlayerEvent.Pause,
409
+ )
281
410
  } catch (e) {
282
- reportError(e);
411
+ reportError(e)
283
412
  }
284
413
  },
285
414
  onEnded: () => {
286
415
  try {
287
- this.emitter.emit(PlayerEvent.Ended);
416
+ this.emitter.emit(
417
+ PlayerEvent.Ended,
418
+ )
288
419
  } catch (e) {
289
- reportError(e);
420
+ reportError(e)
290
421
  }
291
422
  },
292
423
  onStop: () => {
293
424
  try {
294
- this.emitter.emit(PlayerEvent.Stop);
425
+ this.emitter.emit(
426
+ PlayerEvent.Stop,
427
+ )
295
428
  } catch (e) {
296
- reportError(e);
429
+ reportError(e)
297
430
  }
298
431
  },
299
- };
300
-
301
- private buildCoreOptions(playerElement: HTMLElement): CoreOptions {
302
- const multisources = this.config.multisources;
303
- const mainSource = this.config.playbackType === 'live' ? multisources.find(ms => ms.live !== false) : multisources[0];
304
- const mediaSources = mainSource ? this.buildMediaSourcesList(mainSource) : [];
305
- const mainSourceUrl = mediaSources[0];
306
- const poster = mainSource?.poster ?? this.config.poster;
432
+ }
307
433
 
308
- const coreOptions: CoreOptions & PluginOptions = {
434
+ private buildCoreOptions(
435
+ playerElement: HTMLElement,
436
+ ): CoreOptions {
437
+ const multisources =
438
+ this.config.multisources
439
+ const mainSource =
440
+ this.config.playbackType ===
441
+ 'live'
442
+ ? multisources.find(
443
+ (ms) => ms.live !== false,
444
+ )
445
+ : multisources[0]
446
+ const mediaSources = mainSource
447
+ ? this.buildMediaSourcesList(
448
+ mainSource,
449
+ )
450
+ : []
451
+ // const mainSourceUrl = mediaSources[0];
452
+ const poster =
453
+ mainSource?.poster ??
454
+ this.config.poster
455
+
456
+ const coreOptions: CoreOptions &
457
+ PluginOptions = {
309
458
  ...this.config.pluginSettings,
310
459
  allowUserInteraction: true,
311
460
  autoPlay: false,
312
- debug: this.config.debug || 'none',
461
+ debug:
462
+ this.config.debug || 'none',
313
463
  events: this.events,
314
- height: playerElement.clientHeight,
464
+ height:
465
+ playerElement.clientHeight,
315
466
  loop: this.config.loop,
316
467
  multisources,
317
468
  mute: this.config.mute,
318
469
  playback: {
319
470
  controls: false,
320
471
  playInline: true,
321
- preload: Browser.isiOS ? 'metadata' : 'none',
472
+ preload: Browser.isiOS
473
+ ? 'metadata'
474
+ : 'none',
322
475
  mute: this.config.mute,
323
476
  crossOrigin: 'anonymous', // TODO
324
477
  hlsjsConfig: {
325
- debug: this.config.debug === 'all' || this.config.debug === 'hls',
478
+ debug:
479
+ this.config.debug ===
480
+ 'all' ||
481
+ this.config.debug === 'hls',
326
482
  },
327
483
  },
328
484
  parent: playerElement,
329
- playbackType: this.config.playbackType,
485
+ playbackType:
486
+ this.config.playbackType,
330
487
  poster,
331
488
  width: playerElement.clientWidth,
332
489
  // source: mainSourceUrl,
333
490
  sources: mediaSources,
334
491
  strings: this.config.strings,
335
- };
336
- return coreOptions;
492
+ }
493
+ return coreOptions
337
494
  }
338
495
 
339
496
  private configurePlaybacks() {
340
- Loader.registerPlayback(DashPlayback);
341
- Loader.registerPlayback(HlsPlayback);
497
+ Loader.registerPlayback(
498
+ DashPlayback,
499
+ )
500
+ Loader.registerPlayback(HlsPlayback)
342
501
  }
343
502
 
344
503
  private bindBitrateChangeHandler() {
345
- this.player?.core.activeContainer.on(ClapprEvents.CONTAINER_BITRATE, (bitrate: BitrateInfo) => {
346
- this.bitrateInfo = bitrate;
347
- });
504
+ this.player?.core.activeContainer.on(
505
+ ClapprEvents.CONTAINER_BITRATE,
506
+ (bitrate: BitrateInfo) => {
507
+ this.bitrateInfo = bitrate
508
+ },
509
+ )
348
510
  }
349
511
 
350
- private buildMediaSourcesList(ms: StreamMediaSource): string[] {
351
- const msl: string[] = [];
352
- const sources: Record<'dash' | 'master' | 'hls' | 'mpegts', string | null> = {
512
+ private buildMediaSourcesList(
513
+ ms: StreamMediaSource,
514
+ ): string[] {
515
+ const msl: string[] = []
516
+ const sources: Record<
517
+ | 'dash'
518
+ | 'master'
519
+ | 'hls'
520
+ | 'mpegts',
521
+ string | null
522
+ > = {
353
523
  dash: ms.sourceDash,
354
524
  master: ms.source,
355
525
  hls: ms.hlsCmafUrl,
356
526
  mpegts: ms.hlsMpegtsUrl,
357
527
  }
358
- switch (this.config.priorityTransport) {
528
+ switch (
529
+ this.config.priorityTransport
530
+ ) {
359
531
  case 'dash':
360
- if (sources.dash) {
361
- msl.push(sources.dash);
362
- sources.dash = null;
363
- }
364
- break;
532
+ addDash()
533
+ break
365
534
  case 'hls':
366
- if (sources.hls) {
367
- msl.push(sources.hls);
368
- sources.hls = null;
369
- }
370
- if (sources.master?.endsWith('.m3u8')) {
371
- msl.push(sources.master);
372
- sources.master = null;
373
- }
374
- break;
535
+ addHls()
536
+ break
375
537
  case 'mpegts':
376
- if (sources.mpegts) {
377
- msl.push(sources.mpegts);
378
- sources.mpegts = null
538
+ addMpegts()
539
+ break
540
+ case 'auto':
541
+ addDash()
542
+ addHls()
543
+ break
544
+ }
545
+ Object.values(sources).forEach(
546
+ (s) => {
547
+ if (s) {
548
+ msl.push(s)
379
549
  }
380
- break;
550
+ },
551
+ )
552
+ return msl
553
+
554
+ function addMpegts() {
555
+ if (sources.mpegts) {
556
+ msl.push(sources.mpegts)
557
+ sources.mpegts = null
558
+ }
559
+ }
560
+
561
+ function addHls() {
562
+ if (
563
+ sources.hls &&
564
+ HlsPlayback.canPlay(sources.hls)
565
+ ) {
566
+ msl.push(sources.hls)
567
+ sources.hls = null
568
+ }
569
+ if (
570
+ sources.master?.endsWith(
571
+ '.m3u8',
572
+ ) &&
573
+ HlsPlayback.canPlay(
574
+ sources.master,
575
+ )
576
+ ) {
577
+ msl.push(sources.master)
578
+ sources.master = null
579
+ }
381
580
  }
382
- Object.values(sources).forEach(s => {
383
- if (s) {
384
- msl.push(s);
581
+
582
+ function addDash() {
583
+ if (
584
+ sources.dash &&
585
+ DashPlayback.canPlay(
586
+ sources.dash,
587
+ )
588
+ ) {
589
+ msl.push(sources.dash)
590
+ sources.dash = null
385
591
  }
386
- });
387
- return msl;
592
+ }
388
593
  }
389
594
  }