@gcorevideo/player 2.20.6 → 2.20.8
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.
- package/dist/core.js +37 -13
- package/dist/index.css +1163 -1163
- package/dist/index.js +2557 -2513
- package/dist/plugins/index.css +470 -470
- package/dist/plugins/index.js +5230 -5217
- package/lib/playback/BasePlayback.d.ts +5 -0
- package/lib/playback/BasePlayback.d.ts.map +1 -1
- package/lib/playback/BasePlayback.js +8 -0
- package/lib/playback/HTML5Video.d.ts +4 -0
- package/lib/playback/HTML5Video.d.ts.map +1 -0
- package/lib/playback/HTML5Video.js +3 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts +1 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +6 -2
- package/lib/playback/index.d.ts.map +1 -1
- package/lib/playback/index.js +2 -0
- package/lib/playback/types.d.ts +9 -0
- package/lib/playback/types.d.ts.map +1 -0
- package/lib/playback/types.js +9 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts +6 -11
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +9 -21
- package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +2 -2
- package/lib/plugins/dvr-controls/DvrControls.d.ts +1 -1
- package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
- package/lib/plugins/dvr-controls/DvrControls.js +27 -16
- package/lib/plugins/level-selector/LevelSelector.d.ts +17 -5
- package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
- package/lib/plugins/level-selector/LevelSelector.js +35 -24
- package/lib/plugins/media-control/MediaControl.d.ts +11 -0
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +16 -3
- package/lib/plugins/playback-rate/PlaybackRate.d.ts +11 -10
- package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
- package/lib/plugins/playback-rate/PlaybackRate.js +83 -91
- package/lib/plugins/source-controller/SourceController.d.ts +1 -0
- package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.js +8 -4
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +7 -3
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +35 -27
- package/lib/testUtils.d.ts +5 -8
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +15 -9
- package/package.json +1 -1
- package/src/playback/BasePlayback.ts +12 -4
- package/src/playback/HTML5Video.ts +3 -0
- package/src/playback/dash-playback/DashPlayback.ts +15 -11
- package/src/playback/index.ts +2 -1
- package/src/playback/types.ts +9 -0
- package/src/plugins/bottom-gear/BottomGear.ts +10 -21
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +36 -0
- package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +41 -0
- package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +3 -3
- package/src/plugins/dvr-controls/DvrControls.ts +87 -54
- package/src/plugins/level-selector/LevelSelector.ts +64 -31
- package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +15 -16
- package/src/plugins/media-control/MediaControl.ts +20 -6
- package/src/plugins/playback-rate/PlaybackRate.ts +89 -105
- package/src/plugins/source-controller/SourceController.ts +9 -4
- package/src/plugins/source-controller/__tests__/SourceController.test.ts +35 -1
- package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +80 -57
- package/src/testUtils.ts +16 -9
- package/tsconfig.tsbuildinfo +1 -1
- package/assets/playback-rate/playback-rate-selector.ejs +0 -9
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Events, template, UICorePlugin } from '@clappr/core'
|
|
2
2
|
import { reportError, trace } from '@gcorevideo/utils'
|
|
3
|
+
import assert from 'assert'
|
|
3
4
|
|
|
4
5
|
import { type QualityLevel } from '../../playback.types.js'
|
|
5
6
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
@@ -14,12 +15,26 @@ import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg'
|
|
|
14
15
|
import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg'
|
|
15
16
|
import checkIcon from '../../../assets/icons/new/check.svg'
|
|
16
17
|
import '../../../assets/level-selector/style.scss'
|
|
17
|
-
import
|
|
18
|
-
|
|
18
|
+
import { MediaControl, MediaControlEvents } from '../media-control/MediaControl.js'
|
|
19
19
|
|
|
20
20
|
const T = 'plugins.level_selector'
|
|
21
21
|
const VERSION = '2.19.4'
|
|
22
22
|
|
|
23
|
+
export interface LevelSelectorPluginSettings {
|
|
24
|
+
/**
|
|
25
|
+
* The maximum resolution to allow in the level selector.
|
|
26
|
+
*/
|
|
27
|
+
restrictResolution?: number
|
|
28
|
+
/**
|
|
29
|
+
* The labels to show in the level selector.
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* { 360: 'SD', 720: 'HD' }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
labels?: Record<number, string>
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
/**
|
|
24
39
|
* A {@link MediaControl | media control} plugin that provides a UI to control the quality level of the playback.
|
|
25
40
|
* @beta
|
|
@@ -35,11 +50,7 @@ const VERSION = '2.19.4'
|
|
|
35
50
|
*
|
|
36
51
|
* When clicked, it shows a list of quality levels to choose from.
|
|
37
52
|
*
|
|
38
|
-
* Configuration options
|
|
39
|
-
*
|
|
40
|
-
* - `labels`: The labels to show in the level selector. [video resolution]: string
|
|
41
|
-
*
|
|
42
|
-
* - `restrictResolution`: The maximum resolution to allow in the level selector.
|
|
53
|
+
* Configuration options - {@link LevelSelectorPluginSettings}
|
|
43
54
|
*
|
|
44
55
|
* @example
|
|
45
56
|
* ```ts
|
|
@@ -62,7 +73,8 @@ export class LevelSelector extends UICorePlugin {
|
|
|
62
73
|
|
|
63
74
|
private isOpen = false
|
|
64
75
|
|
|
65
|
-
private static readonly buttonTemplate: TemplateFunction =
|
|
76
|
+
private static readonly buttonTemplate: TemplateFunction =
|
|
77
|
+
template(buttonHtml)
|
|
66
78
|
|
|
67
79
|
private static readonly listTemplate: TemplateFunction = template(listHtml)
|
|
68
80
|
|
|
@@ -113,8 +125,24 @@ export class LevelSelector extends UICorePlugin {
|
|
|
113
125
|
* @internal
|
|
114
126
|
*/
|
|
115
127
|
override bindEvents() {
|
|
116
|
-
this.listenTo(this.core, Events.
|
|
117
|
-
this.listenTo(
|
|
128
|
+
this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
|
|
129
|
+
this.listenTo(
|
|
130
|
+
this.core,
|
|
131
|
+
Events.CORE_ACTIVE_CONTAINER_CHANGED,
|
|
132
|
+
this.bindPlaybackEvents,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private onCoreReady() {
|
|
137
|
+
trace(`${T} onCoreReady`);
|
|
138
|
+
const mediaControl = this.core.getPlugin('media_control') as MediaControl
|
|
139
|
+
assert(mediaControl, 'media_control plugin is required')
|
|
140
|
+
this.listenTo(mediaControl, MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, this.onGearRendered);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private onGearRendered() {
|
|
144
|
+
trace(`${T} onGearRendered`);
|
|
145
|
+
this.deferRender();
|
|
118
146
|
}
|
|
119
147
|
|
|
120
148
|
private bindPlaybackEvents() {
|
|
@@ -123,8 +151,10 @@ export class LevelSelector extends UICorePlugin {
|
|
|
123
151
|
|
|
124
152
|
const activePlayback = this.core.activePlayback
|
|
125
153
|
|
|
126
|
-
this.listenTo(
|
|
127
|
-
|
|
154
|
+
this.listenTo(
|
|
155
|
+
activePlayback,
|
|
156
|
+
Events.PLAYBACK_LEVELS_AVAILABLE,
|
|
157
|
+
this.fillLevels,
|
|
128
158
|
)
|
|
129
159
|
this.listenTo(
|
|
130
160
|
activePlayback,
|
|
@@ -150,32 +180,27 @@ export class LevelSelector extends UICorePlugin {
|
|
|
150
180
|
this.deferRender()
|
|
151
181
|
},
|
|
152
182
|
)
|
|
153
|
-
if (activePlayback
|
|
183
|
+
if (activePlayback.levels?.length > 0) {
|
|
154
184
|
this.fillLevels(activePlayback.levels)
|
|
155
185
|
}
|
|
156
186
|
}
|
|
157
187
|
|
|
158
188
|
private onStop() {
|
|
159
189
|
trace(`${T} onStop`)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (
|
|
190
|
+
this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, () => {
|
|
191
|
+
trace(`${T} on PLAYBACK_PLAY after stop`, {
|
|
192
|
+
selectedLevelId: this.selectedLevelId,
|
|
193
|
+
})
|
|
194
|
+
if (this.core.activePlayback.getPlaybackType() === 'live') {
|
|
165
195
|
if (this.selectedLevelId !== -1) {
|
|
166
|
-
|
|
196
|
+
this.core.activePlayback.currentLevel = this.selectedLevelId
|
|
167
197
|
}
|
|
168
198
|
}
|
|
169
199
|
})
|
|
170
200
|
}
|
|
171
201
|
|
|
172
202
|
private shouldRender() {
|
|
173
|
-
if (!this.core.activeContainer) {
|
|
174
|
-
return false
|
|
175
|
-
}
|
|
176
|
-
|
|
177
203
|
const activePlayback = this.core.activePlayback
|
|
178
|
-
|
|
179
204
|
if (!activePlayback) {
|
|
180
205
|
return false
|
|
181
206
|
}
|
|
@@ -192,8 +217,6 @@ export class LevelSelector extends UICorePlugin {
|
|
|
192
217
|
* @internal
|
|
193
218
|
*/
|
|
194
219
|
override render() {
|
|
195
|
-
assert(this.core.getPlugin('bottom_gear'), 'bottom_gear plugin is required')
|
|
196
|
-
|
|
197
220
|
if (!this.shouldRender()) {
|
|
198
221
|
return this
|
|
199
222
|
}
|
|
@@ -213,7 +236,10 @@ export class LevelSelector extends UICorePlugin {
|
|
|
213
236
|
})
|
|
214
237
|
this.$el.html(html)
|
|
215
238
|
const gear = this.core.getPlugin('bottom_gear') as BottomGear
|
|
216
|
-
gear
|
|
239
|
+
if (!gear) {
|
|
240
|
+
trace(`${T} renderButton: bottom_gear plugin not found`)
|
|
241
|
+
}
|
|
242
|
+
gear?.getElement('quality')?.html(this.el)
|
|
217
243
|
}
|
|
218
244
|
}
|
|
219
245
|
|
|
@@ -228,6 +254,7 @@ export class LevelSelector extends UICorePlugin {
|
|
|
228
254
|
})
|
|
229
255
|
this.$el.html(html)
|
|
230
256
|
const gear = this.core.getPlugin('bottom_gear') as BottomGear
|
|
257
|
+
trace(`${T} renderDropdown: bottom_gear plugin not found`)
|
|
231
258
|
gear?.setContent(this.el)
|
|
232
259
|
}
|
|
233
260
|
|
|
@@ -236,7 +263,8 @@ export class LevelSelector extends UICorePlugin {
|
|
|
236
263
|
return maxRes
|
|
237
264
|
? this.levels.findIndex(
|
|
238
265
|
(level) =>
|
|
239
|
-
(level.height > level.width ? level.width : level.height) ===
|
|
266
|
+
(level.height > level.width ? level.width : level.height) ===
|
|
267
|
+
maxRes,
|
|
240
268
|
)
|
|
241
269
|
: -1
|
|
242
270
|
}
|
|
@@ -248,7 +276,11 @@ export class LevelSelector extends UICorePlugin {
|
|
|
248
276
|
if (maxResolution) {
|
|
249
277
|
this.removeAuto = true
|
|
250
278
|
const initialLevel = levels
|
|
251
|
-
.filter(
|
|
279
|
+
.filter(
|
|
280
|
+
(level) =>
|
|
281
|
+
(level.width > level.height ? level.height : level.width) <=
|
|
282
|
+
maxResolution,
|
|
283
|
+
)
|
|
252
284
|
.pop()
|
|
253
285
|
this.setLevel(initialLevel?.level ?? 0)
|
|
254
286
|
}
|
|
@@ -280,8 +312,9 @@ export class LevelSelector extends UICorePlugin {
|
|
|
280
312
|
private goBack() {
|
|
281
313
|
trace(`${T} goBack`)
|
|
282
314
|
this.isOpen = false
|
|
283
|
-
|
|
284
|
-
|
|
315
|
+
setTimeout(() => {
|
|
316
|
+
this.core.getPlugin('bottom_gear').refresh()
|
|
317
|
+
}, 0);
|
|
285
318
|
}
|
|
286
319
|
|
|
287
320
|
private setLevel(index: number) {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import { UICorePlugin } from '@clappr/core'
|
|
2
|
+
import { $, UICorePlugin } from '@clappr/core'
|
|
3
3
|
import FakeTimers from '@sinonjs/fake-timers'
|
|
4
4
|
import { Logger, LogTracer, setTracer } from '@gcorevideo/utils'
|
|
5
5
|
import { LevelSelector } from '../LevelSelector.js'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createMockCore,
|
|
8
|
+
createMockMediaControl,
|
|
9
|
+
createMockPlayback,
|
|
10
|
+
} from '../../../testUtils.js'
|
|
11
|
+
import { MediaControlEvents } from '../../media-control/MediaControl.js'
|
|
7
12
|
|
|
8
13
|
setTracer(new LogTracer('LevelSelector.test'))
|
|
9
14
|
Logger.enable('*')
|
|
@@ -33,6 +38,8 @@ describe('LevelSelector', () => {
|
|
|
33
38
|
let core: any
|
|
34
39
|
let levelSelector: LevelSelector
|
|
35
40
|
let activePlayback: any
|
|
41
|
+
let mediaControl: UICorePlugin
|
|
42
|
+
let bottomGear: UICorePlugin | null
|
|
36
43
|
beforeEach(() => {
|
|
37
44
|
clock = FakeTimers.install()
|
|
38
45
|
})
|
|
@@ -41,10 +48,6 @@ describe('LevelSelector', () => {
|
|
|
41
48
|
})
|
|
42
49
|
describe('basically', () => {
|
|
43
50
|
beforeEach(() => {
|
|
44
|
-
// const activeContainer = createMockContainer()
|
|
45
|
-
let mediaControl: UICorePlugin | null = null
|
|
46
|
-
let bottomGear: UICorePlugin | null = null
|
|
47
|
-
// TODO create mock core
|
|
48
51
|
core = createMockCore({
|
|
49
52
|
levelSelector: {
|
|
50
53
|
// restrictResolution: 360,
|
|
@@ -61,7 +64,7 @@ describe('LevelSelector', () => {
|
|
|
61
64
|
}
|
|
62
65
|
return null
|
|
63
66
|
})
|
|
64
|
-
mediaControl =
|
|
67
|
+
mediaControl = createMockMediaControl(core)
|
|
65
68
|
bottomGear = createBottomGear(core)
|
|
66
69
|
levelSelector = new LevelSelector(core)
|
|
67
70
|
})
|
|
@@ -126,7 +129,7 @@ describe('LevelSelector', () => {
|
|
|
126
129
|
}
|
|
127
130
|
return null
|
|
128
131
|
})
|
|
129
|
-
mediaControl =
|
|
132
|
+
mediaControl = createMockMediaControl(core)
|
|
130
133
|
bottomGear = createBottomGear(core)
|
|
131
134
|
levelSelector = new LevelSelector(core)
|
|
132
135
|
})
|
|
@@ -219,17 +222,13 @@ expect.extend({
|
|
|
219
222
|
},
|
|
220
223
|
})
|
|
221
224
|
|
|
222
|
-
function createMediaControl(core: any) {
|
|
223
|
-
const mediaControl = new UICorePlugin(core)
|
|
224
|
-
// @ts-ignore
|
|
225
|
-
mediaControl.getElement = vi.fn().mockReturnValue(null)
|
|
226
|
-
return mediaControl
|
|
227
|
-
}
|
|
228
|
-
|
|
229
225
|
function createBottomGear(core: any) {
|
|
230
226
|
const bottomGear = new UICorePlugin(core)
|
|
227
|
+
const elemets = {
|
|
228
|
+
quality: $(document.createElement('div')),
|
|
229
|
+
}
|
|
231
230
|
// @ts-ignore
|
|
232
|
-
bottomGear.getElement = vi.fn().
|
|
231
|
+
bottomGear.getElement = vi.fn().mockImplementation((name) => elemets[name])
|
|
233
232
|
// @ts-ignore
|
|
234
233
|
bottomGear.setContent = vi.fn()
|
|
235
234
|
return bottomGear
|
|
@@ -49,6 +49,17 @@ export type MediaControlElement =
|
|
|
49
49
|
| 'seekBarContainer'
|
|
50
50
|
| 'subtitlesSelector'
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Custom events emitted by the plugins to communicate with one another
|
|
54
|
+
* @beta
|
|
55
|
+
*/
|
|
56
|
+
export enum MediaControlEvents {
|
|
57
|
+
/**
|
|
58
|
+
* Emitted when the gear menu is rendered
|
|
59
|
+
*/
|
|
60
|
+
MEDIACONTROL_GEAR_RENDERED = 'mediacontrol:gear:rendered',
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
const T = 'plugins.media_control'
|
|
53
64
|
|
|
54
65
|
const LEFT_ORDER = [
|
|
@@ -96,7 +107,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
96
107
|
|
|
97
108
|
private currentDurationValue: number = 0
|
|
98
109
|
private currentPositionValue: number = 0
|
|
99
|
-
private currentSeekBarPercentage
|
|
110
|
+
private currentSeekBarPercentage = 0
|
|
100
111
|
|
|
101
112
|
private disabledClickableList: DisabledClickable[] = []
|
|
102
113
|
private displayedDuration: string | null = null
|
|
@@ -264,6 +275,10 @@ export class MediaControl extends UICorePlugin {
|
|
|
264
275
|
}
|
|
265
276
|
}
|
|
266
277
|
|
|
278
|
+
get currentSeekPos() {
|
|
279
|
+
return this.currentSeekBarPercentage
|
|
280
|
+
}
|
|
281
|
+
|
|
267
282
|
/**
|
|
268
283
|
* Current volume [0..100]
|
|
269
284
|
*/
|
|
@@ -735,11 +750,10 @@ export class MediaControl extends UICorePlugin {
|
|
|
735
750
|
this.changeTogglePlay()
|
|
736
751
|
this.bindContainerEvents()
|
|
737
752
|
this.settingsUpdate()
|
|
738
|
-
this.core.activeContainer
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
)
|
|
753
|
+
this.core.activeContainer.trigger(
|
|
754
|
+
Events.CONTAINER_PLAYBACKDVRSTATECHANGED,
|
|
755
|
+
this.core.activeContainer.isDvrInUse(),
|
|
756
|
+
)
|
|
743
757
|
// TODO test
|
|
744
758
|
if (this.core.activeContainer.mediaControlDisabled) {
|
|
745
759
|
this.disable()
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { Events, UICorePlugin, Playback, template } from '@clappr/core';
|
|
1
|
+
import { Events, UICorePlugin, Playback, template, Core } from '@clappr/core';
|
|
2
|
+
import { trace } from '@gcorevideo/utils';
|
|
3
|
+
import assert from 'assert';
|
|
2
4
|
|
|
3
5
|
import { CLAPPR_VERSION } from '../../build.js';
|
|
4
6
|
import type { ZeptoResult } from '../../utils/types.js';
|
|
5
7
|
|
|
6
|
-
import pluginHtml from '../../../assets/playback-rate/playback-rate-selector.ejs';
|
|
7
8
|
import buttonHtml from '../../../assets/playback-rate/button.ejs';
|
|
8
9
|
import listHtml from '../../../assets/playback-rate/list.ejs';
|
|
9
10
|
import speedIcon from '../../../assets/icons/new/speed.svg';
|
|
10
11
|
import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg';
|
|
11
12
|
import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg';
|
|
12
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';
|
|
13
17
|
|
|
14
18
|
type PlaybackRateOption = {
|
|
15
19
|
value: string;
|
|
@@ -28,11 +32,10 @@ const DEFAULT_PLAYBACK_RATES = [
|
|
|
28
32
|
|
|
29
33
|
const DEFAULT_PLAYBACK_RATE = '1.0';
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
const MEDIACONTROL_PLAYBACKRATE = 'playbackRate';
|
|
35
|
+
const T = 'plugins.playback_rate';
|
|
33
36
|
|
|
34
37
|
/**
|
|
35
|
-
*
|
|
38
|
+
* PLUGIN that allows changing the playback speed of the video.
|
|
36
39
|
* @beta
|
|
37
40
|
*
|
|
38
41
|
* @remarks
|
|
@@ -42,15 +45,16 @@ const MEDIACONTROL_PLAYBACKRATE = 'playbackRate';
|
|
|
42
45
|
*
|
|
43
46
|
* - {@link BottomGear | bottom_gear}
|
|
44
47
|
*
|
|
45
|
-
* It renders a button in the gear menu, which opens a dropdown with the
|
|
48
|
+
* It renders a button in the gear menu, which opens a dropdown with the options to change the playback rate.
|
|
46
49
|
*/
|
|
47
50
|
export class PlaybackRate extends UICorePlugin {
|
|
48
|
-
private currentPlayback: Playback | null = null;
|
|
49
|
-
|
|
50
51
|
private playbackRates: PlaybackRateOption[] = DEFAULT_PLAYBACK_RATES;
|
|
51
52
|
|
|
53
|
+
// Saved when an ad starts to restore after it finishes
|
|
52
54
|
private prevSelectedRate: string | undefined;
|
|
53
55
|
|
|
56
|
+
private rendered = false;
|
|
57
|
+
|
|
54
58
|
private selectedRate: string = DEFAULT_PLAYBACK_RATE;
|
|
55
59
|
|
|
56
60
|
/**
|
|
@@ -67,12 +71,16 @@ export class PlaybackRate extends UICorePlugin {
|
|
|
67
71
|
return { min: CLAPPR_VERSION };
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
private static readonly template = template(pluginHtml);
|
|
71
|
-
|
|
72
74
|
private static readonly buttonTemplate = template(buttonHtml);
|
|
73
75
|
|
|
74
76
|
private static readonly listTemplate = template(listHtml);
|
|
75
77
|
|
|
78
|
+
constructor(core: Core) {
|
|
79
|
+
super(core);
|
|
80
|
+
this.playbackRates = core.options.playbackRate?.options || DEFAULT_PLAYBACK_RATES;
|
|
81
|
+
this.selectedRate = core.options.playbackRate?.defaultValue || DEFAULT_PLAYBACK_RATE;
|
|
82
|
+
}
|
|
83
|
+
|
|
76
84
|
/**
|
|
77
85
|
* @internal
|
|
78
86
|
*/
|
|
@@ -98,31 +106,40 @@ export class PlaybackRate extends UICorePlugin {
|
|
|
98
106
|
* @internal
|
|
99
107
|
*/
|
|
100
108
|
override bindEvents() {
|
|
101
|
-
this.listenTo(this.core,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.listenTo(this.core.mediaControl, MEDIACONTROL_PLAYBACKRATE, this.updatePlaybackRate);
|
|
105
|
-
|
|
106
|
-
this.listenTo(this.core, 'core:advertisement:start', this.onStartAd);
|
|
107
|
-
this.listenTo(this.core, 'core:advertisement:finish', this.onFinishAd);
|
|
108
|
-
if (this.core.activeContainer) {
|
|
109
|
-
this.listenTo(this.core.activePlayback, Events.PLAYBACK_BUFFERFULL, this.updateLiveStatus);
|
|
110
|
-
}
|
|
109
|
+
this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
|
|
110
|
+
this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChange);
|
|
111
|
+
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
private onCoreReady() {
|
|
114
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
115
|
+
assert(mediaControl, 'media_control plugin is required');
|
|
116
|
+
const gear = this.core.getPlugin('bottom_gear') as BottomGear;
|
|
117
|
+
assert(gear, 'bottom_gear plugin is required');
|
|
118
|
+
this.listenTo(mediaControl, MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, this.onGearRendered);
|
|
119
|
+
}
|
|
115
120
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
private onActiveContainerChange() {
|
|
122
|
+
this.listenTo(this.core.activePlayback, Events.PLAYBACK_STOP, this.onStop);
|
|
123
|
+
this.listenTo(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onPlay);
|
|
124
|
+
this.listenTo(this.core.activePlayback, PlaybackEvents.PLAYBACK_RATE_CHANGED, this.onPlaybackRateChange);
|
|
125
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_PLAYBACKDVRSTATECHANGED, this.onDvrStateChanged);
|
|
119
126
|
}
|
|
120
127
|
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.
|
|
128
|
+
private onGearRendered() {
|
|
129
|
+
trace(`${T} onGearRendered`, {
|
|
130
|
+
rendered: this.rendered,
|
|
131
|
+
});
|
|
132
|
+
this.rendered = false;
|
|
133
|
+
this.render();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private onDvrStateChanged(dvrEnabled: boolean) {
|
|
137
|
+
trace(`${T} onDvrStateChanged`, {
|
|
138
|
+
dvrEnabled,
|
|
139
|
+
})
|
|
140
|
+
if (dvrEnabled) {
|
|
141
|
+
this.render();
|
|
142
|
+
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
private allRateElements(): ZeptoResult {
|
|
@@ -133,86 +150,65 @@ export class PlaybackRate extends UICorePlugin {
|
|
|
133
150
|
return (this.$(`ul.gear-sub-menu a[data-rate="${rate}"]`) as ZeptoResult).parent();
|
|
134
151
|
}
|
|
135
152
|
|
|
136
|
-
private
|
|
137
|
-
|
|
138
|
-
(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.core.mediaControl.$playbackRate.removeClass('playbackrate-enable');
|
|
145
|
-
this.core.mediaControl.$el.addClass('dvr');
|
|
146
|
-
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
this.updatePlaybackRate(DEFAULT_PLAYBACK_RATE);
|
|
150
|
-
this.core.mediaControl.$playbackRate.addClass('playbackrate-enable');
|
|
151
|
-
this.core.mediaControl.$el.removeClass('dvr');
|
|
153
|
+
private onPlaybackRateChange(playbackRate: number) {
|
|
154
|
+
const selectedRate = parseInt(this.selectedRate, 10);
|
|
155
|
+
if (playbackRate !== selectedRate) {
|
|
156
|
+
trace(`${T} onPlaybackRateChange setting target rate`, {
|
|
157
|
+
playbackRate,
|
|
158
|
+
selectedRate,
|
|
159
|
+
})
|
|
160
|
+
this.core.activePlayback?.setPlaybackRate(selectedRate);
|
|
152
161
|
}
|
|
153
162
|
}
|
|
154
163
|
|
|
155
|
-
private reload() {
|
|
156
|
-
this.unBindEvents();
|
|
157
|
-
this.bindEvents();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
164
|
private shouldRender() {
|
|
161
165
|
if (!this.core.activeContainer) {
|
|
162
166
|
return false;
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
this.
|
|
169
|
+
if (this.core.getPlaybackType() === Playback.LIVE && !this.core.activePlayback.dvrEnabled) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
166
172
|
|
|
167
|
-
return
|
|
173
|
+
return 'setPlaybackRate' in this.core.activePlayback;
|
|
168
174
|
}
|
|
169
175
|
|
|
170
176
|
/**
|
|
171
177
|
* @internal
|
|
172
178
|
*/
|
|
173
179
|
override render() {
|
|
174
|
-
|
|
180
|
+
trace(`${T} render`, {
|
|
181
|
+
rendered: this.rendered,
|
|
182
|
+
shouldRender: this.shouldRender(),
|
|
183
|
+
})
|
|
175
184
|
|
|
176
|
-
if (this.
|
|
185
|
+
if (!this.shouldRender()) {
|
|
177
186
|
return this;
|
|
178
187
|
}
|
|
179
|
-
const cfg = this.core.options.playbackRateConfig || {};
|
|
180
188
|
|
|
181
|
-
if (
|
|
182
|
-
this
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!this.selectedRate) {
|
|
186
|
-
this.selectedRate = cfg.defaultValue || DEFAULT_PLAYBACK_RATE;
|
|
189
|
+
if (this.rendered) {
|
|
190
|
+
return this;
|
|
187
191
|
}
|
|
188
192
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
this.$el.html(button);
|
|
197
|
-
|
|
198
|
-
// if (this.core.getPlaybackType() === Playback.LIVE) {
|
|
199
|
-
// this.core.mediaControl.$playbackRate.addClass('playbackrate-enable');
|
|
200
|
-
// }
|
|
193
|
+
const button = PlaybackRate.buttonTemplate({
|
|
194
|
+
title: this.getTitle(),
|
|
195
|
+
speedIcon,
|
|
196
|
+
arrowRightIcon,
|
|
197
|
+
});
|
|
201
198
|
|
|
202
|
-
|
|
199
|
+
this.$el.html(button);
|
|
203
200
|
|
|
204
|
-
|
|
201
|
+
(this.core.getPlugin('bottom_gear') as BottomGear)?.getElement('rate')?.html(this.el);
|
|
205
202
|
|
|
206
|
-
|
|
207
|
-
}
|
|
203
|
+
this.rendered = true;
|
|
208
204
|
|
|
209
205
|
return this;
|
|
210
206
|
}
|
|
211
207
|
|
|
212
208
|
private onStartAd() {
|
|
213
209
|
this.prevSelectedRate = this.selectedRate;
|
|
214
|
-
this.
|
|
215
|
-
this.listenToOnce(this.
|
|
210
|
+
this.resetPlaybackRate();
|
|
211
|
+
this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onFinishAd);
|
|
216
212
|
}
|
|
217
213
|
|
|
218
214
|
private onFinishAd() {
|
|
@@ -222,16 +218,17 @@ export class PlaybackRate extends UICorePlugin {
|
|
|
222
218
|
}
|
|
223
219
|
|
|
224
220
|
private onPlay() {
|
|
225
|
-
if (!this.core.
|
|
226
|
-
|
|
227
|
-
this.updatePlaybackRate(DEFAULT_PLAYBACK_RATE);
|
|
228
|
-
this.core.mediaControl.$playbackRate.addClass('playbackrate-enable');
|
|
229
|
-
}
|
|
221
|
+
if (this.core.getPlaybackType() === Playback.LIVE && !this.core.activePlayback.dvrEnabled) {
|
|
222
|
+
this.resetPlaybackRate();
|
|
230
223
|
} else {
|
|
231
224
|
this.setSelectedRate(this.selectedRate);
|
|
232
225
|
}
|
|
233
226
|
}
|
|
234
227
|
|
|
228
|
+
private resetPlaybackRate() {
|
|
229
|
+
this.setSelectedRate(DEFAULT_PLAYBACK_RATE);
|
|
230
|
+
}
|
|
231
|
+
|
|
235
232
|
private onStop() {
|
|
236
233
|
}
|
|
237
234
|
|
|
@@ -252,37 +249,24 @@ export class PlaybackRate extends UICorePlugin {
|
|
|
252
249
|
arrowLeftIcon,
|
|
253
250
|
checkIcon,
|
|
254
251
|
}));
|
|
255
|
-
|
|
256
|
-
this.core.mediaControl.$el?.find('.gear-wrapper').html(this.el);
|
|
252
|
+
(this.core.getPlugin('bottom_gear') as BottomGear)?.setContent(this.el);
|
|
257
253
|
this.highlightCurrentRate();
|
|
258
254
|
}
|
|
259
255
|
|
|
260
256
|
private goBack() {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
private updatePlaybackRate(rate: string) {
|
|
265
|
-
this.setSelectedRate(rate);
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
this.core.getPlugin('bottom_gear').refresh()
|
|
259
|
+
}, 0);
|
|
266
260
|
}
|
|
267
261
|
|
|
268
262
|
private setSelectedRate(rate: string) {
|
|
269
263
|
// Set <video playbackRate="..."
|
|
270
|
-
this.core
|
|
264
|
+
this.core.activePlayback?.setPlaybackRate(rate);
|
|
271
265
|
this.selectedRate = rate;
|
|
272
|
-
// TODO
|
|
273
|
-
// Player.player.trigger('playbackRateChanged', rate);
|
|
274
266
|
}
|
|
275
267
|
|
|
276
268
|
private getTitle() {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.playbackRates.forEach((r) => {
|
|
280
|
-
if (r.value === this.selectedRate) {
|
|
281
|
-
title = r.label;
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
return title;
|
|
269
|
+
return this.playbackRates.find((r) => r.value === this.selectedRate)?.label || this.selectedRate;
|
|
286
270
|
}
|
|
287
271
|
|
|
288
272
|
private highlightCurrentRate() {
|