@gcorevideo/player 2.22.16 → 2.22.18

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 (104) hide show
  1. package/assets/clappr-nerd-stats/clappr-nerd-stats.ejs +76 -78
  2. package/assets/clappr-nerd-stats/clappr-nerd-stats.scss +10 -7
  3. package/dist/core.js +10 -14
  4. package/dist/index.css +1441 -1440
  5. package/dist/index.js +589 -522
  6. package/dist/player.d.ts +216 -159
  7. package/dist/plugins/index.css +1204 -1203
  8. package/dist/plugins/index.js +581 -506
  9. package/docs/api/player.clapprstats.exportmetrics.md +1 -1
  10. package/docs/api/player.clapprstats.md +5 -15
  11. package/docs/api/player.clapprstatssettings.md +13 -0
  12. package/docs/api/player.clips.destroy.md +18 -0
  13. package/docs/api/player.clips.disable.md +18 -0
  14. package/docs/api/player.clips.enable.md +18 -0
  15. package/docs/api/player.clips.md +170 -0
  16. package/docs/api/player.clips.render.md +18 -0
  17. package/docs/api/player.clips.supportedversion.md +16 -0
  18. package/docs/api/player.clips.version.md +14 -0
  19. package/docs/api/player.clipspluginsettings.md +2 -2
  20. package/docs/api/player.clipspluginsettings.text.md +1 -1
  21. package/docs/api/player.md +27 -18
  22. package/docs/api/player.mediacontrol.md +1 -1
  23. package/docs/api/{player.mediacontrol.getelement.md → player.mediacontrol.mount.md} +20 -7
  24. package/docs/api/player.mediacontrolleftelement.md +1 -1
  25. package/docs/api/{player.clapprnerdstats._constructor_.md → player.nerdstats._constructor_.md} +3 -3
  26. package/docs/api/{player.clapprnerdstats.md → player.nerdstats.md} +5 -5
  27. package/docs/api/player.qualitylevel.height.md +1 -1
  28. package/docs/api/player.qualitylevel.level.md +1 -1
  29. package/docs/api/player.qualitylevel.md +4 -4
  30. package/docs/api/player.qualitylevel.width.md +1 -1
  31. package/docs/api/player.timeposition.current.md +1 -1
  32. package/docs/api/player.timeposition.md +2 -2
  33. package/docs/api/player.timeposition.total.md +1 -1
  34. package/docs/api/player.timeprogress.md +6 -4
  35. package/docs/api/player.timevalue.md +1 -1
  36. package/lib/index.plugins.d.ts +2 -1
  37. package/lib/index.plugins.d.ts.map +1 -1
  38. package/lib/index.plugins.js +2 -1
  39. package/lib/playback/dash-playback/DashPlayback.d.ts +0 -1
  40. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  41. package/lib/playback/dash-playback/DashPlayback.js +9 -12
  42. package/lib/playback/hls-playback/HlsPlayback.d.ts +1 -1
  43. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  44. package/lib/playback/hls-playback/HlsPlayback.js +0 -1
  45. package/lib/playback.types.d.ts +24 -12
  46. package/lib/playback.types.d.ts.map +1 -1
  47. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +4 -0
  48. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
  49. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +20 -23
  50. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +86 -0
  51. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts.map +1 -0
  52. package/lib/plugins/clappr-nerd-stats/NerdStats.js +390 -0
  53. package/lib/plugins/clappr-nerd-stats/formatter.d.ts +5 -0
  54. package/lib/plugins/clappr-nerd-stats/formatter.d.ts.map +1 -1
  55. package/lib/plugins/clappr-nerd-stats/formatter.js +56 -24
  56. package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts +2 -2
  57. package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts.map +1 -1
  58. package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts +1 -1
  59. package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts.map +1 -1
  60. package/lib/plugins/clappr-nerd-stats/types.d.ts +3 -0
  61. package/lib/plugins/clappr-nerd-stats/types.d.ts.map +1 -1
  62. package/lib/plugins/clappr-nerd-stats/utils.d.ts +7 -0
  63. package/lib/plugins/clappr-nerd-stats/utils.d.ts.map +1 -0
  64. package/lib/plugins/clappr-nerd-stats/utils.js +67 -0
  65. package/lib/plugins/clappr-stats/ClapprStats.d.ts +27 -32
  66. package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
  67. package/lib/plugins/clappr-stats/ClapprStats.js +94 -202
  68. package/lib/plugins/clappr-stats/types.d.ts +65 -25
  69. package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
  70. package/lib/plugins/clappr-stats/types.js +37 -2
  71. package/lib/plugins/clappr-stats/utils.d.ts +1 -1
  72. package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
  73. package/lib/plugins/clappr-stats/utils.js +1 -3
  74. package/lib/plugins/seek-time/SeekTime.d.ts +1 -1
  75. package/lib/plugins/seek-time/SeekTime.d.ts.map +1 -1
  76. package/lib/plugins/seek-time/SeekTime.js +3 -4
  77. package/lib/testUtils.d.ts +2 -1
  78. package/lib/testUtils.d.ts.map +1 -1
  79. package/lib/testUtils.js +3 -2
  80. package/package.json +1 -1
  81. package/src/index.plugins.ts +2 -1
  82. package/src/playback/dash-playback/DashPlayback.ts +10 -15
  83. package/src/playback/hls-playback/HlsPlayback.ts +2 -4
  84. package/src/playback.types.ts +25 -11
  85. package/src/plugins/clappr-nerd-stats/NerdStats.ts +503 -0
  86. package/src/plugins/clappr-nerd-stats/formatter.ts +91 -47
  87. package/src/plugins/clappr-nerd-stats/speedtest/index.ts +2 -2
  88. package/src/plugins/clappr-nerd-stats/speedtest/types.ts +1 -1
  89. package/src/plugins/clappr-nerd-stats/types.ts +43 -3
  90. package/src/plugins/clappr-nerd-stats/utils.ts +75 -0
  91. package/src/plugins/clappr-stats/ClapprStats.ts +242 -306
  92. package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +133 -0
  93. package/src/plugins/clappr-stats/types.ts +93 -47
  94. package/src/plugins/clappr-stats/utils.ts +4 -6
  95. package/src/plugins/error-screen/__tests__/ErrorScreen.test.ts +3 -4
  96. package/src/plugins/seek-time/SeekTime.ts +4 -5
  97. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -0
  98. package/src/testUtils.ts +3 -2
  99. package/temp/player.api.json +311 -159
  100. package/tsconfig.tsbuildinfo +1 -1
  101. package/docs/api/player.clapprstats.setupdatemetrics.md +0 -56
  102. package/docs/api/player.clipsplugin.gettext.md +0 -58
  103. package/docs/api/player.clipsplugin.md +0 -59
  104. package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +0 -435
