@gcorevideo/player 2.21.3 → 2.21.4

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 (89) hide show
  1. package/assets/audio-selector/style.scss +1 -1
  2. package/assets/audio-selector/track-selector.ejs +3 -3
  3. package/assets/bottom-gear/bottomgear.ejs +2 -2
  4. package/assets/media-control/container.scss +1 -1
  5. package/assets/media-control/media-control.ejs +1 -6
  6. package/assets/media-control/media-control.scss +6 -4
  7. package/assets/media-control/width270.scss +1 -1
  8. package/assets/media-control/width370.scss +4 -4
  9. package/assets/playback-rate/button.ejs +2 -2
  10. package/assets/playback-rate/list.ejs +4 -4
  11. package/assets/subtitles/combobox.ejs +5 -5
  12. package/assets/subtitles/string.ejs +1 -1
  13. package/assets/subtitles/style.scss +2 -2
  14. package/dist/core.js +2 -1
  15. package/dist/index.css +972 -967
  16. package/dist/index.js +126 -106
  17. package/dist/player.d.ts +141 -119
  18. package/dist/plugins/index.css +801 -796
  19. package/dist/plugins/index.js +119 -104
  20. package/docs/api/player.bottomgear.getelement.md +2 -2
  21. package/docs/api/player.bottomgear.md +1 -1
  22. package/docs/api/{player.subtitles.hide.md → player.closedcaptions.hide.md} +2 -2
  23. package/docs/api/{player.subtitles.md → player.closedcaptions.md} +11 -11
  24. package/docs/api/{player.subtitles.show.md → player.closedcaptions.show.md} +2 -2
  25. package/docs/api/player.closedcaptionspluginsettings.md +13 -0
  26. package/docs/api/player.gearitemelement.md +6 -4
  27. package/docs/api/player.gearoptionsitem.md +16 -0
  28. package/docs/api/player.md +48 -12
  29. package/docs/api/player.mediacontrol.putelement.md +2 -2
  30. package/docs/api/player.mediacontrolelement.md +1 -1
  31. package/docs/api/player.playbackrate.md +1 -1
  32. package/docs/api/player.subtitlespluginsettings.md +18 -0
  33. package/docs/api/player.texttrackitem.id.md +11 -0
  34. package/docs/api/player.texttrackitem.md +87 -0
  35. package/docs/api/player.texttrackitem.name.md +11 -0
  36. package/docs/api/player.texttrackitem.track.md +11 -0
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.js +1 -1
  39. package/lib/index.plugins.d.ts +2 -1
  40. package/lib/index.plugins.d.ts.map +1 -1
  41. package/lib/index.plugins.js +2 -1
  42. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  43. package/lib/playback/dash-playback/DashPlayback.js +1 -0
  44. package/lib/plugins/audio-selector/AudioSelector.d.ts +2 -3
  45. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  46. package/lib/plugins/audio-selector/AudioSelector.js +6 -7
  47. package/lib/plugins/bottom-gear/BottomGear.d.ts +6 -2
  48. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  49. package/lib/plugins/bottom-gear/BottomGear.js +2 -1
  50. package/lib/plugins/media-control/MediaControl.d.ts +5 -5
  51. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  52. package/lib/plugins/media-control/MediaControl.js +46 -39
  53. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +1 -0
  54. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  55. package/lib/plugins/picture-in-picture/PictureInPicture.js +4 -4
  56. package/lib/plugins/playback-rate/PlaybackRate.d.ts +0 -1
  57. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  58. package/lib/plugins/playback-rate/PlaybackRate.js +23 -14
  59. package/lib/plugins/subtitles/ClosedCaptions.d.ts +118 -0
  60. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -0
  61. package/lib/plugins/subtitles/ClosedCaptions.js +348 -0
  62. package/lib/plugins/subtitles/Subtitles.d.ts +12 -9
  63. package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
  64. package/lib/plugins/subtitles/Subtitles.js +31 -32
  65. package/lib/testUtils.d.ts +22 -18
  66. package/lib/testUtils.d.ts.map +1 -1
  67. package/lib/testUtils.js +22 -36
  68. package/package.json +1 -1
  69. package/src/index.plugins.ts +2 -1
  70. package/src/index.ts +1 -1
  71. package/src/playback/dash-playback/DashPlayback.ts +1 -0
  72. package/src/plugins/audio-selector/AudioSelector.ts +9 -8
  73. package/src/plugins/bottom-gear/BottomGear.ts +11 -4
  74. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -1
  75. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +2 -2
  76. package/src/plugins/media-control/MediaControl.ts +53 -46
  77. package/src/plugins/media-control/__tests__/MediaControl.test.ts +43 -0
  78. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +175 -0
  79. package/src/plugins/picture-in-picture/PictureInPicture.ts +5 -5
  80. package/src/plugins/playback-rate/PlaybackRate.ts +142 -100
  81. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +65 -0
  82. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +11 -0
  83. package/src/plugins/subtitles/{Subtitles.ts → ClosedCaptions.ts} +42 -34
  84. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +58 -0
  85. package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +25 -0
  86. package/src/testUtils.ts +22 -36
  87. package/temp/player.api.json +269 -89
  88. package/tsconfig.tsbuildinfo +1 -1
  89. package/src/plugins/index.ts +0 -39
