@gcorevideo/player 2.22.16 → 2.22.17

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 (74) hide show
  1. package/dist/core.js +6 -8
  2. package/dist/index.css +1338 -1338
  3. package/dist/index.js +361 -439
  4. package/dist/player.d.ts +216 -159
  5. package/dist/plugins/index.css +1463 -1463
  6. package/dist/plugins/index.js +354 -427
  7. package/docs/api/player.clapprstats.exportmetrics.md +1 -1
  8. package/docs/api/player.clapprstats.md +5 -15
  9. package/docs/api/player.clapprstatssettings.md +13 -0
  10. package/docs/api/player.clips.destroy.md +18 -0
  11. package/docs/api/player.clips.disable.md +18 -0
  12. package/docs/api/player.clips.enable.md +18 -0
  13. package/docs/api/player.clips.md +170 -0
  14. package/docs/api/player.clips.render.md +18 -0
  15. package/docs/api/player.clips.supportedversion.md +16 -0
  16. package/docs/api/player.clips.version.md +14 -0
  17. package/docs/api/player.clipspluginsettings.md +2 -2
  18. package/docs/api/player.clipspluginsettings.text.md +1 -1
  19. package/docs/api/player.md +27 -18
  20. package/docs/api/player.mediacontrol.md +1 -1
  21. package/docs/api/{player.mediacontrol.getelement.md → player.mediacontrol.mount.md} +20 -7
  22. package/docs/api/player.mediacontrolleftelement.md +1 -1
  23. package/docs/api/{player.clapprnerdstats._constructor_.md → player.nerdstats._constructor_.md} +3 -3
  24. package/docs/api/{player.clapprnerdstats.md → player.nerdstats.md} +5 -5
  25. package/docs/api/player.qualitylevel.height.md +1 -1
  26. package/docs/api/player.qualitylevel.level.md +1 -1
  27. package/docs/api/player.qualitylevel.md +4 -4
  28. package/docs/api/player.qualitylevel.width.md +1 -1
  29. package/docs/api/player.timeposition.current.md +1 -1
  30. package/docs/api/player.timeposition.md +2 -2
  31. package/docs/api/player.timeposition.total.md +1 -1
  32. package/docs/api/player.timeprogress.md +6 -4
  33. package/docs/api/player.timevalue.md +1 -1
  34. package/lib/index.plugins.d.ts +2 -1
  35. package/lib/index.plugins.d.ts.map +1 -1
  36. package/lib/index.plugins.js +2 -1
  37. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  38. package/lib/playback/dash-playback/DashPlayback.js +5 -7
  39. package/lib/playback.types.d.ts +22 -9
  40. package/lib/playback.types.d.ts.map +1 -1
  41. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +4 -0
  42. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
  43. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +20 -23
  44. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +83 -0
  45. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts.map +1 -0
  46. package/lib/plugins/clappr-nerd-stats/NerdStats.js +339 -0
  47. package/lib/plugins/clappr-stats/ClapprStats.d.ts +27 -32
  48. package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
  49. package/lib/plugins/clappr-stats/ClapprStats.js +94 -202
  50. package/lib/plugins/clappr-stats/types.d.ts +65 -24
  51. package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
  52. package/lib/plugins/clappr-stats/types.js +37 -2
  53. package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
  54. package/lib/plugins/clappr-stats/utils.js +1 -2
  55. package/lib/testUtils.d.ts +2 -1
  56. package/lib/testUtils.d.ts.map +1 -1
  57. package/lib/testUtils.js +3 -2
  58. package/package.json +1 -1
  59. package/src/index.plugins.ts +2 -1
  60. package/src/playback/dash-playback/DashPlayback.ts +5 -8
  61. package/src/playback.types.ts +23 -8
  62. package/src/plugins/clappr-nerd-stats/{ClapprNerdStats.ts → NerdStats.ts} +25 -30
  63. package/src/plugins/clappr-stats/ClapprStats.ts +242 -306
  64. package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +133 -0
  65. package/src/plugins/clappr-stats/types.ts +72 -25
  66. package/src/plugins/clappr-stats/utils.ts +1 -2
  67. package/src/plugins/error-screen/__tests__/ErrorScreen.test.ts +3 -4
  68. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -0
  69. package/src/testUtils.ts +3 -2
  70. package/temp/player.api.json +311 -159
  71. package/tsconfig.tsbuildinfo +1 -1
  72. package/docs/api/player.clapprstats.setupdatemetrics.md +0 -56
  73. package/docs/api/player.clipsplugin.gettext.md +0 -58
  74. package/docs/api/player.clipsplugin.md +0 -59
@@ -10310,6 +10310,339 @@ class BottomGear extends UICorePlugin {
10310
10310
  }
10311
10311
  }
10312
10312
 