@@ -0,0 +1,503 @@
1
+ import {
2
+ UICorePlugin,
3
+ Events,
4
+ template,
5
+ Core,
6
+ Container,
7
+ Playback,
8
+ } from '@clappr/core'
9
+ import { reportError, trace } from '@gcorevideo/utils'
10
+ import Mousetrap from 'mousetrap'
11
+ import assert from 'assert'
12
+
13
+ import { CLAPPR_VERSION } from '../../build.js'
14
+ import {
15
+ ClapprStatsEvents,
16
+ Metrics as PerfMetrics,
17
+ } from '../clappr-stats/types.js'
18
+ import { newMetrics as newBaseMetrics } from '../clappr-stats/utils.js'
19
+ import Formatter from './formatter.js'
20
+ import {
21
+ clearSpeedTestResults,
22
+ configureSpeedTest,
23
+ drawSpeedTestResults,
24
+ initSpeedTest,
25
+ startSpeedtest,
26
+ stopSpeedtest,
27
+ } from './speedtest/index.js'
28
+ import { SpeedtestMetrics } from './speedtest/types.js'
29
+ import { PlaybackType } from '../../types.js'
30
+
31
+ import '../../../assets/clappr-nerd-stats/clappr-nerd-stats.scss'
32
+ import pluginHtml from '../../../assets/clappr-nerd-stats/clappr-nerd-stats.ejs'
33
+ import buttonHtml from '../../../assets/clappr-nerd-stats/button.ejs'
34
+ import statsIcon from '../../../assets/icons/new/stats.svg'
35
+ import { BottomGear, GearEvents } from '../bottom-gear/BottomGear.js'
36
+ import { drawSummary, getPingQuality } from './utils.js'
37
+ import { getDownloadQuality } from './utils.js'
38
+
39
+ const PLAYBACK_NAMES: Record<string, string> = {
40
+ dash: 'DASH.js',
41
+ hls: 'HLS.js',
42
+ html5_video: 'Native',
43
+ }
44
+
45
+ type IconPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
46
+
47
+ type Metrics = PerfMetrics & {
48
+ general: {
49
+ displayResolution?: string
50
+ resolution: {
51
+ width: number
52
+ height: number
53
+ }
54
+ volume: number
55
+ }
56
+ custom: SpeedtestMetrics & {
57
+ vodQuality?: string
58
+ liveQuality?: string
59
+ }
60
+ }
61
+
62
+ const T = 'plugins.nerd_stats'
63
+
64
+ /**
65
+ * `PLUGIN` that displays useful network-related statistics.
66
+ * @beta
67
+ *
68
+ * @remarks
69
+ * Depends on:
70
+ *
71
+ * - {@link BottomGear}
72
+ *
73
+ * - {@link ClapprStats}
74
+ *
75
+ * The plugin is rendered as an item in the gear menu.
76
+ *
77
+ * When clicked, it shows an overlay window with the information about the network speed, latency, etc,
78
+ * and recommended quality level.
79
+ */
80
+ export class NerdStats extends UICorePlugin {
81
+ private container: Container | null = null
82
+
83
+ private speedtestMetrics: SpeedtestMetrics = {
84
+ connectionSpeed: 0,
85
+ ping: 0,
86
+ jitter: 0,
87
+ }
88
+
89
+ private metrics: Metrics = newMetrics()
90
+
91
+ private open = false
92
+
93
+ private shortcut: string[]
94
+
95
+ private iconPosition: IconPosition
96
+
97
+ private static readonly buttonTemplate = template(buttonHtml)
98
+
99
+ /**
100
+ * @internal
101
+ */
102
+ get name() {
103
+ return 'nerd_stats'
104
+ }
105
+
106
+ /**
107
+ * @internal
108
+ */
109
+ get supportedVersion() {
110
+ return { min: CLAPPR_VERSION }
111
+ }
112
+
113
+ private static readonly template = template(pluginHtml)
114
+
115
+ /**
116
+ * @internal
117
+ */
118
+ override get attributes() {
119
+ return {
120
+ class: 'clappr-nerd-stats',
121
+ }
122
+ }
123
+
124
+ /**
125
+ * @internal
126
+ */
127
+ override get events() {
128
+ return {
129
+ click: 'clicked',
130
+ 'click #nerd-stats-close': 'hide',
131
+ 'click #nerd-stats-refresh': 'refreshSpeedTest',
132
+ }
133
+ }
134
+
135
+ private clicked(e: MouseEvent) {
136
+ e.stopPropagation()
137
+ e.preventDefault()
138
+ }
139
+
140
+ private get statsBoxElem() {
141
+ return this.$el.find('#nerd-stats-box')
142
+ }
143
+
144
+ private get statsBoxWidthThreshold() {
145
+ return 720
146
+ }
147
+
148
+ private get playerWidth() {
149
+ return this.core.$el.width()
150
+ }
151
+
152
+ private get playerHeight() {
153
+ return this.core.$el.height()
154
+ }
155
+
156
+ constructor(core: Core) {
157
+ super(core)
158
+ this.shortcut = core.options.clapprNerdStats?.shortcut ?? [
159
+ 'command+shift+s',
160
+ 'ctrl+shift+s',
161
+ ]
162
+ this.iconPosition =
163
+ core.options.clapprNerdStats?.iconPosition ?? 'bottom-right'
164
+ this.speedtestMetrics = {
165
+ connectionSpeed: 0,
166
+ ping: 0,
167
+ jitter: 0,
168
+ }
169
+ configureSpeedTest(core.options.clapprNerdStats?.speedTestServers ?? [])
170
+ }
171
+
172
+ /**
173
+ * @internal
174
+ */
175
+ override bindEvents() {
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
+ )
183
+ }
184
+
185
+ private onCoreReady() {
186
+ const bottomGear = this.core.getPlugin('bottom_gear') as BottomGear
187
+ assert(bottomGear, 'bottom_gear plugin is required')
188
+ this.listenTo(bottomGear, GearEvents.RENDERED, this.attach)
189
+
190
+ Mousetrap.bind(this.shortcut, this.toggle)
191
+ this.updateResolution()
192
+ }
193
+
194
+ private onActiveContainerChanged() {
195
+ this.container = this.core.activeContainer
196
+ const clapprStats = this.container?.getPlugin('clappr_stats')
197
+ assert(
198
+ clapprStats,
199
+ 'clappr-stats not available. Please, include it as a plugin of your Clappr instance.\n' +
200
+ 'For more info, visit: https://github.com/clappr/clappr-stats.',
201
+ )
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
+ )
220
+ this.updateMetrics(clapprStats.exportMetrics())
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)
225
+ }
226
+
227
+ /**
228
+ * @internal
229
+ */
230
+ override destroy() {
231
+ Mousetrap.unbind(this.shortcut)
232
+ return super.destroy()
233
+ }
234
+
235
+ private toggle = () => {
236
+ if (this.open) {
237
+ this.hide()
238
+ } else {
239
+ this.show()
240
+ }
241
+ }
242
+
243
+ private show() {
244
+ this.$el.show()
245
+ this.statsBoxElem.scrollTop(this.statsBoxElem.scrollTop())
246
+ this.open = true
247
+
248
+ this.refreshSpeedTest()
249
+ initSpeedTest(this.speedtestMetrics)
250
+ .then(() => {
251
+ startSpeedtest()
252
+ })
253
+ .catch((e) => {
254
+ reportError(e)
255
+ this.disable()
256
+ })
257
+ }
258
+
259
+ private hide() {
260
+ this.$el.hide()
261
+ this.open = false
262
+ stopSpeedtest()
263
+ }
264
+
265
+ private onPlayerResize() {
266
+ this.setStatsBoxSize()
267
+ this.updateResolution()
268
+ }
269
+
270
+ private updateResolution() {
271
+ this.metrics.general.resolution = {
272
+ width: this.playerWidth,
273
+ height: this.playerHeight,
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)
281
+ }
282
+
283
+ private estimateQuality() {
284
+ trace(`${T} estimateQuality`)
285
+ const videoQualityNames = [
286
+ 'SD (480p)',
287
+ 'HD (720p)',
288
+ 'Full HD (1080p)',
289
+ '2K (1440p)',
290
+ '4K (2160p)',
291
+ ]
292
+ const { connectionSpeed, ping } = this.speedtestMetrics
293
+
294
+ if (!connectionSpeed || !ping) {
295
+ const calculatingText = this.core.i18n.t('stats.calculating')
296
+ this.metrics.custom.vodQuality = calculatingText
297
+ this.metrics.custom.liveQuality = calculatingText
298
+ return
299
+ }
300
+
301
+ const downloadQuality = getDownloadQuality(connectionSpeed)
302
+ const pingQuality = getPingQuality(ping)
303
+ const liveQuality = Math.min(downloadQuality, pingQuality)
304
+
305
+ const prefix = 'Optimal for '
306
+
307
+ this.metrics.custom.vodQuality =
308
+ prefix + videoQualityNames[downloadQuality - 1]
309
+ this.metrics.custom.liveQuality =
310
+ prefix + videoQualityNames[liveQuality - 1]
311
+ }
312
+
313
+ private updateMetrics(metrics: PerfMetrics) {
314
+ trace(`${T} updateMetrics`, { custom: this.speedtestMetrics })
315
+ Object.assign(this.metrics, metrics)
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))
382
+
383
+ this.setStatsBoxSize()
384
+ drawSpeedTestResults()
385
+ drawSummary(
386
+ this.speedtestMetrics,
387
+ this.$el.find('#nerd-stats-quality-vod'),
388
+ this.$el.find('#nerd-stats-quality-live'),
389
+ )
390
+
391
+ if (!this.open) {
392
+ this.hide()
393
+ }
394
+ }
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
+
406
+ private setStatsBoxSize() {
407
+ if (this.playerWidth >= this.statsBoxWidthThreshold) {
408
+ this.statsBoxElem.addClass('wide')
409
+ this.statsBoxElem.removeClass('narrow')
410
+ } else {
411
+ this.statsBoxElem.removeClass('wide')
412
+ this.statsBoxElem.addClass('narrow')
413
+ }
414
+ }
415
+
416
+ /**
417
+ * @internal
418
+ */
419
+ override render() {
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()
429
+
430
+ return this
431
+ }
432
+
433
+ private attach() {
434
+ trace(`${T} attach`)
435
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
436
+ gear
437
+ .addItem('nerd_stats')
438
+ .html(
439
+ NerdStats.buttonTemplate({
440
+ icon: statsIcon,
441
+ i18n: this.core.i18n,
442
+ }),
443
+ )
444
+ .on('click', (e: MouseEvent) => {
445
+ e.stopPropagation()
446
+ this.toggle()
447
+ })
448
+ }
449
+
450
+ private clearSpeedtestMetrics() {
451
+ const clapprStats = this.container?.getPlugin('clappr_stats')
452
+
453
+ this.speedtestMetrics.connectionSpeed = 0
454
+ this.speedtestMetrics.ping = 0
455
+ this.speedtestMetrics.jitter = 0
456
+
457
+ if (clapprStats) {
458
+ this.updateMetrics(clapprStats.exportMetrics())
459
+ }
460
+ }
461
+
462
+ private refreshSpeedTest() {
463
+ stopSpeedtest()
464
+ setTimeout(() => {
465
+ this.clearSpeedtestMetrics()
466
+ clearSpeedTestResults()
467
+ drawSpeedTestResults()
468
+ }, 200)
469
+ setTimeout(() => {
470
+ startSpeedtest()
471
+ }, 800)
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
+ }
484
+ }
485
+
486
+ function newMetrics(): Metrics {
487
+ return {
488
+ ...newBaseMetrics(),
489
+ general: {
490
+ displayResolution: '',
491
+ resolution: {
492
+ width: 0,
493
+ height: 0,
494
+ },
495
+ volume: 0,
496
+ },
497
+ custom: {
498
+ connectionSpeed: 0,
499
+ ping: 0,
500
+ jitter: 0,
501
+ },
502
+ }
503
+ }
@@ -1,109 +1,153 @@
1
- import humanFormat, { ScaleLike } from 'human-format';
2
- import type { MetricsKind, MetricsType } from './types';
1
+ import humanFormat, { ScaleLike } from 'human-format'
2
+ import type { MetricName, MetricsKind, MetricsType } from './types'
3
3
 
