@gcorevideo/player 2.22.0 → 2.22.2

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 (125) hide show
  1. package/assets/bottom-gear/bottomgear copy.ejs +10 -0
  2. package/assets/bottom-gear/bottomgear.ejs +4 -8
  3. package/assets/bottom-gear/gear-sub-menu.scss +0 -1
  4. package/assets/bottom-gear/gear.scss +0 -1
  5. package/assets/clappr-nerd-stats/button.ejs +3 -3
  6. package/assets/level-selector/button.ejs +2 -4
  7. package/assets/level-selector/list.ejs +14 -10
  8. package/assets/level-selector/style.scss +9 -4
  9. package/assets/media-control/container.scss +1 -1
  10. package/assets/playback-rate/list.ejs +5 -5
  11. package/assets/spinner-three-bounce/spinner.scss +1 -1
  12. package/dist/core.js +1 -2
  13. package/dist/index.css +885 -884
  14. package/dist/index.js +3938 -3779
  15. package/dist/player.d.ts +246 -108
  16. package/dist/plugins/index.css +1230 -1229
  17. package/dist/plugins/index.js +4036 -3878
  18. package/docs/api/player.bottomgear.additem.md +95 -0
  19. package/docs/api/player.bottomgear.md +63 -19
  20. package/docs/api/player.bottomgear.refresh.md +5 -1
  21. package/docs/api/player.clapprnerdstats.md +0 -2
  22. package/docs/api/player.clicktopause.md +1 -1
  23. package/docs/api/player.closedcaptions.md +2 -2
  24. package/docs/api/player.closedcaptionspluginsettings.md +5 -0
  25. package/docs/api/player.errorscreen.md +18 -4
  26. package/docs/api/player.errorscreenpluginsettings.md +1 -4
  27. package/docs/api/player.errorscreensettings.md +15 -0
  28. package/docs/api/{player.mediacontrolevents.md → player.gearevents.md} +7 -7
  29. package/docs/api/player.levelselector.events.md +0 -1
  30. package/docs/api/player.levelselector.md +1 -1
  31. package/docs/api/player.md +33 -36
  32. package/docs/api/{player.bottomgear.setcontent.md → player.mediacontrol.handlecustomarea.md} +5 -9
  33. package/docs/api/player.mediacontrol.md +10 -24
  34. package/docs/api/player.mediacontrol.putelement.md +2 -2
  35. package/docs/api/{player.bottomgear.getelement.md → player.mediacontrol.toggleelement.md} +23 -9
  36. package/docs/api/player.mediacontrolelement.md +1 -1
  37. package/docs/api/player.playbackrate.md +22 -3
  38. package/docs/api/{player.gearoptionsitem.md → player.playbackrateoption.md} +6 -4
  39. package/docs/api/{player.mediacontrol.getcenterpanel.md → player.playbackratesettings.md} +8 -6
  40. package/docs/api/player.sourcecontroller._constructor_.md +49 -0
  41. package/docs/api/player.sourcecontroller.md +70 -7
  42. package/docs/api/player.spinnerevents.md +1 -4
  43. package/docs/api/player.spinnerthreebounce._constructor_.md +0 -3
  44. package/docs/api/player.spinnerthreebounce.hide.md +0 -3
  45. package/docs/api/player.spinnerthreebounce.md +5 -8
  46. package/docs/api/player.spinnerthreebounce.show.md +2 -5
  47. package/lib/internal.types.d.ts +5 -0
  48. package/lib/internal.types.d.ts.map +1 -1
  49. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  50. package/lib/playback/dash-playback/DashPlayback.js +0 -1
  51. package/lib/playback.types.d.ts +0 -5
  52. package/lib/playback.types.d.ts.map +1 -1
  53. package/lib/plugins/bottom-gear/BottomGear.d.ts +93 -20
  54. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  55. package/lib/plugins/bottom-gear/BottomGear.js +145 -37
  56. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +2 -3
  57. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
  58. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +18 -15
  59. package/lib/plugins/click-to-pause/ClickToPause.d.ts +1 -1
  60. package/lib/plugins/click-to-pause/ClickToPause.d.ts.map +1 -1
  61. package/lib/plugins/click-to-pause/ClickToPause.js +3 -2
  62. package/lib/plugins/dvr-controls/DvrControls.js +1 -1
  63. package/lib/plugins/error-screen/ErrorScreen.d.ts +29 -4
  64. package/lib/plugins/error-screen/ErrorScreen.d.ts.map +1 -1
  65. package/lib/plugins/error-screen/ErrorScreen.js +17 -2
  66. package/lib/plugins/level-selector/LevelSelector.d.ts +8 -11
  67. package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
  68. package/lib/plugins/level-selector/LevelSelector.js +66 -102
  69. package/lib/plugins/media-control/MediaControl.d.ts +6 -15
  70. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  71. package/lib/plugins/media-control/MediaControl.js +36 -30
  72. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  73. package/lib/plugins/picture-in-picture/PictureInPicture.js +7 -2
  74. package/lib/plugins/playback-rate/PlaybackRate.d.ts +42 -14
  75. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  76. package/lib/plugins/playback-rate/PlaybackRate.js +101 -83
  77. package/lib/plugins/source-controller/SourceController.d.ts +40 -4
  78. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  79. package/lib/plugins/source-controller/SourceController.js +41 -4
  80. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +8 -6
  81. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  82. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +10 -6
  83. package/lib/plugins/subtitles/ClosedCaptions.d.ts +7 -7
  84. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
  85. package/lib/plugins/subtitles/ClosedCaptions.js +3 -3
  86. package/lib/testUtils.d.ts +1 -0
  87. package/lib/testUtils.d.ts.map +1 -1
  88. package/lib/testUtils.js +13 -0
  89. package/package.json +1 -1
  90. package/src/internal.types.ts +6 -0
  91. package/src/playback/dash-playback/DashPlayback.ts +0 -1
  92. package/src/playback.types.ts +0 -5
  93. package/src/plugins/bottom-gear/BottomGear.ts +186 -77
  94. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +21 -5
  95. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +5 -12
  96. package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +27 -25
  97. package/src/plugins/click-to-pause/ClickToPause.ts +3 -2
  98. package/src/plugins/dvr-controls/DvrControls.ts +1 -1
  99. package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +1 -1
  100. package/src/plugins/error-screen/ErrorScreen.ts +30 -4
  101. package/src/plugins/level-selector/LevelSelector.ts +80 -120
  102. package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +69 -79
  103. package/src/plugins/level-selector/__tests__/__snapshots__/LevelSelector.test.ts.snap +38 -71
  104. package/src/plugins/media-control/MediaControl.ts +50 -36
  105. package/src/plugins/media-control/__tests__/MediaControl.test.ts +4 -4
  106. package/src/plugins/picture-in-picture/PictureInPicture.ts +7 -2
  107. package/src/plugins/playback-rate/PlaybackRate.ts +136 -108
  108. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +84 -37
  109. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +55 -6
  110. package/src/plugins/source-controller/SourceController.ts +41 -4
  111. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +10 -6
  112. package/src/plugins/subtitles/ClosedCaptions.ts +9 -10
  113. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
  114. package/src/testUtils.ts +14 -0
  115. package/src/typings/vitest.d.ts +1 -0
  116. package/temp/player.api.json +303 -370
  117. package/tsconfig.tsbuildinfo +1 -1
  118. package/docs/api/player.gearitemelement.md +0 -18
  119. package/docs/api/player.mediacontrol.getleftpanel.md +0 -22
  120. package/docs/api/player.mediacontrol.getrightpanel.md +0 -22
  121. package/docs/api/player.subtitlespluginsettings.md +0 -18
  122. package/docs/api/player.texttrackitem.id.md +0 -11
  123. package/docs/api/player.texttrackitem.md +0 -87
  124. package/docs/api/player.texttrackitem.name.md +0 -11
  125. package/docs/api/player.texttrackitem.track.md +0 -11
