@gcorevideo/player 2.22.18 → 2.22.21
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/assets/audio-selector/track-selector.ejs +3 -3
- package/assets/bottom-gear/bottomgear.ejs +3 -3
- package/assets/dvr-controls/dvr_controls.scss +0 -12
- package/assets/level-selector/button.ejs +1 -1
- package/dist/core.js +1 -1
- package/dist/index.css +664 -671
- package/dist/index.js +285 -260
- package/dist/player.d.ts +144 -123
- package/dist/plugins/index.css +758 -765
- package/dist/plugins/index.js +194 -172
- package/docs/api/player.clapprstats.exportmetrics.md +2 -2
- package/docs/api/player.clapprstats.md +0 -4
- package/docs/api/player.clapprstatsbitratetrack.md +20 -0
- package/docs/api/player.clapprstatschronograph.md +115 -0
- package/docs/api/player.clapprstatscounter.md +211 -0
- package/docs/api/player.clapprstatsevents.md +51 -0
- package/docs/api/player.clapprstatsmetrics.md +52 -0
- package/docs/api/player.clipspluginsettings.md +1 -1
- package/docs/api/player.gearevents.md +1 -1
- package/docs/api/player.md +57 -2
- package/docs/api/player.mediacontrol.mount.md +0 -5
- package/docs/api/player.mediacontrol.putelement.md +5 -0
- package/docs/api/player.mediacontrol.toggleelement.md +1 -1
- package/docs/api/player.nerdstats.md +3 -3
- package/docs/api/player.playerconfig.md +1 -1
- package/docs/api/player.playerconfig.playbacktype.md +6 -1
- package/docs/api/player.timeupdate.md +6 -3
- package/lib/plugins/audio-selector/AudioSelector.d.ts +1 -1
- package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
- package/lib/plugins/audio-selector/AudioSelector.js +15 -8
- package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +2 -2
- package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +4 -4
- package/lib/plugins/clappr-nerd-stats/NerdStats.js +4 -4
- package/lib/plugins/clappr-stats/ClapprStats.d.ts +5 -2
- package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
- package/lib/plugins/clappr-stats/ClapprStats.js +31 -33
- package/lib/plugins/clappr-stats/types.d.ts +21 -21
- package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
- package/lib/plugins/clappr-stats/types.js +22 -22
- package/lib/plugins/clappr-stats/utils.d.ts +2 -2
- package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
- package/lib/plugins/click-to-pause/ClickToPause.js +1 -1
- package/lib/plugins/clips/Clips.d.ts +1 -1
- package/lib/plugins/dvr-controls/DvrControls.d.ts +6 -2
- package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
- package/lib/plugins/dvr-controls/DvrControls.js +39 -27
- package/lib/plugins/media-control/MediaControl.d.ts +9 -2
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +26 -10
- package/lib/plugins/picture-in-picture/PictureInPicture.js +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
- package/lib/plugins/vast-ads/VastAds.js +1 -1
- package/lib/plugins/vast-ads/rollmanager.js +1 -1
- package/lib/plugins/volume-fade/VolumeFade.d.ts +25 -10
- package/lib/plugins/volume-fade/VolumeFade.d.ts.map +1 -1
- package/lib/plugins/volume-fade/VolumeFade.js +62 -60
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +7 -4
- package/lib/types.d.ts +1 -1
- package/package.json +3 -3
- package/src/playback/__tests__/HTML5Video.test.ts +2 -2
- package/src/plugins/audio-selector/AudioSelector.ts +14 -7
- package/src/plugins/audio-selector/__tests__/AudioSelector.test.ts +8 -8
- package/src/plugins/audio-selector/__tests__/__snapshots__/AudioSelector.test.ts.snap +15 -15
- package/src/plugins/bottom-gear/BottomGear.ts +2 -2
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +8 -5
- package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +3 -3
- package/src/plugins/clappr-nerd-stats/NerdStats.ts +5 -5
- package/src/plugins/clappr-stats/ClapprStats.ts +41 -40
- package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +12 -12
- package/src/plugins/clappr-stats/types.ts +21 -21
- package/src/plugins/clappr-stats/utils.ts +2 -2
- package/src/plugins/click-to-pause/ClickToPause.ts +1 -1
- package/src/plugins/clips/Clips.ts +1 -1
- package/src/plugins/clips/__tests__/Clips.test.ts +1 -1
- package/src/plugins/clips/__tests__/__snapshots__/Clips.test.ts.snap +1 -1
- package/src/plugins/dvr-controls/DvrControls.ts +51 -37
- package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +84 -26
- package/src/plugins/dvr-controls/__tests__/__snapshots__/DvrControls.test.ts.snap +0 -12
- package/src/plugins/level-selector/__tests__/__snapshots__/QualityLevels.test.ts.snap +1 -1
- package/src/plugins/media-control/MediaControl.ts +27 -10
- package/src/plugins/media-control/__tests__/MediaControl.test.ts +8 -5
- package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +20 -20
- package/src/plugins/picture-in-picture/PictureInPicture.ts +1 -1
- package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
- package/src/plugins/vast-ads/VastAds.ts +1 -1
- package/src/plugins/vast-ads/rollmanager.ts +1 -1
- package/src/plugins/volume-fade/VolumeFade.ts +92 -75
- package/src/testUtils.ts +11 -5
- package/src/types.ts +1 -1
- package/temp/player.api.json +634 -16
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -9,6 +9,7 @@ import '../../../assets/audio-selector/style.scss'
|
|
|
9
9
|
import audioArrow from '../../../assets/icons/old/quality-arrow.svg'
|
|
10
10
|
import { ZeptoResult } from '../../types.js'
|
|
11
11
|
import { MediaControl } from '../media-control/MediaControl.js'
|
|
12
|
+
// import { trace } from '@gcorevideo/utils'
|
|
12
13
|
|
|
13
14
|
const VERSION: string = '2.22.4'
|
|
14
15
|
|
|
@@ -87,7 +88,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
87
88
|
const mediaControl = this.core.getPlugin('media_control')
|
|
88
89
|
assert(mediaControl, 'media_control plugin is required')
|
|
89
90
|
this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, () => {
|
|
90
|
-
mediaControl.
|
|
91
|
+
mediaControl.mount('audiotracks', this.$el)
|
|
91
92
|
})
|
|
92
93
|
this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu)
|
|
93
94
|
}
|
|
@@ -130,12 +131,12 @@ export class AudioTracks extends UICorePlugin {
|
|
|
130
131
|
return this
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
const mediaControl = this.core.getPlugin('media_control') as MediaControl
|
|
134
134
|
this.$el.html(
|
|
135
135
|
AudioTracks.template({
|
|
136
136
|
tracks: this.tracks,
|
|
137
137
|
title: this.getTitle(),
|
|
138
138
|
icon: audioArrow,
|
|
139
|
+
current: this.currentTrack?.id,
|
|
139
140
|
}),
|
|
140
141
|
)
|
|
141
142
|
this.updateText()
|
|
@@ -145,7 +146,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
private onTrackSelect(event: MouseEvent) {
|
|
148
|
-
const id = (event.
|
|
149
|
+
const id = (event.currentTarget as HTMLElement)?.dataset?.audiotracksSelect
|
|
149
150
|
if (id) {
|
|
150
151
|
this.selectAudioTrack(id)
|
|
151
152
|
}
|
|
@@ -155,7 +156,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
private selectAudioTrack(id: string) {
|
|
158
|
-
this.
|
|
159
|
+
this.startTrackSwitching()
|
|
159
160
|
this.core.activeContainer.switchAudioTrack(id)
|
|
160
161
|
this.updateText()
|
|
161
162
|
}
|
|
@@ -165,7 +166,9 @@ export class AudioTracks extends UICorePlugin {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
private toggleContextMenu() {
|
|
168
|
-
this.$el.find('#audiotracks-select').toggleClass('hidden')
|
|
169
|
+
this.$el.find('#audiotracks-select').toggleClass('hidden') // TODO use plain CSS display: none
|
|
170
|
+
const open = !this.$el.find('#audiotracks-select').hasClass('hidden') // TODO hold state
|
|
171
|
+
this.$el.find('#audiotracks-button').attr('aria-expanded', open)
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
private buttonElement(): ZeptoResult {
|
|
@@ -192,7 +195,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
192
195
|
return this.currentTrack.label || this.currentTrack.language
|
|
193
196
|
}
|
|
194
197
|
|
|
195
|
-
private
|
|
198
|
+
private startTrackSwitching() {
|
|
196
199
|
this.buttonElement().addClass('changing')
|
|
197
200
|
}
|
|
198
201
|
|
|
@@ -205,13 +208,17 @@ export class AudioTracks extends UICorePlugin {
|
|
|
205
208
|
|
|
206
209
|
private highlightCurrentTrack() {
|
|
207
210
|
this.trackElement().removeClass('current')
|
|
208
|
-
this.trackElement()
|
|
211
|
+
this.trackElement()
|
|
212
|
+
.find('a')
|
|
213
|
+
.removeClass('gcore-skin-active')
|
|
214
|
+
.attr('aria-checked', 'false')
|
|
209
215
|
|
|
210
216
|
if (this.currentTrack) {
|
|
211
217
|
this.trackElement(this.currentTrack.id)
|
|
212
218
|
.addClass('current')
|
|
213
219
|
.find('a')
|
|
214
220
|
.addClass('gcore-skin-active')
|
|
221
|
+
.attr('aria-checked', 'true')
|
|
215
222
|
}
|
|
216
223
|
}
|
|
217
224
|
}
|
|
@@ -34,7 +34,7 @@ describe('AudioSelector', () => {
|
|
|
34
34
|
emitTracksAvailable(core, TRACKS)
|
|
35
35
|
})
|
|
36
36
|
it('should not attach to the media control', () => {
|
|
37
|
-
expect(mediaControl.
|
|
37
|
+
expect(mediaControl.mount).not.toHaveBeenCalledWith(
|
|
38
38
|
'audiotracks',
|
|
39
39
|
expect.anything(),
|
|
40
40
|
)
|
|
@@ -45,20 +45,20 @@ describe('AudioSelector', () => {
|
|
|
45
45
|
mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
|
|
46
46
|
})
|
|
47
47
|
it('should attach to the media control', () => {
|
|
48
|
-
expect(mediaControl.
|
|
48
|
+
expect(mediaControl.mount).toHaveBeenCalledWith(
|
|
49
49
|
'audiotracks',
|
|
50
50
|
audioSelector.$el,
|
|
51
51
|
)
|
|
52
52
|
})
|
|
53
53
|
})
|
|
54
|
-
describe('
|
|
54
|
+
describe('given that audio tracks are available', () => {
|
|
55
55
|
beforeEach(() => {
|
|
56
56
|
emitTracksAvailable(core, TRACKS)
|
|
57
57
|
})
|
|
58
|
-
it('should render
|
|
58
|
+
it('should render button', () => {
|
|
59
59
|
expect(audioSelector.$el.find('#audiotracks-button').length).toBe(1)
|
|
60
60
|
})
|
|
61
|
-
it('should render
|
|
61
|
+
it('should render menu hidden', () => {
|
|
62
62
|
expect(audioSelector.el.innerHTML).toMatchSnapshot()
|
|
63
63
|
expect(
|
|
64
64
|
audioSelector.$el.find('#audiotracks-select').hasClass('hidden'),
|
|
@@ -68,17 +68,17 @@ describe('AudioSelector', () => {
|
|
|
68
68
|
expect(trackItems.eq(0).text().trim()).toBe('English')
|
|
69
69
|
expect(trackItems.eq(1).text().trim()).toBe('Spanish')
|
|
70
70
|
})
|
|
71
|
-
describe('when
|
|
71
|
+
describe('when button is clicked', () => {
|
|
72
72
|
beforeEach(() => {
|
|
73
73
|
audioSelector.$el.find('#audiotracks-button').click()
|
|
74
74
|
})
|
|
75
|
-
it('should show
|
|
75
|
+
it('should show menu', () => {
|
|
76
76
|
expect(audioSelector.$el.html()).toMatchSnapshot()
|
|
77
77
|
expect(
|
|
78
78
|
audioSelector.$el.find('#audiotracks-select').hasClass('hidden'),
|
|
79
79
|
).toBe(false)
|
|
80
80
|
})
|
|
81
|
-
describe('when
|
|
81
|
+
describe('when audio track is selected', () => {
|
|
82
82
|
beforeEach(() => {
|
|
83
83
|
audioSelector.$el
|
|
84
84
|
.find('#audiotracks-select [data-audiotracks-select="2"]')
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`AudioSelector >
|
|
4
|
-
"<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button">
|
|
3
|
+
exports[`AudioSelector > given that audio tracks are available > should render menu hidden 1`] = `
|
|
4
|
+
"<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button" aria-haspopup="menu" aria-expanded="false">
|
|
5
5
|
<span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
|
|
6
6
|
</button>
|
|
7
|
-
<ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select">
|
|
7
|
+
<ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select" role="menu">
|
|
8
8
|
|
|
9
9
|
<li class="">
|
|
10
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
|
|
10
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
|
|
11
11
|
English
|
|
12
12
|
</a>
|
|
13
13
|
</li>
|
|
14
14
|
|
|
15
15
|
<li class="">
|
|
16
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
|
|
16
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
|
|
17
17
|
Spanish
|
|
18
18
|
</a>
|
|
19
19
|
</li>
|
|
@@ -22,20 +22,20 @@ exports[`AudioSelector > when audio tracks are available > should render the men
|
|
|
22
22
|
"
|
|
23
23
|
`;
|
|
24
24
|
|
|
25
|
-
exports[`AudioSelector >
|
|
26
|
-
"<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button">
|
|
25
|
+
exports[`AudioSelector > given that audio tracks are available > when button is clicked > should show menu 1`] = `
|
|
26
|
+
"<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button" aria-haspopup="menu" aria-expanded="true">
|
|
27
27
|
<span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
|
|
28
28
|
</button>
|
|
29
|
-
<ul class="gcore-skin-bg-color menu" id="audiotracks-select">
|
|
29
|
+
<ul class="gcore-skin-bg-color menu" id="audiotracks-select" role="menu">
|
|
30
30
|
|
|
31
31
|
<li class="">
|
|
32
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
|
|
32
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
|
|
33
33
|
English
|
|
34
34
|
</a>
|
|
35
35
|
</li>
|
|
36
36
|
|
|
37
37
|
<li class="">
|
|
38
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
|
|
38
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
|
|
39
39
|
Spanish
|
|
40
40
|
</a>
|
|
41
41
|
</li>
|
|
@@ -44,20 +44,20 @@ exports[`AudioSelector > when audio tracks are available > when the button is cl
|
|
|
44
44
|
"
|
|
45
45
|
`;
|
|
46
46
|
|
|
47
|
-
exports[`AudioSelector >
|
|
48
|
-
"<button data-audiotracks-button="" class="gcore-skin-button-color changing" id="audiotracks-button">
|
|
47
|
+
exports[`AudioSelector > given that audio tracks are available > when button is clicked > when audio track is selected > should hide the menu 1`] = `
|
|
48
|
+
"<button data-audiotracks-button="" class="gcore-skin-button-color changing" id="audiotracks-button" aria-haspopup="menu" aria-expanded="true">
|
|
49
49
|
<span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
|
|
50
50
|
</button>
|
|
51
|
-
<ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select">
|
|
51
|
+
<ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select" role="menu">
|
|
52
52
|
|
|
53
53
|
<li class="">
|
|
54
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
|
|
54
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
|
|
55
55
|
English
|
|
56
56
|
</a>
|
|
57
57
|
</li>
|
|
58
58
|
|
|
59
59
|
<li class="">
|
|
60
|
-
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
|
|
60
|
+
<a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
|
|
61
61
|
Spanish
|
|
62
62
|
</a>
|
|
63
63
|
</li>
|
|
@@ -21,7 +21,7 @@ const T = 'plugins.bottom_gear'
|
|
|
21
21
|
*/
|
|
22
22
|
export enum GearEvents {
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Subscribe to this event to accurately attach an item to the gear menu
|
|
25
25
|
*/
|
|
26
26
|
RENDERED = 'rendered',
|
|
27
27
|
}
|
|
@@ -276,6 +276,6 @@ export class BottomGear extends UICorePlugin {
|
|
|
276
276
|
private onMediaControlRendered() {
|
|
277
277
|
trace(`${T} onMediaControlRendered`)
|
|
278
278
|
const mediaControl = this.core.getPlugin('media_control')
|
|
279
|
-
mediaControl.
|
|
279
|
+
mediaControl.mount('gear', this.$el)
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -38,7 +38,10 @@ describe('BottomGear', () => {
|
|
|
38
38
|
})
|
|
39
39
|
describe('until media control is rendered', () => {
|
|
40
40
|
it('should not attach to media control', () => {
|
|
41
|
-
expect(mediaControl.
|
|
41
|
+
expect(mediaControl.mount).not.toHaveBeenCalledWith(
|
|
42
|
+
'gear',
|
|
43
|
+
expect.anything(),
|
|
44
|
+
)
|
|
42
45
|
})
|
|
43
46
|
})
|
|
44
47
|
describe('when media control is rendered', () => {
|
|
@@ -46,7 +49,7 @@ describe('BottomGear', () => {
|
|
|
46
49
|
mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
|
|
47
50
|
})
|
|
48
51
|
it('should attach to media control', () => {
|
|
49
|
-
expect(mediaControl.
|
|
52
|
+
expect(mediaControl.mount).toHaveBeenCalledWith('gear', bottomGear.$el)
|
|
50
53
|
})
|
|
51
54
|
})
|
|
52
55
|
describe('when clicked', () => {
|
|
@@ -54,9 +57,9 @@ describe('BottomGear', () => {
|
|
|
54
57
|
bottomGear.$el.find('#gear-button').click()
|
|
55
58
|
})
|
|
56
59
|
it('should toggle the gear menu', () => {
|
|
57
|
-
expect(
|
|
58
|
-
'
|
|
59
|
-
)
|
|
60
|
+
expect(
|
|
61
|
+
bottomGear.$el.find('#gear-options-wrapper').css('display'),
|
|
62
|
+
).not.toBe('none')
|
|
60
63
|
})
|
|
61
64
|
})
|
|
62
65
|
})
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`BottomGear > should render 1`] = `
|
|
4
|
-
"<button
|
|
4
|
+
"<button class="media-control-button gplayer-lite-btn gcore-skin-button-color gear-icon" id="gear-button">
|
|
5
5
|
/assets/icons/new/gear.svg
|
|
6
6
|
</button>
|
|
7
|
-
<div class="gear-wrapper gcore-skin-bg-color" id="gear-options-wrapper" style="display:
|
|
8
|
-
<ul class="gear-options-list" id="gear-options"></ul>
|
|
7
|
+
<div class="gear-wrapper gcore-skin-bg-color" id="gear-options-wrapper" style="display:none">
|
|
8
|
+
<ul class="gear-options-list" id="gear-options" role="menu"></ul>
|
|
9
9
|
</div>
|
|
10
10
|
"
|
|
11
11
|
`;
|
|
@@ -13,7 +13,7 @@ import assert from 'assert'
|
|
|
13
13
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
14
14
|
import {
|
|
15
15
|
ClapprStatsEvents,
|
|
16
|
-
|
|
16
|
+
ClapprStatsMetrics as PerfMetrics,
|
|
17
17
|
} from '../clappr-stats/types.js'
|
|
18
18
|
import { newMetrics as newBaseMetrics } from '../clappr-stats/utils.js'
|
|
19
19
|
import Formatter from './formatter.js'
|
|
@@ -62,16 +62,16 @@ type Metrics = PerfMetrics & {
|
|
|
62
62
|
const T = 'plugins.nerd_stats'
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* `PLUGIN` that displays useful network
|
|
65
|
+
* `PLUGIN` that displays useful statistics regarding the playback as well as the network quality estimation.
|
|
66
66
|
* @beta
|
|
67
67
|
*
|
|
68
68
|
* @remarks
|
|
69
69
|
* Depends on:
|
|
70
70
|
*
|
|
71
|
-
* - {@link BottomGear}
|
|
72
|
-
*
|
|
73
|
-
* - {@link ClapprStats}
|
|
71
|
+
* - {@link BottomGear} - where the button is attached
|
|
74
72
|
*
|
|
73
|
+
* - {@link ClapprStats} - to get the metrics from
|
|
74
|
+
*
|
|
75
75
|
* The plugin is rendered as an item in the gear menu.
|
|
76
76
|
*
|
|
77
77
|
* When clicked, it shows an overlay window with the information about the network speed, latency, etc,
|
|
@@ -8,8 +8,13 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
10
10
|
import { TimerId } from '../../utils/types.js'
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
11
|
+
import type { ClapprStatsMetrics } from './types.js'
|
|
12
|
+
import {
|
|
13
|
+
ClapprStatsEvents,
|
|
14
|
+
ClapprStatsChronograph,
|
|
15
|
+
ClapprStatsCounter,
|
|
16
|
+
} from './types.js'
|
|
17
|
+
export * from './types.js'
|
|
13
18
|
import { newMetrics } from './utils.js'
|
|
14
19
|
|
|
15
20
|
export type ClapprStatsSettings = {
|
|
@@ -26,6 +31,8 @@ export type ClapprStatsSettings = {
|
|
|
26
31
|
* @remarks
|
|
27
32
|
* This plugin does not render anything and is supposed to be extended or used together with other plugins that actually render something.
|
|
28
33
|
*
|
|
34
|
+
* @see {@link NerdStats} - a plugin that visualises the playback metrics
|
|
35
|
+
*
|
|
29
36
|
* Configuration options - {@link ClapprStatsSettings}
|
|
30
37
|
*
|
|
31
38
|
* Events - {@link ClapprStatsEvents}
|
|
@@ -35,14 +42,14 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
35
42
|
|
|
36
43
|
private lastDecodedFramesCount = 0
|
|
37
44
|
|
|
38
|
-
private metrics:
|
|
45
|
+
private metrics: ClapprStatsMetrics = newMetrics()
|
|
39
46
|
|
|
40
|
-
private timers: Record<
|
|
41
|
-
[
|
|
42
|
-
[
|
|
43
|
-
[
|
|
44
|
-
[
|
|
45
|
-
[
|
|
47
|
+
private timers: Record<ClapprStatsChronograph, number> = {
|
|
48
|
+
[ClapprStatsChronograph.Startup]: 0,
|
|
49
|
+
[ClapprStatsChronograph.Watch]: 0,
|
|
50
|
+
[ClapprStatsChronograph.Pause]: 0,
|
|
51
|
+
[ClapprStatsChronograph.Buffering]: 0,
|
|
52
|
+
[ClapprStatsChronograph.Session]: 0,
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
private runEach: number
|
|
@@ -78,21 +85,15 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
78
85
|
: new Date().getTime()
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
private inc(counter:
|
|
88
|
+
private inc(counter: ClapprStatsCounter) {
|
|
82
89
|
this.metrics.counters[counter] += 1
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
// return this[`_start${timer}`] !== undefined;
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
private start(timer: Chronograph) {
|
|
90
|
-
// this[`_start${timer}`] = this._now();
|
|
92
|
+
private start(timer: ClapprStatsChronograph) {
|
|
91
93
|
this.timers[timer] = this.now()
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
private stop(timer:
|
|
95
|
-
// this._metrics.timers[timer] += this._now() - this[`_start${timer}`];
|
|
96
|
+
private stop(timer: ClapprStatsChronograph) {
|
|
96
97
|
this.metrics.chrono[timer] += this.now() - this.timers[timer]
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -131,16 +132,16 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
131
132
|
)
|
|
132
133
|
this.listenTo(this.container, CoreEvents.CONTAINER_SEEK, this.onSeek)
|
|
133
134
|
this.listenTo(this.container, CoreEvents.CONTAINER_ERROR, () =>
|
|
134
|
-
this.inc(
|
|
135
|
+
this.inc(ClapprStatsCounter.Error),
|
|
135
136
|
)
|
|
136
137
|
this.listenTo(this.container, CoreEvents.CONTAINER_FULLSCREEN, () =>
|
|
137
|
-
this.inc(
|
|
138
|
+
this.inc(ClapprStatsCounter.Fullscreen),
|
|
138
139
|
)
|
|
139
140
|
this.listenTo(
|
|
140
141
|
this.container,
|
|
141
142
|
CoreEvents.CONTAINER_PLAYBACKDVRSTATECHANGED,
|
|
142
143
|
(dvrInUse: boolean) => {
|
|
143
|
-
dvrInUse && this.inc(
|
|
144
|
+
dvrInUse && this.inc(ClapprStatsCounter.DvrUsage)
|
|
144
145
|
},
|
|
145
146
|
)
|
|
146
147
|
this.listenTo(
|
|
@@ -186,7 +187,7 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
186
187
|
|
|
187
188
|
this.metrics.extra.bitratesHistory.push({ start: this.now(), bitrate })
|
|
188
189
|
|
|
189
|
-
this.inc(
|
|
190
|
+
this.inc(ClapprStatsCounter.ChangeLevel)
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
private stopReporting() {
|
|
@@ -200,8 +201,8 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
200
201
|
|
|
201
202
|
private startTimers() {
|
|
202
203
|
this.timerId = setInterval(this.buildReport.bind(this), this.runEach)
|
|
203
|
-
this.start(
|
|
204
|
-
this.start(
|
|
204
|
+
this.start(ClapprStatsChronograph.Session)
|
|
205
|
+
this.start(ClapprStatsChronograph.Startup)
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
private onFirstPlaying() {
|
|
@@ -211,8 +212,8 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
211
212
|
this.onContainerUpdateWhilePlaying,
|
|
212
213
|
)
|
|
213
214
|
|
|
214
|
-
this.start(
|
|
215
|
-
this.stop(
|
|
215
|
+
this.start(ClapprStatsChronograph.Watch)
|
|
216
|
+
this.stop(ClapprStatsChronograph.Startup)
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
private playAfterPause() {
|
|
@@ -221,18 +222,18 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
221
222
|
CoreEvents.CONTAINER_TIMEUPDATE,
|
|
222
223
|
this.onContainerUpdateWhilePlaying,
|
|
223
224
|
)
|
|
224
|
-
this.stop(
|
|
225
|
-
this.start(
|
|
225
|
+
this.stop(ClapprStatsChronograph.Pause)
|
|
226
|
+
this.start(ClapprStatsChronograph.Watch)
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
private onPlay() {
|
|
229
|
-
this.inc(
|
|
230
|
+
this.inc(ClapprStatsCounter.Play)
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
private onPause() {
|
|
233
|
-
this.stop(
|
|
234
|
-
this.start(
|
|
235
|
-
this.inc(
|
|
234
|
+
this.stop(ClapprStatsChronograph.Watch)
|
|
235
|
+
this.start(ClapprStatsChronograph.Pause)
|
|
236
|
+
this.inc(ClapprStatsCounter.Pause)
|
|
236
237
|
this.listenToOnce(
|
|
237
238
|
this.container,
|
|
238
239
|
CoreEvents.CONTAINER_PLAY,
|
|
@@ -246,7 +247,7 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
246
247
|
}
|
|
247
248
|
|
|
248
249
|
private onSeek(e: number) {
|
|
249
|
-
this.inc(
|
|
250
|
+
this.inc(ClapprStatsCounter.Seek)
|
|
250
251
|
this.metrics.extra.watchHistory.push([e * 1000, e * 1000])
|
|
251
252
|
}
|
|
252
253
|
|
|
@@ -282,14 +283,14 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
282
283
|
|
|
283
284
|
private onContainerUpdateWhilePlaying() {
|
|
284
285
|
if (this.container.playback.isPlaying()) {
|
|
285
|
-
this.stop(
|
|
286
|
-
this.start(
|
|
286
|
+
this.stop(ClapprStatsChronograph.Watch)
|
|
287
|
+
this.start(ClapprStatsChronograph.Watch)
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
private onBuffering() {
|
|
291
|
-
this.inc(
|
|
292
|
-
this.start(
|
|
292
|
+
this.inc(ClapprStatsCounter.Buffering)
|
|
293
|
+
this.start(ClapprStatsChronograph.Buffering)
|
|
293
294
|
this.listenToOnce(
|
|
294
295
|
this.container,
|
|
295
296
|
CoreEvents.CONTAINER_STATE_BUFFERFULL,
|
|
@@ -298,7 +299,7 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
private onBufferfull() {
|
|
301
|
-
this.stop(
|
|
302
|
+
this.stop(ClapprStatsChronograph.Buffering)
|
|
302
303
|
this.listenToOnce(
|
|
303
304
|
this.container,
|
|
304
305
|
CoreEvents.CONTAINER_STATE_BUFFERING,
|
|
@@ -317,8 +318,8 @@ export class ClapprStats extends ContainerPlugin {
|
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
private buildReport() {
|
|
320
|
-
this.stop(
|
|
321
|
-
this.start(
|
|
321
|
+
this.stop(ClapprStatsChronograph.Session)
|
|
322
|
+
this.start(ClapprStatsChronograph.Session)
|
|
322
323
|
|
|
323
324
|
this.metrics.extra.playbackName = this.playbackName
|
|
324
325
|
this.metrics.extra.playbackType = this.playbackType
|
|
@@ -4,7 +4,7 @@ import FakeTimers from '@sinonjs/fake-timers'
|
|
|
4
4
|
|
|
5
5
|
import { ClapprStats } from '../ClapprStats'
|
|
6
6
|
import { createMockCore } from '../../../testUtils'
|
|
7
|
-
import {
|
|
7
|
+
import { ClapprStatsChronograph, ClapprStatsCounter, ClapprStatsEvents } from '../types'
|
|
8
8
|
|
|
9
9
|
describe('ClapprStats', () => {
|
|
10
10
|
let core: any
|
|
@@ -29,7 +29,7 @@ describe('ClapprStats', () => {
|
|
|
29
29
|
})
|
|
30
30
|
it('should measure', () => {
|
|
31
31
|
const metrics = stats.exportMetrics()
|
|
32
|
-
expect(metrics.chrono[
|
|
32
|
+
expect(metrics.chrono[ClapprStatsChronograph.Startup]).toBe(155)
|
|
33
33
|
// expect(metrics.times[Chronograph.Session]).toBe(155)
|
|
34
34
|
})
|
|
35
35
|
})
|
|
@@ -50,8 +50,8 @@ describe('ClapprStats', () => {
|
|
|
50
50
|
})
|
|
51
51
|
it('should measure cumulative play and pause durations', () => {
|
|
52
52
|
const metrics = stats.exportMetrics()
|
|
53
|
-
expect(metrics.chrono[
|
|
54
|
-
expect(metrics.chrono[
|
|
53
|
+
expect(metrics.chrono[ClapprStatsChronograph.Watch]).toBe(3850)
|
|
54
|
+
expect(metrics.chrono[ClapprStatsChronograph.Pause]).toBe(2900)
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
describe('buffering', () => {
|
|
@@ -71,7 +71,7 @@ describe('ClapprStats', () => {
|
|
|
71
71
|
})
|
|
72
72
|
it('should measure cumulative buffering durations', () => {
|
|
73
73
|
const metrics = stats.exportMetrics()
|
|
74
|
-
expect(metrics.chrono[
|
|
74
|
+
expect(metrics.chrono[ClapprStatsChronograph.Buffering]).toBe(200)
|
|
75
75
|
})
|
|
76
76
|
})
|
|
77
77
|
describe('session', () => {
|
|
@@ -88,7 +88,7 @@ describe('ClapprStats', () => {
|
|
|
88
88
|
it('should measure', () => {
|
|
89
89
|
expect(onReport).toHaveBeenCalledWith(expect.objectContaining({
|
|
90
90
|
chrono: expect.objectContaining({
|
|
91
|
-
[
|
|
91
|
+
[ClapprStatsChronograph.Session]: 60200,
|
|
92
92
|
}),
|
|
93
93
|
}))
|
|
94
94
|
})
|
|
@@ -116,16 +116,16 @@ describe('ClapprStats', () => {
|
|
|
116
116
|
it('should measure fps', () => {
|
|
117
117
|
expect(onReport).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
118
118
|
counters: expect.objectContaining({
|
|
119
|
-
[
|
|
120
|
-
[
|
|
121
|
-
[
|
|
119
|
+
[ClapprStatsCounter.DecodedFrames]: 126,
|
|
120
|
+
[ClapprStatsCounter.DroppedFrames]: 3,
|
|
121
|
+
[ClapprStatsCounter.Fps]: expect.closeTo(25, 0),
|
|
122
122
|
}),
|
|
123
123
|
}))
|
|
124
124
|
expect(onReport).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
125
125
|
counters: expect.objectContaining({
|
|
126
|
-
[
|
|
127
|
-
[
|
|
128
|
-
[
|
|
126
|
+
[ClapprStatsCounter.DecodedFrames]: 275,
|
|
127
|
+
[ClapprStatsCounter.DroppedFrames]: 4,
|
|
128
|
+
[ClapprStatsCounter.Fps]: expect.closeTo(30, 0),
|
|
129
129
|
}),
|
|
130
130
|
}))
|
|
131
131
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beta
|
|
3
3
|
*/
|
|
4
|
-
export enum
|
|
4
|
+
export enum ClapprStatsChronograph {
|
|
5
5
|
Startup = 'startup',
|
|
6
6
|
Watch = 'watch',
|
|
7
7
|
Pause = 'pause',
|
|
@@ -13,7 +13,7 @@ export enum Chronograph {
|
|
|
13
13
|
/**
|
|
14
14
|
* @beta
|
|
15
15
|
*/
|
|
16
|
-
export enum
|
|
16
|
+
export enum ClapprStatsCounter {
|
|
17
17
|
Play = 'play',
|
|
18
18
|
Pause = 'pause',
|
|
19
19
|
Error = 'error',
|
|
@@ -30,7 +30,7 @@ export enum Counter {
|
|
|
30
30
|
/**
|
|
31
31
|
* @beta
|
|
32
32
|
*/
|
|
33
|
-
export type
|
|
33
|
+
export type ClapprStatsMetrics = {
|
|
34
34
|
/**
|
|
35
35
|
* Events count counters
|
|
36
36
|
*/
|
|
@@ -38,17 +38,17 @@ export type Metrics = {
|
|
|
38
38
|
/**
|
|
39
39
|
*
|
|
40
40
|
*/
|
|
41
|
-
[
|
|
42
|
-
[
|
|
43
|
-
[
|
|
44
|
-
[
|
|
45
|
-
[
|
|
46
|
-
[
|
|
47
|
-
[
|
|
48
|
-
[
|
|
49
|
-
[
|
|
50
|
-
[
|
|
51
|
-
[
|
|
41
|
+
[ClapprStatsCounter.Play]: number
|
|
42
|
+
[ClapprStatsCounter.Pause]: number
|
|
43
|
+
[ClapprStatsCounter.Error]: number
|
|
44
|
+
[ClapprStatsCounter.Buffering]: number
|
|
45
|
+
[ClapprStatsCounter.DecodedFrames]: number
|
|
46
|
+
[ClapprStatsCounter.DroppedFrames]: number
|
|
47
|
+
[ClapprStatsCounter.Fps]: number
|
|
48
|
+
[ClapprStatsCounter.ChangeLevel]: number
|
|
49
|
+
[ClapprStatsCounter.Seek]: number
|
|
50
|
+
[ClapprStatsCounter.Fullscreen]: number
|
|
51
|
+
[ClapprStatsCounter.DvrUsage]: number
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Time measurements - accumulated duration of time-based activities
|
|
@@ -57,23 +57,23 @@ export type Metrics = {
|
|
|
57
57
|
/**
|
|
58
58
|
* Time spent in the startup phase
|
|
59
59
|
*/
|
|
60
|
-
[
|
|
60
|
+
[ClapprStatsChronograph.Startup]: number
|
|
61
61
|
/**
|
|
62
62
|
* Total time spent in the watch phase
|
|
63
63
|
*/
|
|
64
|
-
[
|
|
64
|
+
[ClapprStatsChronograph.Watch]: number
|
|
65
65
|
/**
|
|
66
66
|
*
|
|
67
67
|
*/
|
|
68
|
-
[
|
|
69
|
-
[
|
|
70
|
-
[
|
|
68
|
+
[ClapprStatsChronograph.Pause]: number
|
|
69
|
+
[ClapprStatsChronograph.Buffering]: number
|
|
70
|
+
[ClapprStatsChronograph.Session]: number
|
|
71
71
|
// [Chronograph.Latency]: number;
|
|
72
72
|
}
|
|
73
73
|
extra: {
|
|
74
74
|
playbackName: string
|
|
75
75
|
playbackType: string
|
|
76
|
-
bitratesHistory:
|
|
76
|
+
bitratesHistory: ClapprStatsBitrateTrack[]
|
|
77
77
|
bitrateWeightedMean: number
|
|
78
78
|
bitrateMostUsed: number
|
|
79
79
|
buffersize: number
|
|
@@ -89,7 +89,7 @@ export type Metrics = {
|
|
|
89
89
|
/**
|
|
90
90
|
* @beta
|
|
91
91
|
*/
|
|
92
|
-
export type
|
|
92
|
+
export type ClapprStatsBitrateTrack = {
|
|
93
93
|
start: number
|
|
94
94
|
end?: number
|
|
95
95
|
time?: number
|