@gcorevideo/player 2.22.17 → 2.22.20

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 (120) hide show
  1. package/assets/audio-selector/track-selector.ejs +3 -3
  2. package/assets/bottom-gear/bottomgear.ejs +3 -3
  3. package/assets/clappr-nerd-stats/clappr-nerd-stats.ejs +76 -78
  4. package/assets/clappr-nerd-stats/clappr-nerd-stats.scss +10 -7
  5. package/assets/dvr-controls/dvr_controls.scss +0 -12
  6. package/dist/core.js +5 -7
  7. package/dist/index.css +1245 -1251
  8. package/dist/index.js +425 -261
  9. package/dist/player.d.ts +121 -108
  10. package/dist/plugins/index.css +577 -583
  11. package/dist/plugins/index.js +355 -187
  12. package/docs/api/player.bitratetrackrecord.md +20 -0
  13. package/docs/api/player.clapprstats.exportmetrics.md +2 -2
  14. package/docs/api/player.clapprstats.md +0 -4
  15. package/docs/api/player.clapprstatschronograph.md +115 -0
  16. package/docs/api/player.clapprstatscounter.md +211 -0
  17. package/docs/api/player.clapprstatsevents.md +51 -0
  18. package/docs/api/player.clapprstatsmetrics.md +52 -0
  19. package/docs/api/player.clipspluginsettings.md +1 -1
  20. package/docs/api/player.md +57 -2
  21. package/docs/api/player.nerdstats.md +3 -3
  22. package/docs/api/player.playerconfig.md +1 -1
  23. package/docs/api/player.playerconfig.playbacktype.md +6 -1
  24. package/docs/api/player.timeupdate.md +6 -3
  25. package/lib/playback/dash-playback/DashPlayback.d.ts +0 -1
  26. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  27. package/lib/playback/dash-playback/DashPlayback.js +4 -5
  28. package/lib/playback/hls-playback/HlsPlayback.d.ts +1 -1
  29. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  30. package/lib/playback/hls-playback/HlsPlayback.js +0 -1
  31. package/lib/playback.types.d.ts +2 -3
  32. package/lib/playback.types.d.ts.map +1 -1
  33. package/lib/plugins/audio-selector/AudioSelector.d.ts +1 -1
  34. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  35. package/lib/plugins/audio-selector/AudioSelector.js +15 -8
  36. package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -1
  37. package/lib/plugins/bottom-gear/BottomGear.js +2 -2
  38. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +17 -14
  39. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts.map +1 -1
  40. package/lib/plugins/clappr-nerd-stats/NerdStats.js +175 -124
  41. package/lib/plugins/clappr-nerd-stats/formatter.d.ts +5 -0
  42. package/lib/plugins/clappr-nerd-stats/formatter.d.ts.map +1 -1
  43. package/lib/plugins/clappr-nerd-stats/formatter.js +56 -24
  44. package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts +2 -2
  45. package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts.map +1 -1
  46. package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts +1 -1
  47. package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts.map +1 -1
  48. package/lib/plugins/clappr-nerd-stats/types.d.ts +3 -0
  49. package/lib/plugins/clappr-nerd-stats/types.d.ts.map +1 -1
  50. package/lib/plugins/clappr-nerd-stats/utils.d.ts +7 -0
  51. package/lib/plugins/clappr-nerd-stats/utils.d.ts.map +1 -0
  52. package/lib/plugins/clappr-nerd-stats/utils.js +67 -0
  53. package/lib/plugins/clappr-stats/ClapprStats.d.ts +5 -2
  54. package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
  55. package/lib/plugins/clappr-stats/ClapprStats.js +31 -33
  56. package/lib/plugins/clappr-stats/types.d.ts +21 -22
  57. package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
  58. package/lib/plugins/clappr-stats/types.js +22 -22
  59. package/lib/plugins/clappr-stats/utils.d.ts +2 -2
  60. package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
  61. package/lib/plugins/clappr-stats/utils.js +0 -1
  62. package/lib/plugins/click-to-pause/ClickToPause.js +1 -1
  63. package/lib/plugins/clips/Clips.d.ts +1 -1
  64. package/lib/plugins/dvr-controls/DvrControls.d.ts +6 -2
  65. package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
  66. package/lib/plugins/dvr-controls/DvrControls.js +39 -27
  67. package/lib/plugins/media-control/MediaControl.d.ts +6 -2
  68. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  69. package/lib/plugins/media-control/MediaControl.js +20 -9
  70. package/lib/plugins/picture-in-picture/PictureInPicture.js +1 -1
  71. package/lib/plugins/seek-time/SeekTime.d.ts +1 -1
  72. package/lib/plugins/seek-time/SeekTime.d.ts.map +1 -1
  73. package/lib/plugins/seek-time/SeekTime.js +3 -4
  74. package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
  75. package/lib/plugins/vast-ads/VastAds.js +1 -1
  76. package/lib/plugins/vast-ads/rollmanager.js +1 -1
  77. package/lib/testUtils.d.ts.map +1 -1
  78. package/lib/testUtils.js +7 -4
  79. package/lib/types.d.ts +1 -1
  80. package/package.json +3 -3
  81. package/src/playback/__tests__/HTML5Video.test.ts +2 -2
  82. package/src/playback/dash-playback/DashPlayback.ts +5 -7
  83. package/src/playback/hls-playback/HlsPlayback.ts +2 -4
  84. package/src/playback.types.ts +2 -3
  85. package/src/plugins/audio-selector/AudioSelector.ts +14 -7
  86. package/src/plugins/audio-selector/__tests__/AudioSelector.test.ts +8 -8
  87. package/src/plugins/audio-selector/__tests__/__snapshots__/AudioSelector.test.ts.snap +15 -15
  88. package/src/plugins/bottom-gear/BottomGear.ts +2 -2
  89. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +8 -5
  90. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +3 -3
  91. package/src/plugins/clappr-nerd-stats/NerdStats.ts +216 -143
  92. package/src/plugins/clappr-nerd-stats/formatter.ts +91 -47
  93. package/src/plugins/clappr-nerd-stats/speedtest/index.ts +2 -2
  94. package/src/plugins/clappr-nerd-stats/speedtest/types.ts +1 -1
  95. package/src/plugins/clappr-nerd-stats/types.ts +43 -3
  96. package/src/plugins/clappr-nerd-stats/utils.ts +75 -0
  97. package/src/plugins/clappr-stats/ClapprStats.ts +41 -40
  98. package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +12 -12
  99. package/src/plugins/clappr-stats/types.ts +43 -44
  100. package/src/plugins/clappr-stats/utils.ts +4 -5
  101. package/src/plugins/click-to-pause/ClickToPause.ts +1 -1
  102. package/src/plugins/clips/Clips.ts +1 -1
  103. package/src/plugins/clips/__tests__/Clips.test.ts +1 -1
  104. package/src/plugins/clips/__tests__/__snapshots__/Clips.test.ts.snap +1 -1
  105. package/src/plugins/dvr-controls/DvrControls.ts +51 -37
  106. package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +84 -26
  107. package/src/plugins/dvr-controls/__tests__/__snapshots__/DvrControls.test.ts.snap +0 -12
  108. package/src/plugins/media-control/MediaControl.ts +21 -9
  109. package/src/plugins/media-control/__tests__/MediaControl.test.ts +8 -5
  110. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +20 -20
  111. package/src/plugins/picture-in-picture/PictureInPicture.ts +1 -1
  112. package/src/plugins/seek-time/SeekTime.ts +4 -5
  113. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  114. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
  115. package/src/plugins/vast-ads/VastAds.ts +1 -1
  116. package/src/plugins/vast-ads/rollmanager.ts +1 -1
  117. package/src/testUtils.ts +11 -5
  118. package/src/types.ts +1 -1
  119. package/temp/player.api.json +630 -12
  120. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,11 @@