4
4
  const timeScale = new humanFormat.Scale({
5
5
  ms: 1,
6
6
  sec: 1000,
7
7
  min: 60000,
8
- hours: 3600000
9
- });
8
+ hours: 3600000,
9
+ })
10
10
 
11
11
  const percentScale = new humanFormat.Scale({
12
- '%': 1
13
- });
12
+ '%': 1,
13
+ })
14
14
 
15
15
  type FormatParams = {
16
- scale?: ScaleLike;
17
- unit?: 'bps';
18
- decimals?: number;
16
+ scale?: ScaleLike
17
+ unit?: 'bps'
18
+ decimals?: number
19
19
  }
20
20
 
21
- const formattingTemplate: Record<MetricsKind, Partial<Record<MetricsType, FormatParams>>> = {
21
+ const metricTemplates: Partial<Record<MetricName, FormatParams>> = {
22
+ fps: {
23
+ scale: 'SI',
24
+ decimals: 0,
25
+ },
26
+ volume: {
27
+ scale: percentScale,
28
+ },
29
+ }
30
+
31
+ const formattingTemplate: Record<
32
+ MetricsKind,
33
+ Partial<Record<MetricsType, FormatParams>>
34
+ > = {
22
35
  general: {
23
36
  volume: {
24
- scale: percentScale
25
- }
37
+ scale: percentScale,
38
+ },
26
39
  },
27
40
  timers: {
28
41
  startup: {
29
- scale: timeScale
42
+ scale: timeScale,
30
43
  },
31
44
  watch: {
32
- scale: timeScale
45
+ scale: timeScale,
33
46
  },
34
47
  pause: {
35
- scale: timeScale
48
+ scale: timeScale,
36
49
  },
37
50
  buffering: {
38
- scale: timeScale
51
+ scale: timeScale,
39
52
  },
40
53
  session: {
41
- scale: timeScale
54
+ scale: timeScale,
42
55
  },
43
56
  latency: {
44
- scale: timeScale
45
- }
57
+ scale: timeScale,
58
+ },
46
59
  },