@@ -129,7 +129,7 @@ export class DvrControls extends UICorePlugin {
129
129
  i18n: this.core.i18n,
130
130
  }),
131
131
  )
132
- mediaControl.putElement('dvr', this.el)
132
+ mediaControl.putElement('dvr', this.$el)
133
133
 
134
134
  return this
135
135
  }
@@ -50,7 +50,7 @@ describe('DvrControls', () => {
50
50
  expect(mediaControl.toggleElement).toHaveBeenCalledWith('position', false)
51
51
  })
52
52
  it('should render to the media control', () => {
53
- expect(mediaControl.putElement).toHaveBeenCalledWith('dvr', dvrControls.el)
53
+ expect(mediaControl.putElement).toHaveBeenCalledWith('dvr', dvrControls.$el)
54
54
  })
55
55
  })
56
56
  describe('when back_to_live button is clicked', () => {
@@ -16,8 +16,19 @@ type ErrorScreenDesc = {
16
16
  }
17
17
 
18
18
  /**
19
- * Configuration options for the {@link ErrorScreen | error screen} plugin.
20
- * @beta
19
+ * Settings for the {@link ErrorScreen} plugin.
20
+ * @public
21
+ */
22
+ export type ErrorScreenSettings = {
23
+ /**
24
+ * Whether to hide the reload button. The reload button triggers reload of the current source.
25
+ */
26
+ noReload?: boolean
27
+ }
28
+
29
+ /**
30
+ * Configuration options for the {@link ErrorScreen} plugin.
31
+ * @public
21
32
  */
22
33
  export type ErrorScreenPluginSettings = {
23
34
  /**
@@ -29,8 +40,23 @@ export type ErrorScreenPluginSettings = {
29
40
  const T = 'plugins.error_screen'
30
41
 
31
42
  /**
32
- * `PLUGIN` that displays errors nicely in the overlay on top of the player.
33
- * @beta
43
+ * `PLUGIN` that displays fatal errors nicely in the overlay on top of the player.
44
+ * @public
45
+ * @remarks
46
+ * A fatal error is an error that prevents the player from playing the content.
47
+ * It's usually a network error that persists after multiple retries.
48
+ *
49
+ * The error screen should not be confused with the content stub that is shown when no media sources are available.
50
+ * This can happen due to the lack of the support of the given sources type or because the sources are misconfigured (e.g., omitted).
51
+ *
52
+ * Configuration options - {@link ErrorScreenPluginSettings}
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { ErrorScreen, Player } from '@gcorevideo/player'
57
+ *
58
+ * Player.registerPlugin(ErrorScreen)
59
+ * ```
34
60
  */
35
61
  export class ErrorScreen extends UICorePlugin {
36
62
  private err: ErrorScreenDesc | null = null
@@ -6,7 +6,7 @@ import { type QualityLevel } from '../../playback.types.js'
6
6
  import { CLAPPR_VERSION } from '../../build.js'
7
7
  import { ZeptoResult } from '../../types.js'
8
8
  import { TemplateFunction } from '../types.js'
9
- import { BottomGear } from '../bottom-gear/BottomGear.js'
9
+ import { BottomGear, GearEvents } from '../bottom-gear/BottomGear.js'
10
10
 
11
11
  import buttonHtml from '../../../assets/level-selector/button.ejs'
12
12
  import listHtml from '../../../assets/level-selector/list.ejs'
@@ -15,7 +15,7 @@ import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg'
15
15
  import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg'
16
16
  import checkIcon from '../../../assets/icons/new/check.svg'
17
17
  import '../../../assets/level-selector/style.scss'
18
- import { MediaControl, MediaControlEvents } from '../media-control/MediaControl.js'
18
+ import { MediaControl } from '../media-control/MediaControl.js'
19
19
 
20
20
  const T = 'plugins.level_selector'
21
21
  const VERSION = '2.19.4'
@@ -73,7 +73,9 @@ export class LevelSelector extends UICorePlugin {
73
73
 
74
74
  private isHd = false
75
75
 
76
- private isOpen = false
76
+ private currentText = ''
77
+
78
+ private selectedLevelId = -1
77
79
 
78
80
  private static readonly buttonTemplate: TemplateFunction =
79
81
  template(buttonHtml)
@@ -111,14 +113,9 @@ export class LevelSelector extends UICorePlugin {
111
113
  }
112
114
  }
113
115
 
114
- private currentText = 'Auto'
115
-
116
- private selectedLevelId = -1
117
-
118
116
  override get events() {
119
117
  return {
120
- 'click .gear-sub-menu_btn': 'onLevelSelect',
121
- 'click .gear-option': 'onShowLevelSelectMenu',
118
+ 'click .gear-sub-menu_btn': 'onSelect',
122
119
  'click .go-back': 'goBack',
123
120
  }
124
121
  }
@@ -127,27 +124,29 @@ export class LevelSelector extends UICorePlugin {
127
124
  * @internal
128
125
  */
129
126
  override bindEvents() {
130
- this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
127
+ this.listenToOnce(this.core, Events.CORE_READY, this.onCoreReady)
131
128
  this.listenTo(
132
129
  this.core,
133
130
  Events.CORE_ACTIVE_CONTAINER_CHANGED,
134
- this.bindPlaybackEvents,
131
+ this.onActiveContainerChange,
135
132
  )
136
133
  }
137
134
 
138
135
  private onCoreReady() {
139
- trace(`${T} onCoreReady`);
140
- const mediaControl = this.core.getPlugin('media_control') as MediaControl
141
- assert(mediaControl, 'media_control plugin is required')
142
- this.listenTo(mediaControl, MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, this.onGearRendered);
136
+ trace(`${T} onCoreReady`)
137
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
138
+ assert(gear, 'bottom_gear plugin is required')
139
+
140
+ this.currentText = this.core.i18n.t('auto')
141
+ this.listenTo(gear, GearEvents.RENDERED, this.onGearRendered)
143
142
  }
144
143
 
145
144
  private onGearRendered() {
146
- trace(`${T} onGearRendered`);
147
- this.deferRender();
145
+ trace(`${T} onGearRendered`)
146
+ this.render()
148
147
  }
149
148
 
150
- private bindPlaybackEvents() {
149
+ private onActiveContainerChange() {
151
150
  this.removeAuto = false
152
151
  this.isHd = false
153
152
 
@@ -156,7 +155,7 @@ export class LevelSelector extends UICorePlugin {
156
155
  this.listenTo(
157
156
  activePlayback,
158
157
  Events.PLAYBACK_LEVELS_AVAILABLE,
159
- this.fillLevels,
158
+ this.onLevelsAvailable,
160
159
  )
161
160
  this.listenTo(
162
161
  activePlayback,
@@ -168,31 +167,32 @@ export class LevelSelector extends UICorePlugin {
168
167
  Events.PLAYBACK_LEVEL_SWITCH_END,
169
168
  this.onLevelSwitchEnd,
170
169
  )
171
- this.listenTo(
172
- activePlayback,
173
- Events.PLAYBACK_BITRATE,
174
- this.updateCurrentLevel,
175
- )
170
+ this.listenTo(activePlayback, Events.PLAYBACK_BITRATE, this.onBitrate)
176
171
  this.listenTo(activePlayback, Events.PLAYBACK_STOP, this.onStop)
177
172
  this.listenTo(
178
173
  activePlayback,
179
174
  Events.PLAYBACK_HIGHDEFINITIONUPDATE,
180
175
  (isHd: boolean) => {
181
176
  this.isHd = isHd
182
- this.deferRender()
177
+ this.updateHd()
183
178
  },
184
179
  )
185
180
  if (activePlayback.levels?.length > 0) {
186
- this.fillLevels(activePlayback.levels)
181
+ this.onLevelsAvailable(activePlayback.levels)
182
+ }
183
+ }
184
+
185
+ private updateHd() {
186
+ if (this.isHd) {
187
+ this.$el.find('.gear-option_hd-icon').removeClass('hidden')
188
+ } else {
189
+ this.$el.find('.gear-option_hd-icon').addClass('hidden')
187
190
  }
188
191
  }
189
192
 
190
193
  private onStop() {
191
194
  trace(`${T} onStop`)
192
195
  this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, () => {
193
- trace(`${T} on PLAYBACK_PLAY after stop`, {
194
- selectedLevelId: this.selectedLevelId,
195
- })
196
196
  if (this.core.activePlayback.getPlaybackType() === 'live') {
197
197
  if (this.selectedLevelId !== -1) {
198
198
  this.core.activePlayback.currentLevel = this.selectedLevelId
@@ -222,56 +222,53 @@ export class LevelSelector extends UICorePlugin {
222
222
  if (!this.shouldRender()) {
223
223
  return this
224
224
  }
225
-
226
- this.renderButton()
225
+ this.renderDropdown()
226
+ this.updateButton()
227
227
 
228
228
  return this
229
229
  }
230
230
 
231
- private renderButton() {
232
- if (!this.isOpen) {
233
- const html = LevelSelector.buttonTemplate({
234
- arrowRightIcon,
235
- currentText: this.currentText,
236
- isHd: this.isHd,
237
- hdIcon,
238
- })
239
- this.$el.html(html)
240
- const gear = this.core.getPlugin('bottom_gear') as BottomGear
241
- if (!gear) {
242
- trace(`${T} renderButton: bottom_gear plugin not found`)
243
- }
244
- gear?.getElement('quality')?.html(this.el)
245
- }
231
+ private renderDropdown() {
232
+ this.$el.html(
233
+ LevelSelector.listTemplate({
234
+ arrowLeftIcon,
235
+ checkIcon,
236
+ current: this.selectedLevelId,
237
+ labels: this.levelLabels,
238
+ levels: this.levels,
239
+ maxLevel: this.maxLevel,
240
+ removeAuto: this.removeAuto,
241
+ i18n: this.core.i18n,
242
+ }),
243
+ )
246
244
  }
247
245
 
248
- private renderDropdown() {
249
- const html = LevelSelector.listTemplate({
250
- arrowLeftIcon,
251
- checkIcon,
252
- labels: this.levelLabels,
253
- levels: this.levels,
254
- maxLevel: this.maxLevel,
255
- removeAuto: this.removeAuto,
256
- })
257
- this.$el.html(html)
258
- const gear = this.core.getPlugin('bottom_gear') as BottomGear
259
- trace(`${T} renderDropdown: bottom_gear plugin not found`)
260
- gear?.setContent(this.el)
246
+ private updateButton() {
247
+ ;(this.core.getPlugin('bottom_gear') as BottomGear)
248
+ ?.addItem('quality', this.$el)
249
+ .html(
250
+ LevelSelector.buttonTemplate({
251
+ arrowRightIcon,
252
+ currentText: this.currentText,
253
+ isHd: this.isHd,
254
+ hdIcon,
255
+ i18n: this.core.i18n,
256
+ }),
257
+ )
261
258
  }
262
259
 
263
260
  private get maxLevel() {
264
261
  const maxRes = this.core.options.levelSelector?.restrictResolution
265
262
  return maxRes
266
- ? this.levels.findIndex(
263
+ ? this.levels.find(
267
264
  (level) =>
268
265
  (level.height > level.width ? level.width : level.height) ===
269
266
  maxRes,
270
- )
267
+ )?.level ?? -1
271
268
  : -1
272
269
  }
273
270
 
274
- private fillLevels(levels: QualityLevel[]) {
271
+ private onLevelsAvailable(levels: QualityLevel[]) {
275
272
  const maxResolution = this.core.options.levelSelector?.restrictResolution
276
273
  this.levels = levels
277
274
  this.makeLevelsLabels()
@@ -286,77 +283,54 @@ export class LevelSelector extends UICorePlugin {
286
283
  .pop()
287
284
  this.setLevel(initialLevel?.level ?? 0)
288
285
  }
289
- this.deferRender()
286
+ this.render()
290
287
  }
291
288
 
292
289
  private makeLevelsLabels() {
293
290
  const labels = this.core.options.levelSelector?.labels ?? {}
294
291
  this.levelLabels = []
295
292
 
296
- for (let i = 0; i < this.levels.length; i++) {
297
- const level = this.levels[i]
293
+ for (const level of this.levels) {
298
294
  const ll = level.width > level.height ? level.height : level.width
299
295
  const label = labels[ll] || `${ll}p`
300
296
  this.levelLabels.push(label)
301
297
  }
302
298
  }
303
299
 
304
- private onLevelSelect(event: MouseEvent) {
300
+ private onSelect(event: MouseEvent) {
305
301
  const selectedLevel = parseInt(
306
302
  (event.currentTarget as HTMLElement)?.dataset?.id ?? '-1',
307
303
  10,
308
304
  )
309
305
  this.setLevel(selectedLevel)
306
+
310
307
  event.stopPropagation()
308
+ event.preventDefault()
311
309
  return false
312
310
  }
313
311
 
314
312
  private goBack() {
315
313
  trace(`${T} goBack`)
316
- this.isOpen = false
317
- setTimeout(() => {
318
- this.core.getPlugin('bottom_gear').refresh()
319
- }, 0);
314
+ this.core.getPlugin('bottom_gear').refresh()
320
315
  }
321
316
 
322
317
  private setLevel(index: number) {
323
- trace(`${T} setIndexLevel`, { index })
324
318
  this.selectedLevelId = index
325
- if (!this.core.activePlayback) {
326
- return
327
- }
328
- if (this.core.activePlayback.currentLevel === this.selectedLevelId) {
329
- return
330
- }
331
319
  this.core.activePlayback.currentLevel = this.selectedLevelId
332
-
333
- try {
334
- this.highlightCurrentLevel()
335
- } catch (error) {
336
- reportError(error)
337
- }
338
- this.deferRender()
339
- }
340
-
341
- private onShowLevelSelectMenu() {
342
- trace(`${T} onShowLevelSelectMenu`)
343
- this.isOpen = true
344
- this.renderDropdown()
345
320
  this.highlightCurrentLevel()
346
321
  }
347
322
 
348
323
  private allLevelElements() {
349
- return this.$('ul.gear-sub-menu li') as ZeptoResult
324
+ return this.$('#level-selector-menu li') as ZeptoResult
350
325
  }
351
326
 
352
327
  private levelElement(id = -1) {
353
328
  return (
354
- this.$(`ul.gear-sub-menu a[data-id="${id}"]`) as ZeptoResult
329
+ this.$(`#level-selector-menu a[data-id="${id}"]`) as ZeptoResult
355
330
  ).parent()
356
331
  }
357
332
 
358
333
  private onLevelSwitchStart() {
359
- this.core.activePlayback.trigger('playback:level:select:start')
360
334
  this.levelElement(this.selectedLevelId).addClass('changing')
361
335
  }
362
336
 
@@ -365,25 +339,22 @@ export class LevelSelector extends UICorePlugin {
365
339
  }
366
340
 
367
341
  private updateText(level: number) {
368
- if (level === undefined || isNaN(level)) {
369
- return
370
- }
371
342
  this.currentText = this.getLevelLabel(level)
372
- this.deferRender()
343
+ this.updateButton()
373
344
  }
374
345
 
375
346
  private getLevelLabel(id: number): string {
376
- if (id === -1) {
377
- return 'Auto'
347
+ if (id < 0) {
348
+ return this.core.i18n.t('auto')
378
349
  }
379
350
  const index = this.levels.findIndex((l) => l.level === id)
380
351
  if (index < 0) {
381
- return 'Auto'
352
+ return this.core.i18n.t('auto')
382
353
  }
383
354
  return this.levelLabels[index] ?? formatLevelLabel(this.levels[index])
384
355
  }
385
356
 
386
- private updateCurrentLevel(info: QualityLevel) {
357
+ private onBitrate(info: QualityLevel) {
387
358
  trace(`${T} updateCurrentLevel`, { info })
388
359
  this.highlightCurrentLevel()
389
360
  }
@@ -392,34 +363,23 @@ export class LevelSelector extends UICorePlugin {
392
363
  trace(`${T} highlightCurrentLevel`, {
393
364
  selectedLevelId: this.selectedLevelId,
394
365
  })
395
- this.allLevelElements().removeClass('current')
396
- this.allLevelElements().find('a').removeClass('gcore-skin-active')
366
+ this.allLevelElements()
367
+ .removeClass('current')
368
+ .find('a')
369
+ .removeClass('gcore-skin-active')
397
370
 
398
371
  const currentLevelElement = this.levelElement(this.selectedLevelId)
399
372
 
400
- currentLevelElement.addClass('current')
401
- currentLevelElement.find('a').addClass('gcore-skin-active')
373
+ currentLevelElement
374
+ .addClass('current')
375
+ .find('a')
376
+ .addClass('gcore-skin-active')
402
377
 
403
378
  this.updateText(this.selectedLevelId)
404
379
  }
405
-
406
- private deferRender = debounce(() => this.render(), 0)
407
380
  }
408
381
 
409
382
  function formatLevelLabel(level: QualityLevel): string {
410
383
  const h = level.width > level.height ? level.height : level.width
411
384
  return `${h}p`
412
385
  }
413
-
414
- function debounce(fn: () => void, wait: number) {
415
- let timerId: ReturnType<typeof setTimeout> | null = null
416
- return function () {
417
- if (timerId !== null) {
418
- clearTimeout(timerId)
419
- }
420
- timerId = setTimeout(() => {
421
- timerId = null
422
- fn()
423
- }, wait)
424
- }
425
- }