@@ -0,0 +1,175 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`MediaControl > putElement > cc > should put the element in the right panel 1`] = `
4
+ "<div class="media-control-background" data-background=""></div>
5
+
6
+ <div class="media-control-layer gcore-skin-bg-color" data-controls="">
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+ <div class="media-control-left-panel" data-media-control="">
21
+
22
+ <div class="drawer-container" data-volume="">
23
+ <div class="drawer-icon-container" data-volume="">
24
+ <div class="drawer-icon media-control-icon gcore-skin-button-color" data-volume="">/assets/icons/new/volume-max.svg</div>
25
+ <span class="drawer-text" data-volume=""></span>
26
+ </div>
27
+
28
+ <div class="bar-container" data-volume="">
29
+ <div class="bar-background" data-volume="">
30
+ <div class="bar-fill-1 gcore-skin-main-color" data-volume="" style=""></div>
31
+ </div>
32
+ <div class="bar-scrubber" data-volume="" style="">
33
+ <div class="bar-scrubber-icon gcore-skin-main-color" data-volume=""></div>
34
+ </div>
35
+ </div>
36
+
37
+ </div>
38
+
39
+
40
+ <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
41
+ <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
42
+ <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
43
+ </div>
44
+
45
+ </div>
46
+
47
+
48
+
49
+ <div class="media-control-right-panel" data-media-control="">
50
+
51
+ <div class="media-control-multicamera" data-multicamera=""></div>
52
+
53
+ <div class="media-control-vr" data-vr=""></div>
54
+
55
+ <div class="my-media-control" data-cc="">test</div></div>
56
+
57
+ </div>
58
+ <style>:root {}</style>"
59
+ `;
60
+
61
+ exports[`MediaControl > putElement > gear > should put the element in the right panel 1`] = `
62
+ "<div class="media-control-background" data-background=""></div>
63
+
64
+ <div class="media-control-layer gcore-skin-bg-color" data-controls="">
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+ <div class="media-control-left-panel" data-media-control="">
79
+
80
+ <div class="drawer-container" data-volume="">
81
+ <div class="drawer-icon-container" data-volume="">
82
+ <div class="drawer-icon media-control-icon gcore-skin-button-color" data-volume="">/assets/icons/new/volume-max.svg</div>
83
+ <span class="drawer-text" data-volume=""></span>
84
+ </div>
85
+
86
+ <div class="bar-container" data-volume="">
87
+ <div class="bar-background" data-volume="">
88
+ <div class="bar-fill-1 gcore-skin-main-color" data-volume="" style=""></div>
89
+ </div>
90
+ <div class="bar-scrubber" data-volume="" style="">
91
+ <div class="bar-scrubber-icon gcore-skin-main-color" data-volume=""></div>
92
+ </div>
93
+ </div>
94
+
95
+ </div>
96
+
97
+
98
+ <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
99
+ <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
100
+ <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
101
+ </div>
102
+
103
+ </div>
104
+
105
+
106
+
107
+ <div class="media-control-right-panel" data-media-control="">
108
+
109
+ <div class="media-control-multicamera" data-multicamera=""></div>
110
+
111
+ <div class="media-control-vr" data-vr=""></div>
112
+
113
+ <div class="my-media-control" data-gear="">test</div></div>
114
+
115
+ </div>
116
+ <style>:root {}</style>"
117
+ `;
118
+
119
+ exports[`MediaControl > putElement > pip > should put the element in the right panel 1`] = `
120
+ "<div class="media-control-background" data-background=""></div>
121
+
122
+ <div class="media-control-layer gcore-skin-bg-color" data-controls="">
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+ <div class="media-control-left-panel" data-media-control="">
137
+
138
+ <div class="drawer-container" data-volume="">
139
+ <div class="drawer-icon-container" data-volume="">
140
+ <div class="drawer-icon media-control-icon gcore-skin-button-color" data-volume="">/assets/icons/new/volume-max.svg</div>
141
+ <span class="drawer-text" data-volume=""></span>
142
+ </div>
143
+
144
+ <div class="bar-container" data-volume="">
145
+ <div class="bar-background" data-volume="">
146
+ <div class="bar-fill-1 gcore-skin-main-color" data-volume="" style=""></div>
147
+ </div>
148
+ <div class="bar-scrubber" data-volume="" style="">
149
+ <div class="bar-scrubber-icon gcore-skin-main-color" data-volume=""></div>
150
+ </div>
151
+ </div>
152
+
153
+ </div>
154
+
155
+
156
+ <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
157
+ <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
158
+ <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
159
+ </div>
160
+
161
+ </div>
162
+
163
+
164
+
165
+ <div class="media-control-right-panel" data-media-control="">
166
+
167
+ <div class="media-control-multicamera" data-multicamera=""></div>
168
+
169
+ <div class="media-control-vr" data-vr=""></div>
170
+
171
+ <div class="my-media-control" data-pip="">test</div></div>
172
+
173
+ </div>
174
+ <style>:root {}</style>"
175
+ `;
@@ -43,6 +43,8 @@ export class PictureInPicture extends UICorePlugin {
43
43
  return VERSION;
44
44
  }
