@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
|
@@ -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
|
})
|
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style
|
|
3
3
|
// license that can be found in the LICENSE file.
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
|
|
5
|
+
import {
|
|
6
|
+
Container,
|
|
7
|
+
Events as ClapprEvents,
|
|
8
|
+
UIContainerPlugin,
|
|
9
|
+
template,
|
|
10
|
+
} from '@clappr/core'
|
|
11
|
+
import { trace } from '@gcorevideo/utils'
|
|
12
|
+
|
|
13
|
+
import { PlaybackError, PlaybackErrorCode } from '../../playback.types.js'
|
|
14
|
+
import spinnerHTML from '../../../assets/spinner-three-bounce/spinner.ejs'
|
|
15
|
+
import '../../../assets/spinner-three-bounce/spinner.scss'
|
|
16
|
+
import { TimerId } from '../../utils/types.js'
|
|
17
|
+
import { CLAPPR_VERSION } from '../../build.js'
|
|
13
18
|
|
|
14
19
|
const T = 'plugins.spinner'
|
|
15
20
|
|
|
@@ -28,21 +33,23 @@ export enum SpinnerEvents {
|
|
|
28
33
|
* Shows a pending operation indicator when playback is buffering or in other appropriate cases
|
|
29
34
|
* @beta
|
|
30
35
|
* @remarks
|
|
31
|
-
* The plugin emits
|
|
36
|
+
* The plugin emits
|
|
32
37
|
*/
|
|
33
38
|
export class SpinnerThreeBounce extends UIContainerPlugin {
|
|
39
|
+
private userShown = false
|
|
40
|
+
|
|
34
41
|
/**
|
|
35
42
|
* @internal
|
|
36
43
|
*/
|
|
37
44
|
get name() {
|
|
38
|
-
return 'spinner'
|
|
45
|
+
return 'spinner'
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
49
|
* @internal
|
|
43
50
|
*/
|
|
44
51
|
get supportedVersion() {
|
|
45
|
-
return { min: CLAPPR_VERSION }
|
|
52
|
+
return { min: CLAPPR_VERSION }
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
/**
|
|
@@ -50,47 +57,53 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
|
|
|
50
57
|
*/
|
|
51
58
|
override get attributes() {
|
|
52
59
|
return {
|
|
53
|
-
'data-spinner':'',
|
|
54
|
-
|
|
55
|
-
}
|
|
60
|
+
'data-spinner': '',
|
|
61
|
+
class: 'spinner-three-bounce',
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
private showTimeout: TimerId | null = null;
|
|
65
|
+
private showTimeout: TimerId | null = null
|
|
61
66
|
|
|
62
|
-
private template = template(spinnerHTML)
|
|
67
|
+
private template = template(spinnerHTML)
|
|
63
68
|
|
|
64
69
|
private hasFatalError = false
|
|
65
70
|
|
|
66
71
|
private hasBuffering = false
|
|
67
72
|
|
|
68
73
|
constructor(container: Container) {
|
|
69
|
-
super(container)
|
|
70
|
-
this.listenTo(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.listenTo(
|
|
76
|
-
|
|
74
|
+
super(container)
|
|
75
|
+
this.listenTo(
|
|
76
|
+
this.container,
|
|
77
|
+
ClapprEvents.CONTAINER_STATE_BUFFERING,
|
|
78
|
+
this.onBuffering,
|
|
79
|
+
)
|
|
80
|
+
this.listenTo(
|
|
81
|
+
this.container,
|
|
82
|
+
ClapprEvents.CONTAINER_STATE_BUFFERFULL,
|
|
83
|
+
this.onBufferFull,
|
|
84
|
+
)
|
|
85
|
+
this.listenTo(this.container, ClapprEvents.CONTAINER_PLAY, this.onPlay)
|
|
86
|
+
this.listenTo(this.container, ClapprEvents.CONTAINER_STOP, this.onStop)
|
|
87
|
+
this.listenTo(this.container, ClapprEvents.CONTAINER_ENDED, this.onStop)
|
|
88
|
+
this.listenTo(this.container, ClapprEvents.CONTAINER_ERROR, this.onError)
|
|
89
|
+
this.listenTo(this.container, ClapprEvents.CONTAINER_READY, this.render)
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
private onBuffering() {
|
|
80
93
|
this.hasBuffering = true
|
|
81
|
-
this.
|
|
94
|
+
this._show()
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
private onBufferFull() {
|
|
85
98
|
if (!this.hasFatalError && this.hasBuffering) {
|
|
86
|
-
this.
|
|
99
|
+
this._hide()
|
|
87
100
|
}
|
|
88
101
|
this.hasBuffering = false
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
private onPlay() {
|
|
92
|
-
trace(`${T} onPlay`)
|
|
93
|
-
this.
|
|
105
|
+
trace(`${T} onPlay`)
|
|
106
|
+
this._hide()
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
private onStop() {
|
|
@@ -99,7 +112,7 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
|
|
|
99
112
|
hasFatalError: this.hasFatalError,
|
|
100
113
|
})
|
|
101
114
|
if (!(this.hasFatalError && this.options.spinner?.showOnError)) {
|
|
102
|
-
this.
|
|
115
|
+
this._hide()
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -112,65 +125,75 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
|
|
|
112
125
|
error: e.code,
|
|
113
126
|
})
|
|
114
127
|
if (this.options.spinner?.showOnError) {
|
|
115
|
-
this.
|
|
128
|
+
this._show()
|
|
116
129
|
} else {
|
|
117
|
-
this.
|
|
130
|
+
this._hide()
|
|
118
131
|
}
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
/**
|
|
122
|
-
* Shows the spinner
|
|
135
|
+
* Shows the spinner.
|
|
136
|
+
*
|
|
137
|
+
* When called, the spinner will not hide (due to its built-in logic) until {@link SpinnerThreeBounce#hide} is called
|
|
123
138
|
*/
|
|
124
139
|
show(delay = 300) {
|
|
125
140
|
trace(`${T} show`)
|
|
141
|
+
this.userShown = true
|
|
142
|
+
this._show(delay)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Hides the spinner.
|
|
147
|
+
*/
|
|
148
|
+
hide() {
|
|
149
|
+
this.userShown = false
|
|
150
|
+
this._hide()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _show(delay = 300) {
|
|
126
154
|
if (this.showTimeout === null) {
|
|
127
|
-
if (this.hideTimeout !== null) {
|
|
128
|
-
clearTimeout(this.hideTimeout);
|
|
129
|
-
this.hideTimeout = null;
|
|
130
|
-
}
|
|
131
155
|
this.showTimeout = setTimeout(() => {
|
|
132
156
|
this.showTimeout = null
|
|
133
157
|
this.$el.show()
|
|
134
|
-
}, delay)
|
|
158
|
+
}, delay)
|
|
135
159
|
}
|
|
136
160
|
}
|
|
137
161
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
private _hide() {
|
|
163
|
+
trace(`${T} _hide`, {
|
|
164
|
+
userShown: this.userShown,
|
|
165
|
+
})
|
|
166
|
+
if (this.userShown) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
142
169
|
if (this.showTimeout !== null) {
|
|
143
|
-
clearTimeout(this.showTimeout)
|
|
144
|
-
this.showTimeout = null
|
|
170
|
+
clearTimeout(this.showTimeout)
|
|
171
|
+
this.showTimeout = null
|
|
145
172
|
}
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
if (this.showTimeout === null) {
|
|
149
|
-
this.$el.hide();
|
|
150
|
-
}
|
|
151
|
-
}, 0);
|
|
173
|
+
this.$el.hide()
|
|
174
|
+
this.trigger(SpinnerEvents.SYNC) // TODO test
|
|
152
175
|
}
|
|
153
176
|
|
|
154
177
|
/**
|
|
155
178
|
* @internal
|
|
156
179
|
*/
|
|
157
180
|
override render() {
|
|
158
|
-
const showOnStart = this.options.spinner?.showOnStart
|
|
181
|
+
const showOnStart = this.options.spinner?.showOnStart
|
|
159
182
|
trace(`${T} render`, {
|
|
160
183
|
buffering: this.container.buffering,
|
|
161
184
|
showOnStart,
|
|
162
185
|
})
|
|
163
|
-
this.$el.html(this.template())
|
|
186
|
+
this.$el.html(this.template())
|
|
164
187
|
this.el.firstElementChild?.addEventListener('animationiteration', () => {
|
|
165
188
|
this.trigger(SpinnerEvents.SYNC)
|
|
166
189
|
})
|
|
167
|
-
this.container.$el.append(this.$el[0])
|
|
190
|
+
this.container.$el.append(this.$el[0])
|
|
168
191
|
if (showOnStart || this.container.buffering) {
|
|
169
|
-
this.
|
|
192
|
+
this._show()
|
|
170
193
|
} else {
|
|
171
|
-
this.
|
|
194
|
+
this._hide()
|
|
172
195
|
}
|
|
173
196
|
|
|
174
|
-
return this
|
|
197
|
+
return this
|
|
175
198
|
}
|
|
176
199
|
}
|
package/src/testUtils.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { $, UICorePlugin } from '@clappr/core'
|
|
1
2
|
import Events from 'eventemitter3'
|
|
2
3
|
import { vi } from 'vitest'
|
|
3
4
|
/**
|
|
@@ -83,12 +84,10 @@ export class _MockPlayback extends Events {
|
|
|
83
84
|
|
|
84
85
|
export function createMockCore(options: Record<string, unknown> = {}, container: any = createMockContainer()) {
|
|
85
86
|
const el = document.createElement('div')
|
|
86
|
-
|
|
87
|
+
const emitter = new Events()
|
|
88
|
+
return Object.assign(emitter, {
|
|
87
89
|
el,
|
|
88
|
-
$el:
|
|
89
|
-
[0]: el,
|
|
90
|
-
append: vi.fn(),
|
|
91
|
-
},
|
|
90
|
+
$el: $(el),
|
|
92
91
|
activePlayback: container.playback,
|
|
93
92
|
activeContainer: container,
|
|
94
93
|
options: {
|
|
@@ -97,6 +96,7 @@ export function createMockCore(options: Record<string, unknown> = {}, container:
|
|
|
97
96
|
configure: vi.fn(),
|
|
98
97
|
getPlugin: vi.fn(),
|
|
99
98
|
load: vi.fn(),
|
|
99
|
+
trigger: emitter.emit,
|
|
100
100
|
})
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -166,12 +166,19 @@ export function createMockPlayback(name = 'mock') {
|
|
|
166
166
|
export function createMockContainer(playback: any = createMockPlayback()) {
|
|
167
167
|
const el = document.createElement('div')
|
|
168
168
|
return Object.assign(new Events(), {
|
|
169
|
-
$el:
|
|
170
|
-
html: vi.fn(),
|
|
171
|
-
[0]: el,
|
|
172
|
-
},
|
|
169
|
+
$el: $(el),
|
|
173
170
|
el,
|
|
174
171
|
getPlugin: vi.fn(),
|
|
175
172
|
playback,
|
|
176
173
|
})
|
|
177
174
|
}
|
|
175
|
+
|
|
176
|
+
export function createMockMediaControl(core: any) {
|
|
177
|
+
const mediaControl = new UICorePlugin(core)
|
|
178
|
+
const elements = {
|
|
179
|
+
gear: $(document.createElement('div')),
|
|
180
|
+
}
|
|
181
|
+
// @ts-ignore
|
|
182
|
+
mediaControl.getElement = vi.fn().mockImplementation((name) => elements[name])
|
|
183
|
+
return mediaControl
|
|
184
|
+
}
|