1
- import { UICorePlugin, Events, template, Core, Container } from '@clappr/core'
1
+ import {
2
+ UICorePlugin,
3
+ Events,
4
+ template,
5
+ Core,
6
+ Container,
7
+ Playback,
8
+ } from '@clappr/core'
2
9
  import { reportError, trace } from '@gcorevideo/utils'
3
10
  import Mousetrap from 'mousetrap'
4
11
  import assert from 'assert'
@@ -6,7 +13,7 @@ import assert from 'assert'
6
13
  import { CLAPPR_VERSION } from '../../build.js'
7
14
  import {
8
15
  ClapprStatsEvents,
9
- Metrics as BaseMetrics,
16
+ ClapprStatsMetrics as PerfMetrics,
10
17
  } from '../clappr-stats/types.js'
11
18
  import { newMetrics as newBaseMetrics } from '../clappr-stats/utils.js'
12
19
  import Formatter from './formatter.js'
@@ -18,96 +25,35 @@ import {
18
25
  startSpeedtest,
19
26
  stopSpeedtest,
20
27
  } from './speedtest/index.js'
21
- import { CustomMetrics } from './speedtest/types.js'
22
- import { ZeptoResult } from '../../types.js'
28
+ import { SpeedtestMetrics } from './speedtest/types.js'
29
+ import { PlaybackType } from '../../types.js'
23
30
 