47
60
  extra: {
48
61
  buffersize: {
49
- scale: timeScale
62
+ scale: timeScale,
50
63
  },
51
64
  duration: {
52
- scale: timeScale
65
+ scale: timeScale,
53
66
  },
54
67
  currentTime: {
55
- scale: timeScale
68
+ scale: timeScale,
56
69
  },
57
70
  bitrateWeightedMean: {
58
- unit: 'bps'
71
+ unit: 'bps',
59
72
  },
60
73
  bitrateMostUsed: {
61
- unit: 'bps'
74
+ unit: 'bps',
62
75
  },
63
76
  bandwidth: {
64
- unit: 'bps'
77
+ unit: 'bps',
65
78
  },
66
79
  watchedPercentage: {
67
- scale: percentScale
80
+ scale: percentScale,
68
81
  },
69
82
  bufferingPercentage: {
70
- scale: percentScale
71
- }
72
- }
73
- };
83
+ scale: percentScale,
84
+ },
85
+ },
86
+ }
74
87
 
75
- type MetricsValue = number | string;
76
- type Metrics = Partial<Record<MetricsKind, Partial<Record<MetricsType, MetricsValue>>>>;
88
+ type MetricsValue = number | string
89
+ type Metrics = Partial<
90
+ Record<MetricsKind, Partial<Record<MetricsType, MetricsValue>>>
91
+ >
77
92
 