45
45
 
46
+ private static buttonTemplate = template(buttonHtml);
47
+
46
48
  /**
47
49
  * @internal
48
50
  */
@@ -71,8 +73,8 @@ export class PictureInPicture extends UICorePlugin {
71
73
 
72
74
  private isPiPSupported() {
73
75
  trace(`${T} isPiPSupported`, {
74
- pictureInPictureEnabled: document.pictureInPictureEnabled,
75
- requestPictureInPicture: HTMLVideoElement.prototype.requestPictureInPicture,
76
+ pictureInPictureEnabled: !!document.pictureInPictureEnabled,
77
+ requestPictureInPicture: !!HTMLVideoElement.prototype.requestPictureInPicture,
76
78
  });
77
79
 
78
80
  return document.pictureInPictureEnabled && !!HTMLVideoElement.prototype.requestPictureInPicture;
@@ -86,9 +88,7 @@ export class PictureInPicture extends UICorePlugin {
86
88
  return this;
87
89
  }
88
90
 
89
- const t = template(buttonHtml);
90
-
91
- this.$el.html(t({ pipIcon }));
91
+ this.$el.html(PictureInPicture.buttonTemplate({ pipIcon }));
92
92
 
93
93
  const mediaControl = this.core.getPlugin('media_control');
94
94
  if (mediaControl) {
@@ -1,23 +1,26 @@
1
- import { Events, UICorePlugin, Playback, template, Core } from '@clappr/core';
2
- import { trace } from '@gcorevideo/utils';
3
- import assert from 'assert';
4
-
5
- import { CLAPPR_VERSION } from '../../build.js';
6
- import type { ZeptoResult } from '../../types.js';
7
-
8
- import buttonHtml from '../../../assets/playback-rate/button.ejs';
9
- import listHtml from '../../../assets/playback-rate/list.ejs';
10
- import speedIcon from '../../../assets/icons/new/speed.svg';
11
- import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg';
12
- import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg';
13
- import checkIcon from '../../../assets/icons/new/check.svg';
14
- import { BottomGear } from '../bottom-gear/BottomGear.js';
15
- import { PlaybackEvents } from '../../playback/types.js';
16
- import { MediaControl, MediaControlEvents } from '../media-control/MediaControl.js';
1
+ import { Events, UICorePlugin, Playback, template, Core } from '@clappr/core'
2
+ import { trace } from '@gcorevideo/utils'
3
+ import assert from 'assert'
4
+
5
+ import { CLAPPR_VERSION } from '../../build.js'
6
+ import type { ZeptoResult } from '../../types.js'
7
+
8
+ import buttonHtml from '../../../assets/playback-rate/button.ejs'
9
+ import listHtml from '../../../assets/playback-rate/list.ejs'
10
+ import speedIcon from '../../../assets/icons/new/speed.svg'
11
+ import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg'
12
+ import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg'
13
+ import checkIcon from '../../../assets/icons/new/check.svg'
14
+ import { BottomGear } from '../bottom-gear/BottomGear.js'
15
+ import { PlaybackEvents } from '../../playback/types.js'
16
+ import {
17
+ MediaControl,
18
+ MediaControlEvents,
19
+ } from '../media-control/MediaControl.js'
17
20
 
18
21
  type PlaybackRateOption = {
19
- value: string;
20
- label: string;
22
+ value: string
23
+ label: string
21
24
  }
22
25
 
23
26
  const DEFAULT_PLAYBACK_RATES = [
@@ -27,12 +30,12 @@ const DEFAULT_PLAYBACK_RATES = [
27
30
  { value: '1.25', label: '1.25x' },
28
31
  { value: '1.5', label: '1.5x' },
29
32
  { value: '1.75', label: '1.75x' },
30
- { value: '2.0', label: '2x' }
31
- ];
33
+ { value: '2.0', label: '2x' },
34
+ ]
32
35
 
33
- const DEFAULT_PLAYBACK_RATE = '1.0';
36
+ const DEFAULT_PLAYBACK_RATE = '1.0'
34
37
 
35
- const T = 'plugins.playback_rate';
38
+ const T = 'plugins.playback_rate'
36
39
 
37
40
  /**
38
41
  * `PLUGIN` that allows changing the playback speed of the video.
@@ -49,37 +52,39 @@ const T = 'plugins.playback_rate';
49
52
  * Note that the playback rate change is supported only for VOD or DVR enabled live streams.
50
53
  */
51
54
  export class PlaybackRate extends UICorePlugin {
52
- private playbackRates: PlaybackRateOption[] = DEFAULT_PLAYBACK_RATES;
55
+ private playbackRates: PlaybackRateOption[] = DEFAULT_PLAYBACK_RATES
53
56
 
54
57
  // Saved when an ad starts to restore after it finishes
55
- private prevSelectedRate: string | undefined;
58
+ private prevSelectedRate: string | undefined
56
59
 
57
- private rendered = false;
60
+ private rendered = false
58
61
 
59
- private selectedRate: string = DEFAULT_PLAYBACK_RATE;
62
+ private selectedRate: string = DEFAULT_PLAYBACK_RATE
60
63
 
61
64
  /**
62
65
  * @internal
63
66
  */
64
67
  get name() {
65
- return 'playback_rate';
68
+ return 'playback_rate'
66
69
  }
67
70
 
68
71
  /**
69
72
  * @internal
70
73
  */
71
74
  get supportedVersion() {
72
- return { min: CLAPPR_VERSION };
75
+ return { min: CLAPPR_VERSION }
73
76
  }
74
77
 
75
- private static readonly buttonTemplate = template(buttonHtml);
78
+ private static readonly buttonTemplate = template(buttonHtml)
76
79
 
77
- private static readonly listTemplate = template(listHtml);
80
+ private static readonly listTemplate = template(listHtml)
78
81
 
79
82
  constructor(core: Core) {
80
- super(core);
81
- this.playbackRates = core.options.playbackRate?.options || DEFAULT_PLAYBACK_RATES;
82
- this.selectedRate = core.options.playbackRate?.defaultValue || DEFAULT_PLAYBACK_RATE;
83
+ super(core)
84
+ this.playbackRates =
85
+ core.options.playbackRate?.options || DEFAULT_PLAYBACK_RATES
86
+ this.selectedRate =
87
+ core.options.playbackRate?.defaultValue || DEFAULT_PLAYBACK_RATE
83
88
  }
84
89
 
85
90
  /**
@@ -87,9 +92,8 @@ export class PlaybackRate extends UICorePlugin {
87
92
  */
88
93
  override get attributes() {
89
94
  return {
90
- 'class': this.name,
91
- 'data-playback-rate-select': ''
92
- };
95
+ class: 'media-control-playbackrate',
96
+ }
93
97
  }
94
98
 
95
99
  /**
@@ -100,38 +104,56 @@ export class PlaybackRate extends UICorePlugin {
100
104
  'click .gear-sub-menu_btn': 'onRateSelect',
101
105
  'click .gear-option': 'onShowMenu',
102
106
  'click .go-back': 'goBack',
103
- };
107
+ }
104
108
  }
105
109
 
106
110
  /**
107
111
  * @internal
108
112
  */
109
113
  override bindEvents() {
110
- this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
111
- this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChange);
114
+ this.listenTo(this.core, Events.CORE_READY, this.onCoreReady)
115
+ this.listenTo(
116
+ this.core,
117
+ Events.CORE_ACTIVE_CONTAINER_CHANGED,
118
+ this.onActiveContainerChange,
119
+ )
112
120
  }
113
121
 
114
122
  private onCoreReady() {
115
- const mediaControl = this.core.getPlugin('media_control');
116
- assert(mediaControl, 'media_control plugin is required');
117
- const gear = this.core.getPlugin('bottom_gear') as BottomGear;
118
- assert(gear, 'bottom_gear plugin is required');
119
- this.listenTo(mediaControl, MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, this.onGearRendered);
123
+ trace(`${T} onCoreReady`)
124
+ const mediaControl = this.core.getPlugin('media_control')
125
+ assert(mediaControl, 'media_control plugin is required')
126
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
127
+ assert(gear, 'bottom_gear plugin is required')
128
+ this.listenTo(
129
+ mediaControl,
130
+ MediaControlEvents.MEDIACONTROL_GEAR_RENDERED,
131
+ this.onGearRendered,
132
+ )
120
133
  }
121
134
 
122
135
  private onActiveContainerChange() {
123
- this.listenTo(this.core.activePlayback, Events.PLAYBACK_STOP, this.onStop);
124
- this.listenTo(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onPlay);
125
- this.listenTo(this.core.activePlayback, PlaybackEvents.PLAYBACK_RATE_CHANGED, this.onPlaybackRateChange);
126
- this.listenTo(this.core.activeContainer, Events.CONTAINER_PLAYBACKDVRSTATECHANGED, this.onDvrStateChanged);
136
+ trace(`${T} onActiveContainerChange`)
137
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_STOP, this.onStop)
138
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onPlay)
139
+ this.listenTo(
140
+ this.core.activePlayback,
141
+ PlaybackEvents.PLAYBACK_RATE_CHANGED,
142
+ this.onPlaybackRateChange,
143
+ )
144
+ this.listenTo(
145
+ this.core.activeContainer,
146
+ Events.CONTAINER_PLAYBACKDVRSTATECHANGED,
147
+ this.onDvrStateChanged,
148
+ )
127
149
  }
