@gcorevideo/player 2.20.6 → 2.20.7
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 +1 -1
- package/dist/index.css +662 -662
- package/dist/index.js +5143 -5123
- package/dist/plugins/index.css +524 -524
- package/dist/plugins/index.js +5111 -5096
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +5 -8
- package/lib/plugins/level-selector/LevelSelector.d.ts +15 -5
- package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
- package/lib/plugins/level-selector/LevelSelector.js +22 -22
- package/lib/plugins/media-control/MediaControl.d.ts +10 -0
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +11 -0
- 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/plugins/bottom-gear/BottomGear.ts +5 -7
- 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/level-selector/LevelSelector.ts +50 -29
- package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +15 -16
- package/src/plugins/media-control/MediaControl.ts +11 -0
- 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
|
@@ -10,6 +10,7 @@ import '../../../assets/bottom-gear/gear-sub-menu.scss';
|
|
|
10
10
|
import gearIcon from '../../../assets/icons/new/gear.svg';
|
|
11
11
|
import gearHdIcon from '../../../assets/icons/new/gear-hd.svg';
|
|
12
12
|
import { ZeptoResult } from '../../utils/types.js';
|
|
13
|
+
import { MediaControlEvents } from '../media-control/MediaControl';
|
|
13
14
|
|
|
14
15
|
const VERSION = '2.19.12';
|
|
15
16
|
|
|
@@ -127,13 +128,9 @@ export class BottomGear extends UICorePlugin {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
private highDefinitionUpdate(isHd: boolean) {
|
|
130
|
-
trace(`${
|
|
131
|
+
trace(`${T} highDefinitionUpdate`, { isHd });
|
|
131
132
|
this.isHd = isHd;
|
|
132
|
-
|
|
133
|
-
this.$el.find('.gear-icon').html(gearHdIcon);
|
|
134
|
-
} else {
|
|
135
|
-
this.$el.find('.gear-icon').html(gearIcon);
|
|
136
|
-
}
|
|
133
|
+
this.$el.find('.gear-icon').html(isHd ? gearHdIcon : gearIcon);
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
/**
|
|
@@ -154,7 +151,8 @@ export class BottomGear extends UICorePlugin {
|
|
|
154
151
|
|
|
155
152
|
mediaControl.getElement('gear')?.html(this.el);
|
|
156
153
|
this.core.trigger('gear:rendered'); // @deprecated
|
|
157
|
-
mediaControl.trigger(GearEvents.MEDIACONTROL_GEAR_RENDERED);
|
|
154
|
+
mediaControl.trigger(GearEvents.MEDIACONTROL_GEAR_RENDERED); // TODO drop
|
|
155
|
+
mediaControl.trigger(MediaControlEvents.MEDIACONTROL_GEAR_RENDERED);
|
|
158
156
|
return this;
|
|
159
157
|
}
|
|
160
158
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MockedFunction, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { BottomGear } from '../BottomGear'
|
|
4
|
+
import { createMockCore, createMockMediaControl } from '../../../testUtils'
|
|
5
|
+
import { MediaControlEvents } from '../../media-control/MediaControl'
|
|
6
|
+
|
|
7
|
+
describe('BottomGear', () => {
|
|
8
|
+
let mediaControl: any
|
|
9
|
+
let core: any
|
|
10
|
+
let bottomGear: BottomGear
|
|
11
|
+
let onGearRendered: MockedFunction<() => void>
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
core = createMockCore()
|
|
14
|
+
mediaControl = createMockMediaControl(core)
|
|
15
|
+
core.getPlugin = vi
|
|
16
|
+
.fn()
|
|
17
|
+
.mockImplementation((name) =>
|
|
18
|
+
name === 'media_control' ? mediaControl : null,
|
|
19
|
+
)
|
|
20
|
+
bottomGear = new BottomGear(core)
|
|
21
|
+
onGearRendered = vi.fn()
|
|
22
|
+
mediaControl.on(MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, onGearRendered, null)
|
|
23
|
+
bottomGear.render()
|
|
24
|
+
})
|
|
25
|
+
it('should render', () => {
|
|
26
|
+
expect(bottomGear.el.innerHTML).toMatchSnapshot()
|
|
27
|
+
})
|
|
28
|
+
it('should attach to media control', () => {
|
|
29
|
+
const gearElement = mediaControl.getElement('gear')
|
|
30
|
+
expect(gearElement[0].innerHTML).not.toEqual('')
|
|
31
|
+
expect(gearElement[0].innerHTML).toMatchSnapshot()
|
|
32
|
+
})
|
|
33
|
+
it('should emit event', () => {
|
|
34
|
+
expect(onGearRendered).toHaveBeenCalled()
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`BottomGear > should attach to media control 1`] = `
|
|
4
|
+
"<div class="bottom_gear" data-track-selector=""><div class="media-control-gear" data-="">
|
|
5
|
+
<button type="button" class="button-gear gplayer-lite-btn gcore-skin-button-color" data-gear-button="-1">
|
|
6
|
+
<span class="gear-icon">/assets/icons/new/gear.svg</span>
|
|
7
|
+
</button>
|
|
8
|
+
<div class="gear-wrapper gcore-skin-bg-color">
|
|
9
|
+
<ul class="gear-options-list">
|
|
10
|
+
|
|
11
|
+
<li data-quality=""></li>
|
|
12
|
+
|
|
13
|
+
<li data-rate=""></li>
|
|
14
|
+
|
|
15
|
+
<li data-nerd=""></li>
|
|
16
|
+
|
|
17
|
+
</ul>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>"
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
exports[`BottomGear > should render 1`] = `
|
|
24
|
+
"<div class="media-control-gear" data-="">
|
|
25
|
+
<button type="button" class="button-gear gplayer-lite-btn gcore-skin-button-color" data-gear-button="-1">
|
|
26
|
+
<span class="gear-icon">/assets/icons/new/gear.svg</span>
|
|
27
|
+
</button>
|
|
28
|
+
<div class="gear-wrapper gcore-skin-bg-color">
|
|
29
|
+
<ul class="gear-options-list">
|
|
30
|
+
|
|
31
|
+
<li data-quality=""></li>
|
|
32
|
+
|
|
33
|
+
<li data-rate=""></li>
|
|
34
|
+
|
|
35
|
+
<li data-nerd=""></li>
|
|
36
|
+
|
|
37
|
+
</ul>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
"
|
|
41
|
+
`;
|
|
@@ -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 } 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,13 @@ export class LevelSelector extends UICorePlugin {
|
|
|
113
125
|
* @internal
|
|
114
126
|
*/
|
|
115
127
|
override bindEvents() {
|
|
116
|
-
this.
|
|
117
|
-
|
|
128
|
+
const mediaControl = this.core.getPlugin('media_control') as MediaControl
|
|
129
|
+
assert(mediaControl, 'media_control plugin is required')
|
|
130
|
+
this.listenTo(
|
|
131
|
+
this.core,
|
|
132
|
+
Events.CORE_ACTIVE_CONTAINER_CHANGED,
|
|
133
|
+
this.bindPlaybackEvents,
|
|
134
|
+
)
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
private bindPlaybackEvents() {
|
|
@@ -123,8 +140,10 @@ export class LevelSelector extends UICorePlugin {
|
|
|
123
140
|
|
|
124
141
|
const activePlayback = this.core.activePlayback
|
|
125
142
|
|
|
126
|
-
this.listenTo(
|
|
127
|
-
|
|
143
|
+
this.listenTo(
|
|
144
|
+
activePlayback,
|
|
145
|
+
Events.PLAYBACK_LEVELS_AVAILABLE,
|
|
146
|
+
this.fillLevels,
|
|
128
147
|
)
|
|
129
148
|
this.listenTo(
|
|
130
149
|
activePlayback,
|
|
@@ -150,32 +169,27 @@ export class LevelSelector extends UICorePlugin {
|
|
|
150
169
|
this.deferRender()
|
|
151
170
|
},
|
|
152
171
|
)
|
|
153
|
-
if (activePlayback
|
|
172
|
+
if (activePlayback.levels?.length > 0) {
|
|
154
173
|
this.fillLevels(activePlayback.levels)
|
|
155
174
|
}
|
|
156
175
|
}
|
|
157
176
|
|
|
158
177
|
private onStop() {
|
|
159
178
|
trace(`${T} onStop`)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (
|
|
179
|
+
this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, () => {
|
|
180
|
+
trace(`${T} on PLAYBACK_PLAY after stop`, {
|
|
181
|
+
selectedLevelId: this.selectedLevelId,
|
|
182
|
+
})
|
|
183
|
+
if (this.core.activePlayback.getPlaybackType() === 'live') {
|
|
165
184
|
if (this.selectedLevelId !== -1) {
|
|
166
|
-
|
|
185
|
+
this.core.activePlayback.currentLevel = this.selectedLevelId
|
|
167
186
|
}
|
|
168
187
|
}
|
|
169
188
|
})
|
|
170
189
|
}
|
|
171
190
|
|
|
172
191
|
private shouldRender() {
|
|
173
|
-
if (!this.core.activeContainer) {
|
|
174
|
-
return false
|
|
175
|
-
}
|
|
176
|
-
|
|
177
192
|
const activePlayback = this.core.activePlayback
|
|
178
|
-
|
|
179
193
|
if (!activePlayback) {
|
|
180
194
|
return false
|
|
181
195
|
}
|
|
@@ -192,8 +206,6 @@ export class LevelSelector extends UICorePlugin {
|
|
|
192
206
|
* @internal
|
|
193
207
|
*/
|
|
194
208
|
override render() {
|
|
195
|
-
assert(this.core.getPlugin('bottom_gear'), 'bottom_gear plugin is required')
|
|
196
|
-
|
|
197
209
|
if (!this.shouldRender()) {
|
|
198
210
|
return this
|
|
199
211
|
}
|
|
@@ -213,7 +225,10 @@ export class LevelSelector extends UICorePlugin {
|
|
|
213
225
|
})
|
|
214
226
|
this.$el.html(html)
|
|
215
227
|
const gear = this.core.getPlugin('bottom_gear') as BottomGear
|
|
216
|
-
gear
|
|
228
|
+
if (!gear) {
|
|
229
|
+
trace(`${T} renderButton: bottom_gear plugin not found`)
|
|
230
|
+
}
|
|
231
|
+
gear?.getElement('quality')?.html(this.el)
|
|
217
232
|
}
|
|
218
233
|
}
|
|
219
234
|
|
|
@@ -228,6 +243,7 @@ export class LevelSelector extends UICorePlugin {
|
|
|
228
243
|
})
|
|
229
244
|
this.$el.html(html)
|
|
230
245
|
const gear = this.core.getPlugin('bottom_gear') as BottomGear
|
|
246
|
+
trace(`${T} renderDropdown: bottom_gear plugin not found`)
|
|
231
247
|
gear?.setContent(this.el)
|
|
232
248
|
}
|
|
233
249
|
|
|
@@ -236,7 +252,8 @@ export class LevelSelector extends UICorePlugin {
|
|
|
236
252
|
return maxRes
|
|
237
253
|
? this.levels.findIndex(
|
|
238
254
|
(level) =>
|
|
239
|
-
(level.height > level.width ? level.width : level.height) ===
|
|
255
|
+
(level.height > level.width ? level.width : level.height) ===
|
|
256
|
+
maxRes,
|
|
240
257
|
)
|
|
241
258
|
: -1
|
|
242
259
|
}
|
|
@@ -248,7 +265,11 @@ export class LevelSelector extends UICorePlugin {
|
|
|
248
265
|
if (maxResolution) {
|
|
249
266
|
this.removeAuto = true
|
|
250
267
|
const initialLevel = levels
|
|
251
|
-
.filter(
|
|
268
|
+
.filter(
|
|
269
|
+
(level) =>
|
|
270
|
+
(level.width > level.height ? level.height : level.width) <=
|
|
271
|
+
maxResolution,
|
|
272
|
+
)
|
|
252
273
|
.pop()
|
|
253
274
|
this.setLevel(initialLevel?.level ?? 0)
|
|
254
275
|
}
|
|
@@ -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 = [
|
|
@@ -90,6 +90,8 @@ export class SourceController extends CorePlugin {
|
|
|
90
90
|
|
|
91
91
|
private active = false
|
|
92
92
|
|
|
93
|
+
private switching = false
|
|
94
|
+
|
|
93
95
|
private sync: SyncFn = noSync
|
|
94
96
|
|
|
95
97
|
/**
|
|
@@ -166,15 +168,18 @@ export class SourceController extends CorePlugin {
|
|
|
166
168
|
description: error?.description,
|
|
167
169
|
level: error?.level,
|
|
168
170
|
},
|
|
171
|
+
switching: this.switching,
|
|
169
172
|
retrying: this.active,
|
|
170
173
|
currentSource: this.sourcesList[this.currentSourceIndex],
|
|
171
174
|
})
|
|
175
|
+
if (this.switching) {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
172
178
|
switch (error.code) {
|
|
173
179
|
case PlaybackErrorCode.MediaSourceUnavailable:
|
|
174
180
|
this.core.activeContainer?.getPlugin('poster_custom')?.disable()
|
|
175
|
-
|
|
181
|
+
this.retryPlayback()
|
|
176
182
|
break
|
|
177
|
-
// TODO handle other errors
|
|
178
183
|
default:
|
|
179
184
|
break
|
|
180
185
|
}
|
|
@@ -187,7 +192,6 @@ export class SourceController extends CorePlugin {
|
|
|
187
192
|
})
|
|
188
193
|
if (this.active) {
|
|
189
194
|
this.reset()
|
|
190
|
-
// TODO make poster reset its state on enable
|
|
191
195
|
this.core.activeContainer?.getPlugin('poster_custom')?.enable()
|
|
192
196
|
this.core.activeContainer?.getPlugin('spinner')?.hide()
|
|
193
197
|
}
|
|
@@ -205,6 +209,7 @@ export class SourceController extends CorePlugin {
|
|
|
205
209
|
currentSource: this.sourcesList[this.currentSourceIndex],
|
|
206
210
|
})
|
|
207
211
|
this.active = true
|
|
212
|
+
this.switching = true
|
|
208
213
|
this.core.activeContainer?.getPlugin('spinner')?.show(0)
|
|
209
214
|
this.getNextMediaSource().then((nextSource: PlayerMediaSourceDesc) => {
|
|
210
215
|
trace(`${T} retryPlayback syncing...`, {
|
|
@@ -213,12 +218,12 @@ export class SourceController extends CorePlugin {
|
|
|
213
218
|
const rnd = RETRY_DELAY_BLUR * Math.random()
|
|
214
219
|
this.sync(() => {
|
|
215
220
|
trace(`${T} retryPlayback loading...`)
|
|
221
|
+
this.switching = false
|
|
216
222
|
this.core.load(nextSource.source, nextSource.mimeType)
|
|
217
223
|
trace(`${T} retryPlayback loaded`, {
|
|
218
224
|
nextSource,
|
|
219
225
|
})
|
|
220
226
|
setTimeout(() => {
|
|
221
|
-
// this.core.activePlayback.consent()
|
|
222
227
|
this.core.activePlayback.play()
|
|
223
228
|
trace(`${T} retryPlayback playing`)
|
|
224
229
|
}, rnd)
|
|
@@ -74,7 +74,6 @@ describe('SourceController', () => {
|
|
|
74
74
|
describe('on fatal playback failure', () => {
|
|
75
75
|
let core: any
|
|
76
76
|
let nextPlayback: any
|
|
77
|
-
|
|
78
77
|
describe('basically', () => {
|
|
79
78
|
beforeEach(() => {
|
|
80
79
|
core = createMockCore({
|
|
@@ -215,5 +214,40 @@ describe('SourceController', () => {
|
|
|
215
214
|
})
|
|
216
215
|
})
|
|
217
216
|
})
|
|
217
|
+
describe('given that playback triggers many errors in a row', () => {
|
|
218
|
+
beforeEach(async () => {
|
|
219
|
+
core = createMockCore({
|
|
220
|
+
sources: MOCK_SOURCES,
|
|
221
|
+
})
|
|
222
|
+
const _ = new SourceController(core)
|
|
223
|
+
core.emit('core:ready')
|
|
224
|
+
core.emit('core:active:container:changed')
|
|
225
|
+
core.activePlayback.emit('playback:error', {
|
|
226
|
+
code: PlaybackErrorCode.MediaSourceUnavailable,
|
|
227
|
+
})
|
|
228
|
+
await clock.tickAsync(1)
|
|
229
|
+
core.activePlayback.emit('playback:error', {
|
|
230
|
+
code: PlaybackErrorCode.MediaSourceUnavailable,
|
|
231
|
+
})
|
|
232
|
+
await clock.tickAsync(1)
|
|
233
|
+
core.activePlayback.emit('playback:error', {
|
|
234
|
+
code: PlaybackErrorCode.MediaSourceUnavailable,
|
|
235
|
+
})
|
|
236
|
+
await clock.tickAsync(1)
|
|
237
|
+
nextPlayback = createMockPlayback()
|
|
238
|
+
vi.spyOn(nextPlayback, 'consent')
|
|
239
|
+
vi.spyOn(nextPlayback, 'play')
|
|
240
|
+
core.activePlayback = nextPlayback
|
|
241
|
+
})
|
|
242
|
+
it('should run handler only once', async () => {
|
|
243
|
+
expect(core.load).not.toHaveBeenCalled()
|
|
244
|
+
await clock.tickAsync(1000)
|
|
245
|
+
expect(core.load).toHaveBeenCalledTimes(1)
|
|
246
|
+
expect(core.load).toHaveBeenCalledWith(
|
|
247
|
+
MOCK_SOURCES[1].source,
|
|
248
|
+
MOCK_SOURCES[1].mimeType,
|
|
249
|
+
)
|
|
250
|
+
})
|
|
251
|
+
})
|
|
218
252
|
})
|
|
219
253
|
})
|