78
93
  export default class Formatter {
79
94
  static format(metrics: Metrics): Metrics {
80
- const formattedMetrics: Metrics = {};
95
+ const formattedMetrics: Metrics = {}
81
96
 
82
97
  Object.entries(metrics).forEach(([type, mm]) => {
83
- const fmt: Partial<Record<MetricsType, MetricsValue>> = {};
84
- formattedMetrics[type as MetricsKind] = fmt;
85
- const typeTemplate = formattingTemplate[type as MetricsKind];
98
+ const fmt: Partial<Record<MetricsType, MetricsValue>> = {}
99
+ formattedMetrics[type as MetricsKind] = fmt
100
+ const typeTemplate = formattingTemplate[type as MetricsKind]
86
101
 
87
102
  Object.entries(mm).forEach(([name, value]) => {
88
- // const value = mm[name];
89
-
90
- if (typeTemplate && typeTemplate[name as MetricsType] && (typeof value === 'number') && !isNaN(value)) {
103
+ if (
104
+ typeTemplate &&
105
+ typeTemplate[name as MetricsType] &&
106
+ typeof value === 'number' &&
107
+ !isNaN(value)
108
+ ) {
91
109
  // @ts-ignore
92
- const templateScale = typeTemplate[name as MetricsType].scale || 'SI';
110
+ const templateScale = typeTemplate[name as MetricsType].scale || 'SI'
93
111
  // @ts-ignore
94
- const templateUnit = typeTemplate[name as MetricsType].unit || '';
112
+ const templateUnit = typeTemplate[name as MetricsType].unit || ''
95
113
 
96
114
  fmt[name as MetricsType] = humanFormat(value, {
97
115
  scale: templateScale,
98
116
  unit: templateUnit,
99
- decimals: 2
100
- });
117
+ decimals: 2,
118
+ })
101
119
  } else {
102
- fmt[name as MetricsType] = value;
120
+ fmt[name as MetricsType] = value
103
121
  }
104
- });
105
- });
122
+ })
123
+ })
124
+
125
+ return formattedMetrics
126
+ }
127
+
128
+ static formatVolume(volume: number): string {
129
+ return humanFormat(volume, metricTemplates.volume)
130
+ }
131
+
132
+ static formatTime(time: number): string {
133
+ return humanFormat(time, {
134
+ scale: timeScale,
135
+ })
136
+ }
137
+
138
+ static formatFps(fps: number): string {
139
+ return humanFormat(fps, metricTemplates.fps)
140
+ }
141
+
142
+ static formatPercentage(percentage: number): string {
143
+ return humanFormat(percentage, {
144
+ scale: percentScale,
145
+ })
146
+ }
106
147
 
107
- return formattedMetrics;
148
+ static formatBitrate(bitrate: number): string {
149
+ return humanFormat(bitrate, {
150
+ unit: 'bps',
151
+ })
108
152
  }
109
153
  }
@@ -1,5 +1,5 @@
1
1
  import { type Server, type TestStatusInfo, Speedtest } from './Speedtest.js';
2
- import { CustomMetrics } from './types.js';
2
+ import { SpeedtestMetrics } from './types.js';
3
3
 
4
4
  const DIGITS_THRESHOLD = 99999;
5
5
  const DEFAULT_DOWNLOAD_SPEED = '0.00';
@@ -59,7 +59,7 @@ export function drawSpeedTestResults() {
59
59
 
60
60
  let inited: Promise<void> | null = null;
61
61
 
62
- export const initSpeedTest = (customMetrics: CustomMetrics): Promise<void> => {
62
+ export const initSpeedTest = (customMetrics: SpeedtestMetrics): Promise<void> => {
63
63
  if (inited !== null) {
64
64
  return inited;
65
65
  }