128
150
 
129
151
  private onGearRendered() {
130
152
  trace(`${T} onGearRendered`, {
131
153
  rendered: this.rendered,
132
- });
133
- this.rendered = false;
134
- this.render();
154
+ })
155
+ this.rendered = false
156
+ this.render()
135
157
  }
136
158
 
137
159
  private onDvrStateChanged(dvrEnabled: boolean) {
@@ -139,40 +161,45 @@ export class PlaybackRate extends UICorePlugin {
139
161
  dvrEnabled,
140
162
  })
141
163
  if (dvrEnabled) {
142
- this.render();
164
+ this.render()
143
165
  }
144
166
  }
145
167
 
146
168
  private allRateElements(): ZeptoResult {
147
- return this.$('ul.gear-sub-menu li');
169
+ return this.$('ul.gear-sub-menu li')
148
170
  }
149
171
 
150
- private rateElement(rate = "1"): ZeptoResult {
151
- return (this.$(`ul.gear-sub-menu a[data-rate="${rate}"]`) as ZeptoResult).parent();
172
+ private rateElement(rate = '1'): ZeptoResult {
173
+ return (
174
+ this.$(`ul.gear-sub-menu a[data-rate="${rate}"]`) as ZeptoResult
175
+ ).parent()
152
176
  }
153
177
 
154
178
  private onPlaybackRateChange(playbackRate: number) {
155
- const selectedRate = parseInt(this.selectedRate, 10);
179
+ const selectedRate = parseInt(this.selectedRate, 10)
156
180
  // TODO check it doesn't interfere with the DASH.js or HLS.js playback live catchup
157
181
  if (Math.abs(playbackRate - selectedRate) > 0.1) {
158
182
  trace(`${T} onPlaybackRateChange setting target rate`, {
159
183
  playbackRate,
160
184
  selectedRate,
161
185
  })
162
- this.core.activePlayback?.setPlaybackRate(selectedRate);
186
+ this.core.activePlayback?.setPlaybackRate(selectedRate)
163
187
  }
164
188
  }
165
189
 
166
190
  private shouldRender() {
167
- if (!this.core.activeContainer) {
168
- return false;
191
+ if (!this.core.activePlayback) {
192
+ return false
169
193
  }
170
194
 
171
- if (this.core.getPlaybackType() === Playback.LIVE && !this.core.activePlayback.dvrEnabled) {
172
- return false;
195
+ if (
196
+ this.core.activePlayback.getPlaybackType() === Playback.LIVE &&
197
+ !this.core.activePlayback.dvrEnabled
198
+ ) {
199
+ return false
173
200
  }
174
201
 
175
- return 'setPlaybackRate' in this.core.activePlayback;
202
+ return 'setPlaybackRate' in this.core.activePlayback
176
203
  }
177
204
 
178
205
  /**
@@ -185,99 +212,114 @@ export class PlaybackRate extends UICorePlugin {
185
212
  })
186
213
 
187
214
  if (!this.shouldRender()) {
188
- return this;
215
+ return this
189
216
  }
190
217
 
191
218
  if (this.rendered) {
192
- return this;
219
+ return this
193
220
  }
194
221
 
195
222
  const button = PlaybackRate.buttonTemplate({
196
223
  title: this.getTitle(),
197
224
  speedIcon,
198
225
  arrowRightIcon,
199
- });
226
+ i18n: this.core.i18n,
227
+ })
200
228
 
201
- this.$el.html(button);
229
+ this.$el.html(button)
202
230
 
203
- (this.core.getPlugin('bottom_gear') as BottomGear)?.getElement('rate')?.html(this.el);
231
+ ;(this.core.getPlugin('bottom_gear') as BottomGear)
232
+ ?.getElement('rate')
233
+ ?.html(this.el)
204
234
 
205
- this.rendered = true;
235
+ this.rendered = true
206
236
 
207
- return this;
237
+ return this
208
238
  }
209
239
 
210
240
  private onStartAd() {
211
- this.prevSelectedRate = this.selectedRate;
212
- this.resetPlaybackRate();
213
- this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onFinishAd);
241
+ this.prevSelectedRate = this.selectedRate
242
+ this.resetPlaybackRate()
243
+ this.listenToOnce(
244
+ this.core.activePlayback,
245
+ Events.PLAYBACK_PLAY,
246
+ this.onFinishAd,
247
+ )
214
248
  }
215
249
 
216
250
  private onFinishAd() {
217
251
  if (this.prevSelectedRate) {
218
- this.setSelectedRate(this.prevSelectedRate);
252
+ this.setSelectedRate(this.prevSelectedRate)
219
253
  }
220
254
  }
221
255
 
222
256
  private onPlay() {
223
- if (this.core.getPlaybackType() === Playback.LIVE && !this.core.activePlayback.dvrEnabled) {
224
- this.resetPlaybackRate();
257
+ if (
258
+ this.core.getPlaybackType() === Playback.LIVE &&
259
+ !this.core.activePlayback.dvrEnabled
260
+ ) {
261
+ this.resetPlaybackRate()
225
262
  } else {
226
- this.setSelectedRate(this.selectedRate);
263
+ this.setSelectedRate(this.selectedRate)
227
264
  }
228
265
  }
229
266
 
230
267
  private resetPlaybackRate() {
231
- this.setSelectedRate(DEFAULT_PLAYBACK_RATE);
268
+ this.setSelectedRate(DEFAULT_PLAYBACK_RATE)
232
269
  }
233
270
 
234
- private onStop() {
235
- }
271
+ private onStop() {}
236
272
 
237
273
  private onRateSelect(event: MouseEvent) {
238
- event.stopPropagation();
239
- const rate = (event.currentTarget as HTMLElement).dataset.rate;
274
+ event.stopPropagation()
275
+ const rate = (event.currentTarget as HTMLElement).dataset.rate
240
276
  if (rate) {
241
- this.setSelectedRate(rate);
242
- this.highlightCurrentRate();
277
+ this.setSelectedRate(rate)
278
+ this.highlightCurrentRate()
243
279
  }
244
280
 
245
- return false;
281
+ return false
246
282
  }
247
283
 
248
284
  private onShowMenu() {
249
- this.$el.html(PlaybackRate.listTemplate({
250
- playbackRates: this.playbackRates,
251
- arrowLeftIcon,
252
- checkIcon,
253
- }));
254
- (this.core.getPlugin('bottom_gear') as BottomGear)?.setContent(this.el);
255
- this.highlightCurrentRate();
285
+ this.$el.html(
286
+ PlaybackRate.listTemplate({
287
+ playbackRates: this.playbackRates,
288
+ arrowLeftIcon,
289
+ checkIcon,
290
+ i18n: this.core.i18n,
291
+ }),
292
+ )
293
+ ;(this.core.getPlugin('bottom_gear') as BottomGear)?.setContent(this.el)
294
+ this.highlightCurrentRate()
256
295
  }
257
296
 
258
297
  private goBack() {
259
298
  setTimeout(() => {
260
299
  this.core.getPlugin('bottom_gear').refresh()
261
- }, 0);
300
+ }, 0)
262
301
  }
263
302
 
264
303
  private setSelectedRate(rate: string) {
265
304
  // Set <video playbackRate="..."
266
- this.core.activePlayback?.setPlaybackRate(rate);
267
- this.selectedRate = rate;
305
+ this.core.activePlayback?.setPlaybackRate(rate)
306
+ this.selectedRate = rate
268
307
  }
269
308
 
270
309
  private getTitle() {
271
- return this.playbackRates.find((r) => r.value === this.selectedRate)?.label || this.selectedRate;
310
+ return (
311
+ this.playbackRates.find((r) => r.value === this.selectedRate)?.label ||
312
+ this.selectedRate
313
+ )
272
314
  }
273
315
 
274
316
  private highlightCurrentRate() {
275
- this.allRateElements().removeClass('current');
276
- this.allRateElements().find('a').removeClass('gcore-skin-active');
317
+ this.allRateElements().removeClass('current')
318
+ this.allRateElements().find('a').removeClass('gcore-skin-active')
277
319
 
278
- const currentLevelElement = this.rateElement(this.selectedRate);
320
+ const currentLevelElement = this.rateElement(this.selectedRate)
279
321
 
280
- currentLevelElement.addClass('current');
281
- currentLevelElement.find('a').addClass('gcore-skin-active');
322
+ currentLevelElement.addClass('current')
323
+ currentLevelElement.find('a').addClass('gcore-skin-active')
282
324
  }
283
325
  }
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { PlaybackRate } from '../PlaybackRate'
3
+ import {
4
+ createMockCore,
5
+ createMockMediaControl,
6
+ createMockPlugin,
7
+ } from '../../../testUtils'
8
+ import { $ } from '@clappr/core'
9
+ import { Logger, LogTracer, setTracer } from '@gcorevideo/utils'
10
+
11
+ Logger.enable('*')
12
+ setTracer(new LogTracer('PlaybackRate.test'))
13
+
14
+ describe('PlaybackRate', () => {
15
+ let core: any
16
+ let bottomGear: any
17
+ beforeEach(() => {
18
+ core = createMockCore()
19
+ const mediaControl = createMockMediaControl(core)
20
+ bottomGear = createMockGearPlugin()
21
+ core.getPlugin.mockImplementation((name: string) => {
22
+ if (name === 'bottom_gear') {
23
+ return bottomGear
24
+ }
25
+ if (name === 'media_control') {
26
+ return mediaControl
27
+ }
28
+ return null
29
+ })
30
+ })
31
+ it('should render', () => {
32
+ const playbackRate = new PlaybackRate(core)
33
+ core.emit('core:ready')
34
+ core.activePlayback.getPlaybackType.mockReturnValue('live')
35
+ core.emit('core:active:container:changed')
36
+ core.activePlayback.dvrEnabled = true
37
+ core.activeContainer.emit('container:dvr', true)
38
+ expect(playbackRate.el.innerHTML).toMatchSnapshot()
39
+ expect(bottomGear.getElement).toHaveBeenCalledWith('rate')
40
+ expect(
41
+ bottomGear.$el
42
+ .find('[data-rate]')
43
+ .text()
44
+ .replace(/\/assets.*\.svg/g, '')
45
+ .replace(/\s+/g, ' ')
46
+ .trim(),
47
+ ).toEqual('playback_rate 1x')
48
+ })
49
+ })
50
+
51
+ function createMockGearPlugin() {
52
+ const elements = {
53
+ nerd: $(document.createElement('li')).attr('data-nerd', 'nerd'),
54
+ quality: $(document.createElement('li')).attr('data-quality', 'quality'),
55
+ rate: $(document.createElement('li')).attr('data-rate', 'rate'),
56
+ }
57
+ const $el = $(document.createElement('ul'))
58
+ $el.append(elements.nerd, elements.quality, elements.rate)
59
+ const plugin = Object.assign(createMockPlugin(), {
60
+ setContent: vi.fn(),
61
+ getElement: vi.fn((name: string) => elements[name]),
62
+ $el,
63
+ })
64
+ return plugin
65
+ }