10313
+ /**
10314
+ * @beta
10315
+ */
10316
+ var Chronograph;
10317
+ (function (Chronograph) {
10318
+ Chronograph["Startup"] = "startup";
10319
+ Chronograph["Watch"] = "watch";
10320
+ Chronograph["Pause"] = "pause";
10321
+ Chronograph["Buffering"] = "buffering";
10322
+ Chronograph["Session"] = "session";
10323
+ // Latency = 'latency',
10324
+ })(Chronograph || (Chronograph = {}));
10325
+ /**
10326
+ * @beta
10327
+ */
10328
+ var Counter;
10329
+ (function (Counter) {
10330
+ Counter["Play"] = "play";
10331
+ Counter["Pause"] = "pause";
10332
+ Counter["Error"] = "error";
10333
+ Counter["Buffering"] = "buffering";
10334
+ Counter["DecodedFrames"] = "decodedFrames";
10335
+ Counter["DroppedFrames"] = "droppedFrames";
10336
+ Counter["Fps"] = "fps";
10337
+ Counter["ChangeLevel"] = "changeLevel";
10338
+ Counter["Seek"] = "seek";
10339
+ Counter["Fullscreen"] = "fullscreen";
10340
+ Counter["DvrUsage"] = "dvrUsage";
10341
+ })(Counter || (Counter = {}));
10342
+ /**
10343
+ * @beta
10344
+ */
10345
+ var ClapprStatsEvents;
10346
+ (function (ClapprStatsEvents) {
10347
+ /**
10348
+ * Emitted periodically with current measurements.
10349
+ */
10350
+ ClapprStatsEvents["REPORT"] = "clappr:stats:report";
10351
+ /**
10352
+ * Emitted when the playback reaches a certain percentage of the total duration.
10353
+ */
10354
+ // PERCENTAGE = 'clappr:stats:percentage',
10355
+ })(ClapprStatsEvents || (ClapprStatsEvents = {}));
10356
+
10357
+ function newMetrics$1() {
10358
+ return {
10359
+ counters: {
10360
+ play: 0,
10361
+ pause: 0,
10362
+ error: 0,
10363
+ buffering: 0,
10364
+ decodedFrames: 0,
10365
+ droppedFrames: 0,
10366
+ fps: 0,
10367
+ changeLevel: 0,
10368
+ seek: 0,
10369
+ fullscreen: 0,
10370
+ dvrUsage: 0,
10371
+ },
10372
+ chrono: {
10373
+ startup: 0,
10374
+ watch: 0,
10375
+ pause: 0,
10376
+ buffering: 0,
10377
+ session: 0,
10378
+ },
10379
+ extra: {
10380
+ playbackName: '',
10381
+ playbackType: '',
10382
+ bitratesHistory: [],
10383
+ bitrateWeightedMean: 0,
10384
+ bitrateMostUsed: 0,
10385
+ buffersize: 0,
10386
+ watchHistory: [],
10387
+ watchedPercentage: 0,
10388
+ bufferingPercentage: 0,
10389
+ bandwidth: 0,
10390
+ duration: 0,
10391
+ currentTime: 0,
10392
+ },
10393
+ custom: {},
10394
+ };
10395
+ }
10396
+
10397
+ /**
10398
+ * `PLUGIN` that measures data about playback, which can be useful for analyzing performance and UX.
10399
+ * @beta
10400
+ * @remarks
10401
+ * This plugin does not render anything and is supposed to be extended or used together with other plugins that actually render something.
10402
+ *
10403
+ * Configuration options - {@link ClapprStatsSettings}
10404
+ *
10405
+ * Events - {@link ClapprStatsEvents}
10406
+ */
10407
+ class ClapprStats extends ContainerPlugin {
10408
+ timerId = null;
10409
+ lastDecodedFramesCount = 0;
10410
+ metrics = newMetrics$1();
10411
+ timers = {
10412
+ [Chronograph.Startup]: 0,
10413
+ [Chronograph.Watch]: 0,
10414
+ [Chronograph.Pause]: 0,
10415
+ [Chronograph.Buffering]: 0,
10416
+ [Chronograph.Session]: 0,
10417
+ };
10418
+ runEach;
10419
+ /**
10420
+ * @internal
10421
+ */
10422
+ get name() {
10423
+ return 'clappr_stats';
10424
+ }
10425
+ /**
10426
+ * @internal
10427
+ */
10428
+ get supportedVersion() {
10429
+ return { min: CLAPPR_VERSION$1 };
10430
+ }
10431
+ get playbackName() {
10432
+ return String(this.container.playback.name || '');
10433
+ }
10434
+ get playbackType() {
10435
+ return this.container.getPlaybackType();
10436
+ }
10437
+ now() {
10438
+ const hasPerformanceSupport = window.performance && typeof window.performance.now === 'function';
10439
+ return hasPerformanceSupport
10440
+ ? window.performance.now()
10441
+ : new Date().getTime();
10442
+ }
10443
+ inc(counter) {
10444
+ this.metrics.counters[counter] += 1;
10445
+ }
10446
+ // _timerHasStarted(timer) {
10447
+ // return this[`_start${timer}`] !== undefined;
10448
+ // }
10449
+ start(timer) {
10450
+ // this[`_start${timer}`] = this._now();
10451
+ this.timers[timer] = this.now();
10452
+ }
10453
+ stop(timer) {
10454
+ // this._metrics.timers[timer] += this._now() - this[`_start${timer}`];
10455
+ this.metrics.chrono[timer] += this.now() - this.timers[timer];
10456
+ }
10457
+ constructor(container) {
10458
+ super(container);
10459
+ this.runEach = container.options.clapprStats?.runEach ?? 5000;
10460
+ }
10461
+ /**
10462
+ * @internal
10463
+ */
10464
+ bindEvents() {
10465
+ this.listenTo(this.container, Events.CONTAINER_BITRATE, this.onBitrate);
10466
+ this.listenTo(this.container, Events.CONTAINER_STOP, this.stopReporting);
10467
+ this.listenTo(this.container, Events.CONTAINER_ENDED, this.stopReporting);
10468
+ this.listenToOnce(this.container.playback, Events.PLAYBACK_PLAY_INTENT, this.startTimers);
10469
+ this.listenToOnce(this.container, Events.CONTAINER_PLAY, this.onFirstPlaying);
10470
+ this.listenTo(this.container, Events.CONTAINER_PLAY, this.onPlay);
10471
+ this.listenTo(this.container, Events.CONTAINER_PAUSE, this.onPause);
10472
+ this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERING, this.onBuffering);
10473
+ this.listenTo(this.container, Events.CONTAINER_SEEK, this.onSeek);
10474
+ this.listenTo(this.container, Events.CONTAINER_ERROR, () => this.inc(Counter.Error));
10475
+ this.listenTo(this.container, Events.CONTAINER_FULLSCREEN, () => this.inc(Counter.Fullscreen));
10476
+ this.listenTo(this.container, Events.CONTAINER_PLAYBACKDVRSTATECHANGED, (dvrInUse) => {
10477
+ dvrInUse && this.inc(Counter.DvrUsage);
10478
+ });
10479
+ this.listenTo(this.container.playback, Events.PLAYBACK_PROGRESS, this.onProgress);
10480
+ this.listenTo(this.container.playback, Events.PLAYBACK_TIMEUPDATE, this.onTimeUpdate);
10481
+ }
10482
+ /**
10483
+ * @internal
10484
+ */
10485
+ destroy() {
10486
+ this.stopReporting();
10487
+ super.destroy();
10488
+ }
10489
+ /**
10490
+ * Returns the collected metrics.
10491
+ * @returns Measurements collected so far
10492
+ */
10493
+ exportMetrics() {
10494
+ return structuredClone(this.metrics);
10495
+ }
10496
+ onBitrate(newBitrate) {
10497
+ const bitrate = newBitrate.bitrate;
10498
+ const now = this.now();
10499
+ if (this.metrics.extra.bitratesHistory.length > 0) {
10500
+ const last = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
10501
+ last.end = now;
10502
+ last.time = now - last.start;
10503
+ }
10504
+ this.metrics.extra.bitratesHistory.push({ start: this.now(), bitrate });
10505
+ this.inc(Counter.ChangeLevel);
10506
+ }
10507
+ stopReporting() {
10508
+ this.buildReport();
10509
+ if (this.timerId !== null) {
10510
+ clearInterval(this.timerId);
10511
+ this.timerId = null;
10512
+ }
10513
+ }
10514
+ startTimers() {
10515
+ this.timerId = setInterval(this.buildReport.bind(this), this.runEach);
10516
+ this.start(Chronograph.Session);
10517
+ this.start(Chronograph.Startup);
10518
+ }
10519
+ onFirstPlaying() {
10520
+ this.listenTo(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
10521
+ this.start(Chronograph.Watch);
10522
+ this.stop(Chronograph.Startup);
10523
+ }
10524
+ playAfterPause() {
10525
+ this.listenTo(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
10526
+ this.stop(Chronograph.Pause);
10527
+ this.start(Chronograph.Watch);
10528
+ }
10529
+ onPlay() {
10530
+ this.inc(Counter.Play);
10531
+ }
10532
+ onPause() {
10533
+ this.stop(Chronograph.Watch);
10534
+ this.start(Chronograph.Pause);
10535
+ this.inc(Counter.Pause);
10536
+ this.listenToOnce(this.container, Events.CONTAINER_PLAY, this.playAfterPause);
10537
+ this.stopListening(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
10538
+ }
10539
+ onSeek(e) {
10540
+ this.inc(Counter.Seek);
10541
+ this.metrics.extra.watchHistory.push([e * 1000, e * 1000]);
10542
+ }
10543
+ onTimeUpdate(e) {
10544
+ const current = e.current * 1000, total = e.total * 1000, l = this.metrics.extra.watchHistory.length;
10545
+ this.metrics.extra.duration = total;
10546
+ this.metrics.extra.currentTime = current;
10547
+ // TODO what if it's a live stream?
10548
+ this.metrics.extra.watchedPercentage = (current / total) * 100;
10549
+ if (l === 0) {
10550
+ this.metrics.extra.watchHistory.push([current, current]);
10551
+ }
10552
+ else {
10553
+ this.metrics.extra.watchHistory[l - 1][1] = current;
10554
+ }
10555
+ if (this.metrics.extra.bitratesHistory.length > 0) {
10556
+ const lastBitrate = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
10557
+ if (!lastBitrate.end) {
10558
+ lastBitrate.time = this.now() - lastBitrate.start;
10559
+ }
10560
+ }
10561
+ this.onCompletion();
10562
+ }
10563
+ onContainerUpdateWhilePlaying() {
10564
+ if (this.container.playback.isPlaying()) {
10565
+ this.stop(Chronograph.Watch);
10566
+ this.start(Chronograph.Watch);
10567
+ }
10568
+ }
10569
+ onBuffering() {
10570
+ this.inc(Counter.Buffering);
10571
+ this.start(Chronograph.Buffering);
10572
+ this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERFULL, this.onBufferfull);
10573
+ }
10574
+ onBufferfull() {
10575
+ this.stop(Chronograph.Buffering);
10576
+ this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERING, this.onBuffering);
10577
+ }
10578
+ onProgress(progress) {
10579
+ this.metrics.extra.buffersize = progress.current * 1000;
10580
+ }
10581
+ onCompletion() {
10582
+ // Decide if this is needed
10583
+ // const currentPercentage = this.metrics.extra.watchedPercentage;
10584
+ // this.trigger(ClapprStatsEvents.PERCENTAGE, currentPercentage);
10585
+ }
10586
+ buildReport() {
10587
+ this.stop(Chronograph.Session);
10588
+ this.start(Chronograph.Session);
10589
+ this.metrics.extra.playbackName = this.playbackName;
10590
+ this.metrics.extra.playbackType = this.playbackType;
10591
+ this.calcBitrates();
10592
+ this.calcBufferingPercentage();
10593
+ // TODO calc FPS properly, e.g., on TIMEUPDATE event
10594
+ this.fetchFPS();
10595
+ this.trigger(ClapprStatsEvents.REPORT, structuredClone(this.metrics));
10596
+ }
10597
+ fetchFPS() {
10598
+ // TODO check if the playback and media sources support video, then use the common method
10599
+ // flashls ??? - hls.droppedFramesl hls.stream.bufferLength (seconds)
10600
+ // hls ??? (use the same?)
10601
+ const fetchFPS = {
10602
+ html5_video: this.html5FetchFPS,
10603
+ hls: this.html5FetchFPS,
10604
+ dash: this.html5FetchFPS,
10605
+ };
10606
+ if (this.playbackName in fetchFPS) {
10607
+ fetchFPS[this.playbackName].call(this);
10608
+ }
10609
+ }
10610
+ // TODO sort out
10611
+ calcBitrates() {
10612
+ const { bitratesHistory } = this.metrics.extra;
10613
+ if (bitratesHistory.length === 0) {
10614
+ return;
10615
+ }
10616
+ let totalTime = 0;
10617
+ let weightedTotal = 0;
10618
+ for (const { bitrate, time = 0 } of bitratesHistory) {
10619
+ totalTime += time;
10620
+ weightedTotal += bitrate * time;
10621
+ }
10622
+ this.metrics.extra.bitrateWeightedMean = weightedTotal / totalTime;
10623
+ this.metrics.extra.bitrateMostUsed = bitratesHistory.reduce((mostUsed, current) => (current.time || 0) > (mostUsed.time || 0) ? current : mostUsed, { time: 0, bitrate: 0, start: 0, end: 0 }).bitrate;
10624
+ }
10625
+ calcBufferingPercentage() {
10626
+ if (this.metrics.extra.duration > 0) {
10627
+ this.metrics.extra.bufferingPercentage =
10628
+ (this.metrics.chrono.buffering / this.metrics.extra.duration) * 100;
10629
+ }
10630
+ }
10631
+ html5FetchFPS() {
10632
+ const videoTag = this.container.playback.el;
10633
+ const getFirstValidValue = (...args) => args.find((val) => val !== undefined);
10634
+ const decodedFrames = getFirstValidValue(videoTag.webkitDecodedFrameCount, videoTag.mozDecodedFrames, 0);
10635
+ const droppedFrames = getFirstValidValue(videoTag.webkitDroppedFrameCount, videoTag.mozParsedFrames && videoTag.mozDecodedFrames
10636
+ ? videoTag.mozParsedFrames - videoTag.mozDecodedFrames
10637
+ : 0, 0);
10638
+ const delta = decodedFrames - (this.lastDecodedFramesCount || 0);
10639
+ this.metrics.counters.decodedFrames = decodedFrames;
10640
+ this.metrics.counters.droppedFrames = droppedFrames;
10641
+ this.metrics.counters.fps = delta / (this.runEach / 1000); // TODO use time delta instead of runEach
10642
+ this.lastDecodedFramesCount = decodedFrames;
10643
+ }
10644
+ }
10645
+
10313
10646
  function getDefaultExportFromCjs (x) {
10314
10647
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
10315
10648
  }