24
31
  import '../../../assets/clappr-nerd-stats/clappr-nerd-stats.scss'
25
32
  import pluginHtml from '../../../assets/clappr-nerd-stats/clappr-nerd-stats.ejs'
26
33
  import buttonHtml from '../../../assets/clappr-nerd-stats/button.ejs'
27
34
  import statsIcon from '../../../assets/icons/new/stats.svg'
28
35
  import { BottomGear, GearEvents } from '../bottom-gear/BottomGear.js'
36
+ import { drawSummary, getPingQuality } from './utils.js'
37
+ import { getDownloadQuality } from './utils.js'
29
38
 
30
- const qualityClasses = [
31
- 'speedtest-quality-value-1',
32
- 'speedtest-quality-value-2',
33
- 'speedtest-quality-value-3',
34
- 'speedtest-quality-value-4',
35
- 'speedtest-quality-value-5',
36
- ]
37
-
38
- const getDownloadQuality = (speedValue: number): number => {
39
- if (speedValue < 3) {
40
- return 1
41
- } else if (speedValue < 7) {
42
- return 2
43
- } else if (speedValue < 13) {
44
- return 3
45
- } else if (speedValue < 25) {
46
- return 4
47
- } else {
48
- return 5
49
- }
50
- }
51
-
52
- const getPingQuality = (pingValue: number): number => {
53
- if (pingValue < 20) {
54
- return 5
55
- } else if (pingValue < 50) {
56
- return 4
57
- } else if (pingValue < 100) {
58
- return 3
59
- } else if (pingValue < 150) {
60
- return 2
61
- } else {
62
- return 1
63
- }
64
- }
65
-
66
- const generateQualityHtml = (quality: number): string => {
67
- const html = []
68
- const qualityClassName = qualityClasses[quality - 1]
69
-
70
- for (let i = 0; i < qualityClasses.length; i++) {
71
- if (i < quality) {
72
- html.push(
73
- `<div class="speedtest-quality-content-item ${qualityClassName}"></div>`,
74
- )
75
- } else {
76
- html.push('<div class="speedtest-quality-content-item"></div>')
77
- }
78
- }
79
-
80
- return html.join('')
81
- }
82
-
83
- const drawSummary = (
84
- customMetrics: CustomMetrics,
85
- vodContainer: ZeptoResult,
86
- liveContainer: ZeptoResult,
87
- ) => {
88
- const { connectionSpeed, ping } = customMetrics
89
-
90
- if (!connectionSpeed || !ping) {
91
- return
92
- }
93
- const downloadQuality = getDownloadQuality(connectionSpeed)
94
- const pingQuality = getPingQuality(ping)
95
- const liveQuality = Math.min(downloadQuality, pingQuality)
96
- const vodHtml = generateQualityHtml(downloadQuality)
97
- const liveHtml = generateQualityHtml(liveQuality)
98
-
99
- vodContainer.html(vodHtml)
100
- liveContainer.html(liveHtml)
39
+ const PLAYBACK_NAMES: Record<string, string> = {
40
+ dash: 'DASH.js',
41
+ hls: 'HLS.js',
42
+ html5_video: 'Native',
101
43
  }
102
44
 
103
45
  type IconPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
104
46
 
105
- type Metrics = BaseMetrics & {
47
+ type Metrics = PerfMetrics & {
106
48
  general: {
107
49
  displayResolution?: string
108
- volume?: number
50
+ resolution: {
51
+ width: number
52
+ height: number
53
+ }
54
+ volume: number
109
55
  }
110
- custom: CustomMetrics & {
56
+ custom: SpeedtestMetrics & {
111
57
  vodQuality?: string
112
58
  liveQuality?: string
113
59
  }
@@ -116,16 +62,16 @@ type Metrics = BaseMetrics & {
116
62
  const T = 'plugins.nerd_stats'
117
63
 
118
64
  /**
119
- * `PLUGIN` that displays useful network-related statistics.
65
+ * `PLUGIN` that displays useful statistics regarding the playback as well as the network quality estimation.
120
66
  * @beta
121
67
  *
122
68
  * @remarks
123
69
  * Depends on:
124
70
  *
125
- * - {@link BottomGear}
126
- *
127
- * - {@link ClapprStats}
71
+ * - {@link BottomGear} - where the button is attached
128
72
  *
73
+ * - {@link ClapprStats} - to get the metrics from
74
+ *
129
75
  * The plugin is rendered as an item in the gear menu.
130
76
  *
131
77
  * When clicked, it shows an overlay window with the information about the network speed, latency, etc,
@@ -134,7 +80,7 @@ const T = 'plugins.nerd_stats'
134
80
  export class NerdStats extends UICorePlugin {
135
81
  private container: Container | null = null
136
82
 
137
- private customMetrics: CustomMetrics = {
83
+ private speedtestMetrics: SpeedtestMetrics = {
138
84
  connectionSpeed: 0,
139
85
  ping: 0,
140
86
  jitter: 0,
@@ -142,7 +88,7 @@ export class NerdStats extends UICorePlugin {
142
88
 
143
89
  private metrics: Metrics = newMetrics()
144
90
 
145
- private showing = false
91
+ private open = false
146
92
 
147
93
  private shortcut: string[]
148
94
 
@@ -171,7 +117,6 @@ export class NerdStats extends UICorePlugin {
171
117
  */
172
118
  override get attributes() {
173
119
  return {
174
- 'data-clappr-nerd-stats': '',
175
120
  class: 'clappr-nerd-stats',
176
121
  }
177
122
  }
@@ -181,14 +126,19 @@ export class NerdStats extends UICorePlugin {
181
126
  */
182
127
  override get events() {
183
128
  return {
184
- 'click [data-show-stats-button]': 'showOrHide',
185
- 'click [data-close-button]': 'hide',
186
- 'click [data-refresh-button]': 'refreshSpeedTest',
129
+ click: 'clicked',
130
+ 'click #nerd-stats-close': 'hide',
131
+ 'click #nerd-stats-refresh': 'refreshSpeedTest',
187
132
  }
188
133
  }
189
134
 
135
+ private clicked(e: MouseEvent) {
136
+ e.stopPropagation()
137
+ e.preventDefault()
138
+ }
139
+
190
140
  private get statsBoxElem() {
191
- return '.clappr-nerd-stats[data-clappr-nerd-stats] .stats-box'
141
+ return this.$el.find('#nerd-stats-box')
192
142
  }
193
143
 
194
144
  private get statsBoxWidthThreshold() {
@@ -211,7 +161,7 @@ export class NerdStats extends UICorePlugin {
211
161
  ]
212
162
  this.iconPosition =
213
163
  core.options.clapprNerdStats?.iconPosition ?? 'bottom-right'
214
- this.customMetrics = {
164
+ this.speedtestMetrics = {
215
165
  connectionSpeed: 0,
216
166
  ping: 0,
217
167
  jitter: 0,
@@ -224,14 +174,24 @@ export class NerdStats extends UICorePlugin {
224
174
  */
225
175
  override bindEvents() {
226
176
  this.listenToOnce(this.core, Events.CORE_READY, this.onCoreReady)
177
+ this.listenTo(this.core, Events.CORE_RESIZE, this.onPlayerResize)
178
+ this.listenTo(
179
+ this.core,
180
+ Events.CORE_ACTIVE_CONTAINER_CHANGED,
181
+ this.onActiveContainerChanged,
182
+ )
227
183
  }
228
184
 
229
185
  private onCoreReady() {
230
186
  const bottomGear = this.core.getPlugin('bottom_gear') as BottomGear
231
187
  assert(bottomGear, 'bottom_gear plugin is required')
188
+ this.listenTo(bottomGear, GearEvents.RENDERED, this.attach)
232
189
 
233
- this.listenTo(bottomGear, GearEvents.RENDERED, this.addToBottomGear)
190
+ Mousetrap.bind(this.shortcut, this.toggle)
191
+ this.updateResolution()
192
+ }
234
193
 
194
+ private onActiveContainerChanged() {
235
195
  this.container = this.core.activeContainer
236
196
  const clapprStats = this.container?.getPlugin('clappr_stats')
237
197
  assert(
@@ -239,11 +199,29 @@ export class NerdStats extends UICorePlugin {
239
199
  'clappr-stats not available. Please, include it as a plugin of your Clappr instance.\n' +
240
200
  'For more info, visit: https://github.com/clappr/clappr-stats.',
241
201
  )
242
- Mousetrap.bind(this.shortcut, this.toggle)
243
- this.listenTo(this.core, Events.CORE_RESIZE, this.onPlayerResize)
244
202
  this.listenTo(clapprStats, ClapprStatsEvents.REPORT, this.updateMetrics)
203
+ this.listenTo(this.core.activeContainer, Events.CONTAINER_VOLUME, () => {
204
+ this.metrics.general.volume = this.container?.volume ?? 0
205
+ this.$el
206
+ .find('#nerd-stats-volume')
207
+ .text(Formatter.formatVolume(this.metrics.general.volume))
208
+ })
209
+ this.listenTo(
210
+ this.core.activePlayback,
211
+ Events.PLAYBACK_LOADEDMETADATA,
212
+ () => {
213
+ this.$el
214
+ .find('#nerd-stats-playback-type')
215
+ .text(
216
+ this.formatPlaybackName(this.core.activePlayback.getPlaybackType()),
217
+ )
218
+ },
219
+ )
245
220
  this.updateMetrics(clapprStats.exportMetrics())
246
- this.render()
221
+ this.$el
222
+ .find('#nerd-stats-playback-name')
223
+ .text(PLAYBACK_NAMES[this.core.activePlayback.name] ?? '-')
224
+ this.core.activeContainer.$el.append(this.$el)
247
225
  }
248
226
 
249
227
  /**
@@ -255,7 +233,7 @@ export class NerdStats extends UICorePlugin {
255
233
  }
256
234
 
257
235
  private toggle = () => {
258
- if (this.showing) {
236
+ if (this.open) {
259
237
  this.hide()
260
238
  } else {
261
239
  this.show()
@@ -263,11 +241,12 @@ export class NerdStats extends UICorePlugin {
263
241
  }
264
242
 
265
243
  private show() {
266
- this.core.$el.find(this.statsBoxElem).show()
267
- this.showing = true
244
+ this.$el.show()
245
+ this.statsBoxElem.scrollTop(this.statsBoxElem.scrollTop())
246
+ this.open = true
268
247
 
269
248
  this.refreshSpeedTest()
270
- initSpeedTest(this.customMetrics)
249
+ initSpeedTest(this.speedtestMetrics)
271
250
  .then(() => {
272
251
  startSpeedtest()
273
252
  })
@@ -278,24 +257,31 @@ export class NerdStats extends UICorePlugin {
278
257
  }
279
258
 
280
259
  private hide() {
281
- this.core.$el.find(this.statsBoxElem).hide()
282
- this.showing = false
260
+ this.$el.hide()
261
+ this.open = false
283
262
  stopSpeedtest()
284
263
  }
285
264
 
286
265
  private onPlayerResize() {
287
266
  this.setStatsBoxSize()
267
+ this.updateResolution()
288
268
  }
289
269
 
290
- private addGeneralMetrics() {
291
- this.metrics.general = {
292
- displayResolution: this.playerWidth + 'x' + this.playerHeight,
293
- volume: this.container?.volume,
270
+ private updateResolution() {
271
+ this.metrics.general.resolution = {
272
+ width: this.playerWidth,
273
+ height: this.playerHeight,
294
274
  }
275
+ this.$el
276
+ .find('#nerd-stats-resolution-width')
277
+ .text(this.metrics.general.resolution.width)
278
+ this.$el
279
+ .find('#nerd-stats-resolution-height')
280
+ .text(this.metrics.general.resolution.height)
295
281
  }
296
282
 
297
- private addCustomMetrics() {
298
- this.metrics.custom = this.customMetrics
283
+ private estimateQuality() {
284
+ trace(`${T} estimateQuality`)
299
285
  const videoQualityNames = [
300
286
  'SD (480p)',
301
287
  'HD (720p)',
@@ -303,16 +289,15 @@ export class NerdStats extends UICorePlugin {
303
289
  '2K (1440p)',
304
290
  '4K (2160p)',
305
291
  ]
306
- const { connectionSpeed, ping } = this.customMetrics
292
+ const { connectionSpeed, ping } = this.speedtestMetrics
307
293
 
308
294
  if (!connectionSpeed || !ping) {
309
- const calculatingText = 'Calculating... Please wait.'
310
-
295
+ const calculatingText = this.core.i18n.t('stats.calculating')
311
296
  this.metrics.custom.vodQuality = calculatingText
312
297
  this.metrics.custom.liveQuality = calculatingText
313
-
314
298
  return
315
299
  }
300
+
316
301
  const downloadQuality = getDownloadQuality(connectionSpeed)
317
302
  const pingQuality = getPingQuality(ping)
318
303
  const liveQuality = Math.min(downloadQuality, pingQuality)
@@ -325,41 +310,106 @@ export class NerdStats extends UICorePlugin {
325
310
  prefix + videoQualityNames[liveQuality - 1]
326
311
  }
327
312
 
328
- private updateMetrics(metrics: BaseMetrics) {
313
+ private updateMetrics(metrics: PerfMetrics) {
314
+ trace(`${T} updateMetrics`, { custom: this.speedtestMetrics })
329
315
  Object.assign(this.metrics, metrics)
330
- this.addGeneralMetrics()
331
- this.addCustomMetrics()
316
+ this.updateEstimatedQuality()
317
+
318
+ this.$el
319
+ .find('#nerd-stats-current-time')
320
+ .text(Formatter.formatTime(this.metrics.extra.currentTime))
321
+ this.$el
322
+ .find('#nerd-stats-video-duration')
323
+ .text(Formatter.formatTime(this.metrics.extra.duration))
324
+ this.$el
325
+ .find('#nerd-stats-buffer-size')
326
+ .text(Formatter.formatTime(this.metrics.extra.buffersize))
327
+
328
+ this.$el
329
+ .find('#nerd-stats-bitrate-weighted-mean')
330
+ .text(Formatter.formatBitrate(this.metrics.extra.bitrateWeightedMean))
331
+ this.$el
332
+ .find('#nerd-stats-bitrate-most-used')
333
+ .text(Formatter.formatBitrate(this.metrics.extra.bitrateMostUsed))
334
+ this.$el
335
+ .find('#nerd-stats-watched-percentage')
336
+ .text(Formatter.formatPercentage(this.metrics.extra.watchedPercentage))
337
+ this.$el
338
+ .find('#nerd-stats-buffering-percentage')
339
+ .text(Formatter.formatPercentage(this.metrics.extra.bufferingPercentage))
340
+
341
+ this.$el
342
+ .find('#nerd-stats-startup-time')
343
+ .text(Formatter.formatTime(this.metrics.chrono.startup))
344
+ this.$el
345
+ .find('#nerd-stats-watch-time')
346
+ .text(Formatter.formatTime(this.metrics.chrono.watch))
347
+ this.$el
348
+ .find('#nerd-stats-pause-time')
349
+ .text(Formatter.formatTime(this.metrics.chrono.pause))
350
+ this.$el
351
+ .find('#nerd-stats-buffering-time')
352
+ .text(Formatter.formatTime(this.metrics.chrono.buffering))
353
+ this.$el
354
+ .find('#nerd-stats-session-time')
355
+ .text(Formatter.formatTime(this.metrics.chrono.session))
356
+
357
+ this.$el.find('#nerd-stats-plays').text(this.metrics.counters.play)
358
+ this.$el.find('#nerd-stats-pauses').text(this.metrics.counters.pause)
359
+ this.$el.find('#nerd-stats-errors').text(this.metrics.counters.error)
360
+ this.$el
361
+ .find('#nerd-stats-bufferings')
362
+ .text(this.metrics.counters.buffering)
363
+ this.$el
364
+ .find('#nerd-stats-decoded-frames')
365
+ .text(this.metrics.counters.decodedFrames)
366
+ this.$el
367
+ .find('#nerd-stats-dropped-frames')
368
+ .text(this.metrics.counters.droppedFrames)
369
+
370
+ this.$el
371
+ .find('#nerd-stats-bitrate-changes')
372
+ .text(this.metrics.counters.changeLevel)
373
+ this.$el.find('#nerd-stats-seeks').text(this.metrics.counters.seek)
374
+ this.$el
375
+ .find('#nerd-stats-fullscreen')
376
+ .text(this.metrics.counters.fullscreen)
377
+ this.$el.find('#nerd-stats-dvr-usage').text(this.metrics.counters.dvrUsage)
378
+
379
+ this.$el
380
+ .find('#nerd-stats-fps')
381
+ .text(Formatter.formatFps(this.metrics.counters.fps))
332
382
 
333
- const scrollTop = this.core.$el.find(this.statsBoxElem).scrollTop()
334
-
335
- this.$el.html(
336
- NerdStats.template({
337
- metrics: Formatter.format(this.metrics),
338
- iconPosition: this.iconPosition,
339
- }),
340
- )
341
383
  this.setStatsBoxSize()
342
384
  drawSpeedTestResults()
343
385
  drawSummary(
344
- this.metrics?.custom,
345
- this.$el.find('.speedtest-quality-content[data-streaming-type="vod"]'),
346
- this.$el.find('.speedtest-quality-content[data-streaming-type="live"]'),
386
+ this.speedtestMetrics,
387
+ this.$el.find('#nerd-stats-quality-vod'),
388
+ this.$el.find('#nerd-stats-quality-live'),
347
389
  )
348
390
 
349
- this.core.$el.find(this.statsBoxElem).scrollTop(scrollTop)
350
-
351
- if (!this.showing) {
391
+ if (!this.open) {
352
392
  this.hide()
353
393
  }
354
394
  }
355
395
 
396
+ private updateEstimatedQuality() {
397
+ this.estimateQuality()
398
+ this.$el
399
+ .find('#nerd-stats-quality-vod-text')
400
+ .html(this.metrics.custom.vodQuality)
401
+ this.$el
402
+ .find('#nerd-stats-quality-live-text')
403
+ .html(this.metrics.custom.liveQuality)
404
+ }
405
+
356
406
  private setStatsBoxSize() {
357
407
  if (this.playerWidth >= this.statsBoxWidthThreshold) {
358
- this.$el.find(this.statsBoxElem).addClass('wide')
359
- this.$el.find(this.statsBoxElem).removeClass('narrow')
408
+ this.statsBoxElem.addClass('wide')
409
+ this.statsBoxElem.removeClass('narrow')
360
410
  } else {
361
- this.$el.find(this.statsBoxElem).removeClass('wide')
362
- this.$el.find(this.statsBoxElem).addClass('narrow')
411
+ this.statsBoxElem.removeClass('wide')
412
+ this.statsBoxElem.addClass('narrow')
363
413
  }
364
414
  }
365
415
 
@@ -367,16 +417,21 @@ export class NerdStats extends UICorePlugin {
367
417
  * @internal
368
418
  */
369
419
  override render() {
370
- trace(`${T} render`)
371
- // TODO append to the container
372
- this.core.$el.append(this.$el[0])
373
- this.hide()
420
+ this.$el
421
+ .html(
422
+ NerdStats.template({
423
+ metrics: Formatter.format(this.metrics ?? newMetrics()),
424
+ iconPosition: this.iconPosition,
425
+ i18n: this.core.i18n,
426
+ }),
427
+ )
428
+ .hide()
374
429
 
375
430
  return this
376
431
  }
377
432
 
378
- private addToBottomGear() {
379
- trace(`${T} addToBottomGear`)
433
+ private attach() {
434
+ trace(`${T} attach`)
380
435
  const gear = this.core.getPlugin('bottom_gear') as BottomGear
381
436
  gear
382
437
  .addItem('nerd_stats')
@@ -392,12 +447,12 @@ export class NerdStats extends UICorePlugin {
392
447
  })
393
448
  }
394
449
 
395
- private clearCustomMetrics() {
450
+ private clearSpeedtestMetrics() {
396
451
  const clapprStats = this.container?.getPlugin('clappr_stats')
397
452
 
398
- this.customMetrics.connectionSpeed = 0
399
- this.customMetrics.ping = 0
400
- this.customMetrics.jitter = 0
453
+ this.speedtestMetrics.connectionSpeed = 0
454
+ this.speedtestMetrics.ping = 0
455
+ this.speedtestMetrics.jitter = 0
401
456
 
402
457
  if (clapprStats) {
403
458
  this.updateMetrics(clapprStats.exportMetrics())
@@ -407,7 +462,7 @@ export class NerdStats extends UICorePlugin {
407
462
  private refreshSpeedTest() {
408
463
  stopSpeedtest()
409
464
  setTimeout(() => {
410
- this.clearCustomMetrics()
465
+ this.clearSpeedtestMetrics()
411
466
  clearSpeedTestResults()
412
467
  drawSpeedTestResults()
413
468
  }, 200)
@@ -415,12 +470,30 @@ export class NerdStats extends UICorePlugin {
415
470
  startSpeedtest()
416
471
  }, 800)
417
472
  }
473
+
474
+ private formatPlaybackName(playbackType: PlaybackType): string {
475
+ switch (playbackType) {
476
+ case Playback.VOD:
477
+ return this.core.i18n.t('vod')
478
+ case Playback.LIVE:
479
+ return this.core.i18n.t('live')
480
+ default:
481
+ return '-'
482
+ }
483
+ }
418
484
  }
419
485
 
420
486
  function newMetrics(): Metrics {
421
487
  return {
422
488
  ...newBaseMetrics(),
423
- general: {},
489
+ general: {
490
+ displayResolution: '',
491
+ resolution: {
492
+ width: 0,
493
+ height: 0,
494
+ },
495
+ volume: 0,
496
+ },
424
497
  custom: {
425
498
  connectionSpeed: 0,
426
499
  ping: 0,