@@ -11381,56 +11714,6 @@ function requireMousetrap () {
11381
11714
  var mousetrapExports = requireMousetrap();
11382
11715
  const Mousetrap = /*@__PURE__*/getDefaultExportFromCjs(mousetrapExports);
11383
11716
 
11384
- /**
11385
- * @beta
11386
- */
11387
- var ClapprStatsEvents;
11388
- (function (ClapprStatsEvents) {
11389
- ClapprStatsEvents["REPORT_EVENT"] = "clappr:stats:report";
11390
- ClapprStatsEvents["PERCENTAGE_EVENT"] = "clappr:stats:percentage";
11391
- })(ClapprStatsEvents || (ClapprStatsEvents = {}));
11392
-
11393
- function newMetrics$1() {
11394
- return {
11395
- counters: {
11396
- play: 0,
11397
- pause: 0,
11398
- error: 0,
11399
- buffering: 0,
11400
- decodedFrames: 0,
11401
- droppedFrames: 0,
11402
- fps: 0,
11403
- changeLevel: 0,
11404
- seek: 0,
11405
- fullscreen: 0,
11406
- dvrUsage: 0,
11407
- },
11408
- timers: {
11409
- startup: 0,
11410
- watch: 0,
11411
- pause: 0,
11412
- buffering: 0,
11413
- session: 0,
11414
- latency: 0,
11415
- },
11416
- extra: {
11417
- playbackName: '',
11418
- playbackType: '',
11419
- bitratesHistory: [],
11420
- bitrateWeightedMean: 0,
11421
- bitrateMostUsed: 0,
11422
- buffersize: 0,
11423
- watchHistory: [],
11424
- watchedPercentage: 0,
11425
- bufferingPercentage: 0,
11426
- bandwidth: 0,
11427
- duration: 0,
11428
- currentTime: 0,
11429
- },
11430
- custom: {},
11431
- };
11432
- }
11433
-
11434
11717
  var humanFormat$2 = {exports: {}};
11435
11718
 
11436
11719
  var humanFormat$1 = humanFormat$2.exports;
@@ -12461,7 +12744,7 @@ const drawSummary = (customMetrics, vodContainer, liveContainer) => {
12461
12744
  * When clicked, it shows an overlay window with the information about the network speed, latency, etc,
12462
12745
  * and recommended quality level.
12463
12746
  */
12464
- class ClapprNerdStats extends UICorePlugin {
12747
+ class NerdStats extends UICorePlugin {
12465
12748
  container = null;
12466
12749
  customMetrics = {
12467
12750
  connectionSpeed: 0,
@@ -12544,29 +12827,29 @@ class ClapprNerdStats extends UICorePlugin {
12544
12827
  this.listenTo(bottomGear, GearEvents.RENDERED, this.addToBottomGear);
12545
12828
  this.container = this.core.activeContainer;
12546
12829
  const clapprStats = this.container?.getPlugin('clappr_stats');
12547
- if (!clapprStats) {
12548
- console.error('clappr-stats not available. Please, include it as a plugin of your Clappr instance.\n' +
12549
- 'For more info, visit: https://github.com/clappr/clappr-stats.');
12550
- this.disable();
12551
- }
12552
- else {
12553
- Mousetrap.bind(this.shortcut, () => this.toggle());
12554
- this.listenTo(this.core, Events.CORE_RESIZE, this.onPlayerResize);
12555
- // TODO: fix
12556
- this.listenTo(clapprStats, ClapprStatsEvents.REPORT_EVENT, this.updateMetrics);
12557
- clapprStats.setUpdateMetrics(this.updateMetrics.bind(this));
12558
- this.updateMetrics(clapprStats.exportMetrics());
12559
- this.render();
12560
- }
12830
+ assert(clapprStats, 'clappr-stats not available. Please, include it as a plugin of your Clappr instance.\n' +
12831
+ 'For more info, visit: https://github.com/clappr/clappr-stats.');
12832
+ Mousetrap.bind(this.shortcut, this.toggle);
12833
+ this.listenTo(this.core, Events.CORE_RESIZE, this.onPlayerResize);
12834
+ this.listenTo(clapprStats, ClapprStatsEvents.REPORT, this.updateMetrics);
12835
+ this.updateMetrics(clapprStats.exportMetrics());
12836
+ this.render();
12837
+ }
12838
+ /**
12839
+ * @internal
12840
+ */
12841
+ destroy() {
12842
+ Mousetrap.unbind(this.shortcut);
12843
+ return super.destroy();
12561
12844
  }
12562
- toggle() {
12845
+ toggle = () => {
12563
12846
  if (this.showing) {
12564
12847
  this.hide();
12565
12848
  }
12566
12849
  else {
12567
12850
  this.show();
12568
12851
  }
12569
- }
12852
+ };
12570
12853
  show() {
12571
12854
  this.core.$el.find(this.statsBoxElem).show();
12572
12855
  this.showing = true;
@@ -12623,7 +12906,7 @@ class ClapprNerdStats extends UICorePlugin {
12623
12906
  this.addGeneralMetrics();
12624
12907
  this.addCustomMetrics();
12625
12908
  const scrollTop = this.core.$el.find(this.statsBoxElem).scrollTop();
12626
- this.$el.html(ClapprNerdStats.template({
12909
+ this.$el.html(NerdStats.template({
12627
12910
  metrics: Formatter.format(this.metrics),
12628
12911
  iconPosition: this.iconPosition,
12629
12912
  }));
@@ -12657,8 +12940,8 @@ class ClapprNerdStats extends UICorePlugin {
12657
12940
  addToBottomGear() {
12658
12941
  const gear = this.core.getPlugin('bottom_gear');
12659
12942
  gear
12660
- .addItem('nerd')
12661
- .html(ClapprNerdStats.buttonTemplate({
12943
+ .addItem('nerd_stats')
12944
+ .html(NerdStats.buttonTemplate({
12662
12945
  icon: statsIcon,
12663
12946
  i18n: this.core.i18n,
12664
12947
  }))
@@ -12700,362 +12983,6 @@ function newMetrics() {
12700
12983
  };
12701
12984
  }
12702
12985
 
12703
- // TODO: fix
12704
- const updateMetrics = () => { };
12705
- /**
12706
- * `PLUGIN` that collects useful statistics about playback performance.
12707
- * @beta
12708
- * @remarks
12709
- * This plugin does not render anything and is supposed to be extended or used together with other plugins that actually render something.
12710
- */
12711
- class ClapprStats extends ContainerPlugin {
12712
- bwMeasureCount = 0;
12713
- intervalId = null;
12714
- lastDecodedFramesCount = 0;
12715
- metrics = newMetrics$1();
12716
- completion;
12717
- _onReport;
12718
- runBandwidthTestEvery;
12719
- runEach;
12720
- timers = {
12721
- startup: 0,
12722
- watch: 0,
12723
- pause: 0,
12724
- buffering: 0,
12725
- session: 0,
12726
- latency: 0,
12727
- };
12728
- updateFn = updateMetrics;
12729
- urisToMeasureBandwidth;
12730
- uriToMeasureLatency;
12731
- /**
12732
- * @internal
12733
- */
12734
- get name() {
12735
- return 'clappr_stats';
12736
- }
12737
- /**
12738
- * @internal
12739
- */
12740
- get supportedVersion() {
12741
- return { min: CLAPPR_VERSION$1 };
12742
- }
12743
- get _playbackName() {
12744
- return String(this.container.playback.name || '');
12745
- }
12746
- get _playbackType() {
12747
- return this.container.getPlaybackType();
12748
- }
12749
- _now() {
12750
- const hasPerformanceSupport = window.performance && typeof (window.performance.now) === 'function';
12751
- return (hasPerformanceSupport) ? window.performance.now() : new Date().getTime();
12752
- }
12753
- _inc(counter) {
12754
- this.metrics.counters[counter] += 1;
12755
- }
12756
- // _timerHasStarted(timer) {
12757
- // return this[`_start${timer}`] !== undefined;
12758
- // }
12759
- start(timer) {
12760
- // this[`_start${timer}`] = this._now();
12761
- this.timers[timer] = this._now();
12762
- }
12763
- _stop(timer) {
12764
- // this._metrics.timers[timer] += this._now() - this[`_start${timer}`];
12765
- this.metrics.timers[timer] += this._now() - this.timers[timer];
12766
- }
12767
- /**
12768
- * Registers a callback to receive the metrics.
12769
- * @param updateMetricsFn - The callback to receive the metrics
12770
- */
12771
- setUpdateMetrics(updateMetricsFn) {
12772
- // TODO use events instead
12773
- this.updateFn = updateMetricsFn;
12774
- }
12775
- _defaultReport(metrics) {
12776
- this.updateFn(metrics);
12777
- }
12778
- constructor(container) {
12779
- super(container);
12780
- this.runEach = container.options.clapprStats?.runEach ?? 5000;
12781
- this._onReport = container.options.clapprStats?.onReport ?? this._defaultReport;
12782
- this.uriToMeasureLatency = container.options.clapprStats?.uriToMeasureLatency;
12783
- this.urisToMeasureBandwidth = container.options.clapprStats?.urisToMeasureBandwidth;
12784
- this.runBandwidthTestEvery = container.options.clapprStats?.runBandwidthTestEvery ?? 10;
12785
- this.completion = {
12786
- watch: container.options.clapprStats?.onCompletion ?? [],
12787
- calls: []
12788
- };
12789
- }
12790
- /**
12791
- * @internal
12792
- */
12793
- bindEvents() {
12794
- this.listenTo(this.container, Events.CONTAINER_BITRATE, this.onBitrate);
12795
- this.listenTo(this.container, Events.CONTAINER_STOP, this.stopReporting);
12796
- this.listenTo(this.container, Events.CONTAINER_ENDED, this.stopReporting);
12797
- this.listenToOnce(this.container.playback, Events.PLAYBACK_PLAY_INTENT, this.startTimers);
12798
- this.listenToOnce(this.container, Events.CONTAINER_PLAY, this.onFirstPlaying);
12799
- this.listenTo(this.container, Events.CONTAINER_PLAY, this.onPlay);
12800
- this.listenTo(this.container, Events.CONTAINER_PAUSE, this.onPause);
12801
- this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERING, this.onBuffering);
12802
- this.listenTo(this.container, Events.CONTAINER_SEEK, this.onSeek);
12803
- this.listenTo(this.container, Events.CONTAINER_ERROR, () => this._inc('error'));
12804
- this.listenTo(this.container, Events.CONTAINER_FULLSCREEN, () => this._inc('fullscreen'));
12805
- this.listenTo(this.container, Events.CONTAINER_PLAYBACKDVRSTATECHANGED, (dvrInUse) => {
12806
- dvrInUse && this._inc('dvrUsage');
12807
- });
12808
- this.listenTo(this.container.playback, Events.PLAYBACK_PROGRESS, this.onProgress);
12809
- this.listenTo(this.container.playback, Events.PLAYBACK_TIMEUPDATE, this.onTimeUpdate);
12810
- }
12811
- /**
12812
- * @internal
12813
- */
12814
- destroy() {
12815
- this.stopReporting();
12816
- super.destroy();
12817
- }
12818
- /**
12819
- * Returns the collected metrics.
12820
- * @returns Currently collected metrics
12821
- */
12822
- exportMetrics() {
12823
- return structuredClone(this.metrics);
12824
- }
12825
- onBitrate(newBitrate) {
12826
- const bitrate = newBitrate.bitrate;
12827
- const now = this._now();
12828
- if (this.metrics.extra.bitratesHistory.length > 0) {
12829
- const beforeLast = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
12830
- beforeLast.end = now;
12831
- beforeLast.time = now - beforeLast.start;
12832
- }
12833
- this.metrics.extra.bitratesHistory.push({ start: this._now(), bitrate: bitrate });
12834
- this._inc('changeLevel');
12835
- }
12836
- stopReporting() {
12837
- this._buildReport();
12838
- if (this.intervalId !== null) {
12839
- clearInterval(this.intervalId);
12840
- this.intervalId = null;
12841
- }
12842
- this._newMetrics();
12843
- // TODO
12844
- // @ts-ignore
12845
- this.stopListening();
12846
- this.bindEvents();
12847
- }
12848
- startTimers() {
12849
- this.intervalId = setInterval(this._buildReport.bind(this), this.runEach);
12850
- this.start('session');
12851
- this.start('startup');
12852
- }
12853
- onFirstPlaying() {
12854
- this.listenTo(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
12855
- this.start('watch');
12856
- this._stop('startup');
12857
- }
12858
- playAfterPause() {
12859
- this.listenTo(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
12860
- this._stop('pause');
12861
- this.start('watch');
12862
- }
12863
- onPlay() {
12864
- this._inc('play');
12865
- }
12866
- onPause() {
12867
- this._stop('watch');
12868
- this.start('pause');
12869
- this._inc('pause');
12870
- this.listenToOnce(this.container, Events.CONTAINER_PLAY, this.playAfterPause);
12871
- this.stopListening(this.container, Events.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
12872
- }
12873
- onSeek(e) {
12874
- this._inc('seek');
12875
- this.metrics.extra.watchHistory.push([e * 1000, e * 1000]);
12876
- }
12877
- onTimeUpdate(e) {
12878
- const current = e.current * 1000, total = e.total * 1000, l = this.metrics.extra.watchHistory.length;
12879
- this.metrics.extra.duration = total;
12880
- this.metrics.extra.currentTime = current;
12881
- this.metrics.extra.watchedPercentage = (current / total) * 100;
12882
- if (l === 0) {
12883
- this.metrics.extra.watchHistory.push([current, current]);
12884
- }
12885
- else {
12886
- this.metrics.extra.watchHistory[l - 1][1] = current;
12887
- }
12888
- if (this.metrics.extra.bitratesHistory.length > 0) {
12889
- const lastBitrate = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
12890
- if (!lastBitrate.end) {
12891
- lastBitrate.time = this._now() - lastBitrate.start;
12892
- }
12893
- }
12894
- this._onCompletion();
12895
- }
12896
- onContainerUpdateWhilePlaying() {
12897
- if (this.container.playback.isPlaying()) {
12898
- this._stop('watch');
12899
- this.start('watch');
12900
- }
12901
- }
12902
- onBuffering() {
12903
- this._inc('buffering');
12904
- this.start('buffering');
12905
- this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERFULL, this.onBufferfull);
12906
- }
12907
- onBufferfull() {
12908
- this._stop('buffering');
12909
- this.listenToOnce(this.container, Events.CONTAINER_STATE_BUFFERING, this.onBuffering);
12910
- }
12911
- onProgress(progress) {
12912
- this.metrics.extra.buffersize = progress.current * 1000;
12913
- }
12914
- _newMetrics() {
12915
- this.metrics = newMetrics$1();
12916
- }
12917
- _onCompletion() {
12918
- const currentPercentage = this.metrics.extra.watchedPercentage;
12919
- const allPercentages = this.completion.watch;
12920
- const isCalled = this.completion.calls.indexOf(currentPercentage) !== -1;
12921
- if (allPercentages.indexOf(currentPercentage) !== -1 && !isCalled) {
12922
- Log.info(this.name + ' PERCENTAGE_EVENT: ' + currentPercentage);
12923
- this.completion.calls.push(currentPercentage);
12924
- this.trigger(ClapprStatsEvents.PERCENTAGE_EVENT, currentPercentage);
12925
- }
12926
- }
12927
- _buildReport() {
12928
- this._stop('session');
12929
- this.start('session');
12930
- this.metrics.extra.playbackName = this._playbackName;
12931
- this.metrics.extra.playbackType = this._playbackType;
12932
- this._calculateBitrates();
12933
- this._calculatePercentages();
12934
- this._fetchFPS();
12935
- this._measureLatency();
12936
- this._measureBandwidth();
12937
- this._onReport(this.metrics);
12938
- this.trigger(ClapprStatsEvents.REPORT_EVENT, structuredClone(this.metrics));
12939
- }
12940
- _fetchFPS() {
12941
- // flashls ??? - hls.droppedFramesl hls.stream.bufferLength (seconds)
12942
- // hls ??? (use the same?)
12943
- const fetchFPS = {
12944
- 'html5_video': this._html5FetchFPS,
12945
- 'hls': this._html5FetchFPS,
12946
- 'dash_shaka_playback': this._html5FetchFPS
12947
- };
12948
- if (this._playbackName in fetchFPS) {
12949
- fetchFPS[this._playbackName].call(this);
12950
- }
12951
- }
12952
- _calculateBitrates() {
12953
- const { bitratesHistory } = this.metrics.extra;
12954
- if (bitratesHistory.length === 0) {
12955
- return;
12956
- }
12957
- let totalTime = 0;
12958
- let weightedTotal = 0;
12959
- for (const { bitrate, time = 0 } of bitratesHistory) {
12960
- totalTime += time;
12961
- weightedTotal += bitrate * time;
12962
- }
12963
- this.metrics.extra.bitrateWeightedMean = weightedTotal / totalTime;
12964
- this.metrics.extra.bitrateMostUsed = bitratesHistory.reduce((mostUsed, current) => (current.time || 0) > (mostUsed.time || 0) ? current : mostUsed, { time: 0, bitrate: 0, start: 0, end: 0 }).bitrate;
12965
- }
12966
- _calculatePercentages() {
12967
- if (this.metrics.extra.duration > 0) {
12968
- this.metrics.extra.bufferingPercentage = (this.metrics.timers.buffering / this.metrics.extra.duration) * 100;
12969
- }
12970
- }
12971
- _html5FetchFPS() {
12972
- const videoTag = this.container.playback.el;
12973
- const getFirstValidValue = (...args) => args.find(val => val !== undefined);
12974
- const decodedFrames = getFirstValidValue(videoTag.webkitDecodedFrameCount, videoTag.mozDecodedFrames, 0);
12975
- const droppedFrames = getFirstValidValue(videoTag.webkitDroppedFrameCount, videoTag.mozParsedFrames && videoTag.mozDecodedFrames ? videoTag.mozParsedFrames - videoTag.mozDecodedFrames : 0, 0);
12976
- const decodedFramesLastTime = decodedFrames - (this.lastDecodedFramesCount || 0);
12977
- this.metrics.counters.decodedFrames = decodedFrames;
12978
- this.metrics.counters.droppedFrames = droppedFrames;
12979
- this.metrics.counters.fps = decodedFramesLastTime / (this.runEach / 1000);
12980
- this.lastDecodedFramesCount = decodedFrames;
12981
- }
12982
- // originally from https://www.smashingmagazine.com/2011/11/analyzing-network-characteristics-using-javascript-and-the-dom-part-1/
12983
- _measureLatency() {
12984
- if (this.uriToMeasureLatency) {
12985
- const t = [];
12986
- const n = 2;
12987
- let rtt;
12988
- const ld = () => {
12989
- t.push(this._now());
12990
- if (t.length > n) {
12991
- done();
12992
- }
12993
- else {
12994
- const img = new Image;
12995
- img.onload = ld;
12996
- img.src = this.uriToMeasureLatency + '?' + Math.random()
12997
- + '=' + this._now();
12998
- }
12999
- };
13000
- const done = () => {
13001
- rtt = t[2] - t[1];
13002
- this.metrics.timers.latency = rtt;
13003
- };
13004
- ld();
13005
- }
13006
- }
13007
- // originally from https://www.smashingmagazine.com/2011/11/analyzing-network-characteristics-using-javascript-and-the-dom-part-1/
13008
- _measureBandwidth() {
13009
- if (this.urisToMeasureBandwidth && (this.bwMeasureCount % this.runBandwidthTestEvery === 0)) {
13010
- let i = 0;
13011
- const ld = (e) => {
13012
- if (i > 0) {
13013
- const prev = this.urisToMeasureBandwidth[i - 1];
13014
- prev.end = this._now();
13015
- if (prev.timer !== null) {
13016
- clearTimeout(prev.timer);
13017
- }
13018
- }
13019
- if (i >= this.urisToMeasureBandwidth.length || (i > 0 && this.urisToMeasureBandwidth[i - 1].expired)) {
13020
- assert(e, 'incorrect invocation in _measureBandwidth');
13021
- done(e);
13022
- }
13023
- else {
13024
- const xhr = new XMLHttpRequest();
13025
- xhr.open('GET', this.urisToMeasureBandwidth[i].url, true);
13026
- xhr.responseType = 'arraybuffer';
13027
- xhr.onload = xhr.onabort = ld;
13028
- this.urisToMeasureBandwidth[i].start = this._now();
13029
- this.urisToMeasureBandwidth[i].timer = setTimeout((j) => {
13030
- this.urisToMeasureBandwidth[j].expired = true;
13031
- xhr.abort();
13032
- }, this.urisToMeasureBandwidth[i].timeout, i);
13033
- xhr.send();
13034
- }
13035
- i++;
13036
- };
13037
- const done = (e) => {
13038
- const timeSpent = (this.urisToMeasureBandwidth[i - 1].end - this.urisToMeasureBandwidth[i - 1].start) / 1000;
13039
- const bandwidthBps = (e.loaded * 8) / timeSpent;
13040
- this.metrics.extra.bandwidth = bandwidthBps;
13041
- this.urisToMeasureBandwidth.forEach((x) => {
13042
- x.start = 0;
13043
- x.end = 0;
13044
- x.expired = false;
13045
- if (x.timer !== null) {
13046
- clearTimeout(x.timer);
13047
- x.timer = null;
13048
- }
13049
- });
13050
- };
13051
- ld();
13052
- }
13053
- this.bwMeasureCount++;
13054
- }
13055
- }
13056
- // ClapprStats.REPORT_EVENT = 'clappr:stats:report';
13057
- // ClapprStats.PERCENTAGE_EVENT = 'clappr:stats:percentage';
13058
-
13059
12986
  // This work is based on the original work of the following authors:
13060
12987
  // Copyright 2014 Globo.com Player authors. All rights reserved.
13061
12988
  // Use of this source code is governed by a BSD-style
@@ -18768,4 +18695,4 @@ class VolumeFade extends UICorePlugin {
18768
18695
  }
18769
18696
  }
18770
18697
 
18771
- export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, ClapprNerdStats, ClapprStats, ClickToPause, Clips, ClosedCaptions, ContextMenu, DvrControls, ErrorScreen, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, Logo, MediaControl, MultiCamera, PictureInPicture, PlaybackRate, Poster, QualityLevels, SeekTime, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents };
18698
+ export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, NerdStats as ClapprNerdStats, ClapprStats, ClickToPause, Clips, ClosedCaptions, ContextMenu, DvrControls, ErrorScreen, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, Logo, MediaControl, MultiCamera, NerdStats, PictureInPicture, PlaybackRate, Poster, QualityLevels, SeekTime, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents };