@gcorevideo/player 2.24.10 → 2.24.13
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/README.md +1 -0
- package/assets/dvr-controls/dvr_controls.scss +43 -80
- package/assets/dvr-controls/index.ejs +8 -2
- package/assets/media-control/width370.scss +1 -1
- package/dist/core.js +34 -23
- package/dist/index.css +394 -420
- package/dist/index.embed.js +107 -71
- package/dist/index.js +172 -134
- package/dist/player.d.ts +3264 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts +2 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +17 -11
- package/lib/playback/hls-playback/HlsPlayback.d.ts +6 -2
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +16 -11
- package/lib/plugins/audio-selector/AudioTracks.js +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +1 -1
- package/lib/plugins/clips/Clips.js +1 -1
- package/lib/plugins/dvr-controls/DvrControls.d.ts +3 -3
- package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
- package/lib/plugins/dvr-controls/DvrControls.js +14 -12
- package/lib/plugins/media-control/MediaControl.d.ts +14 -5
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +55 -29
- package/lib/plugins/picture-in-picture/PictureInPicture.js +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +2 -0
- package/package.json +1 -1
- package/src/playback/dash-playback/DashPlayback.ts +20 -13
- package/src/playback/hls-playback/HlsPlayback.ts +40 -26
- package/src/plugins/audio-selector/AudioTracks.ts +1 -1
- package/src/plugins/audio-selector/__tests__/AudioTracks.test.ts +2 -2
- package/src/plugins/bottom-gear/BottomGear.ts +1 -1
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +2 -2
- package/src/plugins/clips/Clips.ts +1 -1
- package/src/plugins/clips/__tests__/Clips.test.ts +1 -1
- package/src/plugins/dvr-controls/DvrControls.ts +14 -14
- package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +20 -17
- package/src/plugins/dvr-controls/__tests__/__snapshots__/DvrControls.test.ts.snap +4 -2
- package/src/plugins/media-control/MediaControl.ts +69 -35
- package/src/plugins/media-control/__tests__/MediaControl.test.ts +128 -112
- package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +227 -24
- 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/testUtils.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -325,11 +325,10 @@ export default class DashPlayback extends BasePlayback {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
_ready() {
|
|
328
|
-
this.
|
|
329
|
-
|
|
328
|
+
!this._dash && this._setup()
|
|
329
|
+
super._ready()
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
// override
|
|
333
332
|
private override _setupSrc() {
|
|
334
333
|
// this playback manages the src on the video element itself
|
|
335
334
|
}
|
|
@@ -558,10 +557,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
558
557
|
|
|
559
558
|
override play() {
|
|
560
559
|
trace(`${T} play`, { dash: !!this._dash })
|
|
561
|
-
|
|
562
|
-
this._setup()
|
|
563
|
-
}
|
|
564
|
-
|
|
560
|
+
!this._dash && this._setup()
|
|
565
561
|
super.play()
|
|
566
562
|
this._startTimeUpdateTimer()
|
|
567
563
|
}
|
|
@@ -579,14 +575,12 @@ export default class DashPlayback extends BasePlayback {
|
|
|
579
575
|
override stop() {
|
|
580
576
|
if (this._dash) {
|
|
581
577
|
this._stopTimeUpdateTimer()
|
|
582
|
-
this.
|
|
578
|
+
this.destroyInstance()
|
|
583
579
|
super.stop()
|
|
584
|
-
this._dash = null
|
|
585
580
|
}
|
|
586
581
|
}
|
|
587
582
|
|
|
588
|
-
|
|
589
|
-
this._stopTimeUpdateTimer()
|
|
583
|
+
private destroyInstance() {
|
|
590
584
|
if (this._dash) {
|
|
591
585
|
this._dash.off(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError)
|
|
592
586
|
this._dash.off(
|
|
@@ -598,8 +592,13 @@ export default class DashPlayback extends BasePlayback {
|
|
|
598
592
|
this.getDuration,
|
|
599
593
|
)
|
|
600
594
|
this._dash.reset()
|
|
595
|
+
this._dash = null
|
|
601
596
|
}
|
|
602
|
-
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
override destroy() {
|
|
600
|
+
this._stopTimeUpdateTimer()
|
|
601
|
+
this.destroyInstance()
|
|
603
602
|
return super.destroy()
|
|
604
603
|
}
|
|
605
604
|
|
|
@@ -673,7 +672,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
673
672
|
return toClapprTrack(t)
|
|
674
673
|
}
|
|
675
674
|
|
|
676
|
-
switchAudioTrack(id: string): void {
|
|
675
|
+
override switchAudioTrack(id: string): void {
|
|
677
676
|
assert.ok(this._dash, 'DASH.js MediaPlayer is not initialized')
|
|
678
677
|
const tracks = this._dash.getTracksFor('audio')
|
|
679
678
|
const track = tracks.find((t) => t.id === id)
|
|
@@ -681,6 +680,14 @@ export default class DashPlayback extends BasePlayback {
|
|
|
681
680
|
this._dash.setCurrentTrack(track)
|
|
682
681
|
}
|
|
683
682
|
|
|
683
|
+
override load(srcUrl: string) {
|
|
684
|
+
this._stopTimeUpdateTimer()
|
|
685
|
+
this.options.src = srcUrl
|
|
686
|
+
// TODO destroy the instance first?
|
|
687
|
+
this.destroyInstance()
|
|
688
|
+
this._setup()
|
|
689
|
+
}
|
|
690
|
+
|
|
684
691
|
private checkAudioTracks() {
|
|
685
692
|
// @ts-ignore
|
|
686
693
|
const tracks = this._dash.getTracksFor('audio')
|
|
@@ -87,8 +87,6 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
87
87
|
|
|
88
88
|
private _hls: HLSJS | null = null
|
|
89
89
|
|
|
90
|
-
private _isReadyState = false
|
|
91
|
-
|
|
92
90
|
private _lastDuration: number | null = null
|
|
93
91
|
|
|
94
92
|
private _lastTimeUpdate: TimePosition | null = null
|
|
@@ -121,10 +119,16 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
121
119
|
|
|
122
120
|
private _timeUpdateTimer: TimerId | null = null
|
|
123
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @internal
|
|
124
|
+
*/
|
|
124
125
|
get name() {
|
|
125
126
|
return 'hls'
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
128
132
|
get supportedVersion() {
|
|
129
133
|
return { min: CLAPPR_VERSION }
|
|
130
134
|
}
|
|
@@ -137,10 +141,6 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
137
141
|
return this._currentLevel ?? AUTO
|
|
138
142
|
}
|
|
139
143
|
|
|
140
|
-
get isReady() {
|
|
141
|
-
return this._isReadyState
|
|
142
|
-
}
|
|
143
|
-
|
|
144
144
|
set currentLevel(id: number) {
|
|
145
145
|
this._currentLevel = id
|
|
146
146
|
this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START)
|
|
@@ -369,6 +369,7 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
369
369
|
{
|
|
370
370
|
maxBufferLength: 2,
|
|
371
371
|
maxMaxBufferLength: 4,
|
|
372
|
+
autoStartLoad: false,
|
|
372
373
|
},
|
|
373
374
|
this.options.playback.hlsjsConfig,
|
|
374
375
|
)
|
|
@@ -393,6 +394,7 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
393
394
|
this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src)
|
|
394
395
|
})
|
|
395
396
|
|
|
397
|
+
// TODO drop?
|
|
396
398
|
const onPlaying = () => {
|
|
397
399
|
if (this._hls) {
|
|
398
400
|
this._hls.config.maxBufferLength =
|
|
@@ -405,14 +407,15 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
405
407
|
|
|
406
408
|
this.el.addEventListener('playing', onPlaying)
|
|
407
409
|
|
|
408
|
-
this._hls.on(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
)
|
|
410
|
+
this._hls.on(HLSJS.Events.MANIFEST_PARSED, () => {
|
|
411
|
+
this._manifestParsed = true
|
|
412
|
+
this._hls!.startLoad(-1)
|
|
413
|
+
})
|
|
412
414
|
this._hls.on(
|
|
413
415
|
HLSJS.Events.LEVEL_LOADED,
|
|
414
|
-
(evt: HlsEvents.LEVEL_LOADED, data: LevelLoadedData) =>
|
|
415
|
-
this._updatePlaybackType(evt, data)
|
|
416
|
+
(evt: HlsEvents.LEVEL_LOADED, data: LevelLoadedData) => {
|
|
417
|
+
this._updatePlaybackType(evt, data)
|
|
418
|
+
},
|
|
416
419
|
)
|
|
417
420
|
this._hls.on(
|
|
418
421
|
HLSJS.Events.LEVEL_UPDATED,
|
|
@@ -458,7 +461,9 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
458
461
|
this._hls.on(HlsEvents.AUDIO_TRACKS_UPDATED, (evt, data) =>
|
|
459
462
|
this._onAudioTracksUpdated(evt, data),
|
|
460
463
|
)
|
|
461
|
-
this._hls.on(HlsEvents.AUDIO_TRACK_SWITCHED, (evt, data) =>
|
|
464
|
+
this._hls.on(HlsEvents.AUDIO_TRACK_SWITCHED, (evt, data) =>
|
|
465
|
+
this._onAudioTrackSwitched(evt, data),
|
|
466
|
+
)
|
|
462
467
|
this.bindCustomListeners()
|
|
463
468
|
}
|
|
464
469
|
|
|
@@ -499,15 +504,15 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
499
504
|
}
|
|
500
505
|
|
|
501
506
|
protected override _ready() {
|
|
502
|
-
if (this._isReadyState) {
|
|
503
|
-
return
|
|
504
|
-
}
|
|
505
507
|
!this._hls && this._setup()
|
|
506
|
-
|
|
507
|
-
this.trigger(Events.PLAYBACK_READY, this.name)
|
|
508
|
+
super._ready()
|
|
508
509
|
}
|
|
509
510
|
|
|
510
|
-
private _recover(
|
|
511
|
+
private _recover(
|
|
512
|
+
evt: HlsEvents.ERROR,
|
|
513
|
+
data: HlsErrorData,
|
|
514
|
+
error: PlaybackError,
|
|
515
|
+
) {
|
|
511
516
|
assert(this._hls, 'HLS.js is not initialized')
|
|
512
517
|
if (!this._recoveredDecodingError) {
|
|
513
518
|
this._recoveredDecodingError = true
|
|
@@ -808,13 +813,13 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
808
813
|
this.trigger(Events.PLAYBACK_PROGRESS, progress, buffered)
|
|
809
814
|
}
|
|
810
815
|
|
|
811
|
-
load(url: string) {
|
|
816
|
+
override load(url: string) {
|
|
812
817
|
this._stopTimeUpdateTimer()
|
|
813
818
|
this.options.src = url
|
|
814
819
|
this._setup()
|
|
815
820
|
}
|
|
816
821
|
|
|
817
|
-
play() {
|
|
822
|
+
override play() {
|
|
818
823
|
!this._hls && this._setup()
|
|
819
824
|
!this._manifestParsed &&
|
|
820
825
|
!this.options.hlsPlayback.preload &&
|
|
@@ -824,7 +829,7 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
824
829
|
this._startTimeUpdateTimer()
|
|
825
830
|
}
|
|
826
831
|
|
|
827
|
-
pause() {
|
|
832
|
+
override pause() {
|
|
828
833
|
if (!this._hls) {
|
|
829
834
|
return
|
|
830
835
|
}
|
|
@@ -1129,12 +1134,21 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
1129
1134
|
this._hls.audioTrack = Number(id) // TODO or find index by .id == id?
|
|
1130
1135
|
}
|
|
1131
1136
|
|
|
1132
|
-
private _onAudioTracksUpdated(
|
|
1137
|
+
private _onAudioTracksUpdated(
|
|
1138
|
+
_: HlsEvents.AUDIO_TRACKS_UPDATED,
|
|
1139
|
+
data: AudioTracksUpdatedData,
|
|
1140
|
+
) {
|
|
1133
1141
|
trace(`${T} onAudioTracksUpdated`)
|
|
1134
|
-
this.trigger(
|
|
1142
|
+
this.trigger(
|
|
1143
|
+
Events.PLAYBACK_AUDIO_AVAILABLE,
|
|
1144
|
+
data.audioTracks.map(toClapprTrack),
|
|
1145
|
+
)
|
|
1135
1146
|
}
|
|
1136
1147
|
|
|
1137
|
-
private _onAudioTrackSwitched(
|
|
1148
|
+
private _onAudioTrackSwitched(
|
|
1149
|
+
_: HlsEvents.AUDIO_TRACK_SWITCHED,
|
|
1150
|
+
data: AudioTrackSwitchedData,
|
|
1151
|
+
) {
|
|
1138
1152
|
trace(`${T} onAudioTrackSwitched`)
|
|
1139
1153
|
// @ts-ignore
|
|
1140
1154
|
const track = this._hls.audioTracks[data.id]
|
|
@@ -1156,4 +1170,4 @@ function toClapprTrack(t: MediaPlaylist): AudioTrack {
|
|
|
1156
1170
|
kind: t.type === 'main' ? 'main' : 'description', // TODO check
|
|
1157
1171
|
label: t.name,
|
|
1158
1172
|
}
|
|
1159
|
-
}
|
|
1173
|
+
}
|
|
@@ -88,7 +88,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
88
88
|
const mediaControl = this.core.getPlugin('media_control')
|
|
89
89
|
assert(mediaControl, 'media_control plugin is required')
|
|
90
90
|
this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, () => {
|
|
91
|
-
mediaControl.
|
|
91
|
+
mediaControl.slot('audiotracks', this.$el)
|
|
92
92
|
})
|
|
93
93
|
this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu)
|
|
94
94
|
this.listenTo(mediaControl, ExtendedEvents.MEDIACONTROL_MENU_COLLAPSE, (from: string) => {
|
|
@@ -34,7 +34,7 @@ describe('AudioTracks', () => {
|
|
|
34
34
|
emitTracksAvailable(core, TRACKS)
|
|
35
35
|
})
|
|
36
36
|
it('should not attach to the media control', () => {
|
|
37
|
-
expect(mediaControl.
|
|
37
|
+
expect(mediaControl.slot).not.toHaveBeenCalledWith(
|
|
38
38
|
'audiotracks',
|
|
39
39
|
expect.anything(),
|
|
40
40
|
)
|
|
@@ -45,7 +45,7 @@ describe('AudioTracks', () => {
|
|
|
45
45
|
mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
|
|
46
46
|
})
|
|
47
47
|
it('should attach to the media control', () => {
|
|
48
|
-
expect(mediaControl.
|
|
48
|
+
expect(mediaControl.slot).toHaveBeenCalledWith(
|
|
49
49
|
'audiotracks',
|
|
50
50
|
audioTracks.$el,
|
|
51
51
|
)
|
|
@@ -320,7 +320,7 @@ export class BottomGear extends UICorePlugin {
|
|
|
320
320
|
|
|
321
321
|
private mount() {
|
|
322
322
|
const mediaControl = this.core.getPlugin('media_control')
|
|
323
|
-
mediaControl.
|
|
323
|
+
mediaControl.slot('gear', this.$el)
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
private alignSubmenu($subMenu: ZeptoResult) {
|
|
@@ -61,7 +61,7 @@ describe('BottomGear', () => {
|
|
|
61
61
|
})
|
|
62
62
|
describe('until media control is rendered', () => {
|
|
63
63
|
it('should not attach to media control', () => {
|
|
64
|
-
expect(mediaControl.
|
|
64
|
+
expect(mediaControl.slot).not.toHaveBeenCalledWith(
|
|
65
65
|
'gear',
|
|
66
66
|
expect.anything(),
|
|
67
67
|
)
|
|
@@ -72,7 +72,7 @@ describe('BottomGear', () => {
|
|
|
72
72
|
mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
|
|
73
73
|
})
|
|
74
74
|
it('should attach to media control', () => {
|
|
75
|
-
expect(mediaControl.
|
|
75
|
+
expect(mediaControl.slot).toHaveBeenCalledWith('gear', bottomGear.$el)
|
|
76
76
|
})
|
|
77
77
|
})
|
|
78
78
|
describe('when clicked', () => {
|
|
@@ -139,7 +139,7 @@ export class Clips extends UICorePlugin {
|
|
|
139
139
|
private onMcRender() {
|
|
140
140
|
trace(`${T} onMcRender`)
|
|
141
141
|
const mediaControl = this.core.getPlugin('media_control')
|
|
142
|
-
mediaControl.
|
|
142
|
+
mediaControl.slot('clips', this.$el)
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
private onContainerChanged() {
|
|
@@ -66,7 +66,7 @@ describe('Clips', () => {
|
|
|
66
66
|
mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
|
|
67
67
|
})
|
|
68
68
|
it('should mount the indicator', () => {
|
|
69
|
-
expect(mediaControl.
|
|
69
|
+
expect(mediaControl.slot).toHaveBeenCalledWith('clips', clips.$el)
|
|
70
70
|
})
|
|
71
71
|
})
|
|
72
72
|
})
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Events, Playback, UICorePlugin, template } from '@clappr/core'
|
|
2
2
|
import assert from 'assert'
|
|
3
|
+
import { trace } from '@gcorevideo/utils'
|
|
3
4
|
|
|
4
5
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
5
6
|
|
|
6
7
|
import dvrHTML from '../../../assets/dvr-controls/index.ejs'
|
|
7
8
|
import '../../../assets/dvr-controls/dvr_controls.scss'
|
|
8
|
-
// import { trace } from '@gcorevideo/utils'
|
|
9
9
|
import { MediaControl } from '../media-control/MediaControl.js'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const T = 'plugins.dvr_controls'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* `PLUGIN` that adds the DVR controls to the media control UI
|
|
@@ -46,7 +46,7 @@ export class DvrControls extends UICorePlugin {
|
|
|
46
46
|
*/
|
|
47
47
|
override get events() {
|
|
48
48
|
return {
|
|
49
|
-
'click
|
|
49
|
+
'click #gplayer-mc-back-to-live': 'clicked',
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -56,6 +56,7 @@ export class DvrControls extends UICorePlugin {
|
|
|
56
56
|
override get attributes() {
|
|
57
57
|
return {
|
|
58
58
|
class: 'dvr-controls',
|
|
59
|
+
'data-dvr': '',
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -91,7 +92,7 @@ export class DvrControls extends UICorePlugin {
|
|
|
91
92
|
)
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
private
|
|
95
|
+
private clicked() {
|
|
95
96
|
const container = this.core.activeContainer
|
|
96
97
|
if (!container.isPlaying()) {
|
|
97
98
|
container.play()
|
|
@@ -103,19 +104,18 @@ export class DvrControls extends UICorePlugin {
|
|
|
103
104
|
* @internal
|
|
104
105
|
*/
|
|
105
106
|
override render() {
|
|
107
|
+
trace(`${T} render`)
|
|
106
108
|
this.$el.html(
|
|
107
109
|
DvrControls.template({
|
|
108
110
|
i18n: this.core.i18n,
|
|
109
111
|
}),
|
|
110
112
|
)
|
|
113
|
+
this.$el.find('#gplayer-mc-back-to-live').hide()
|
|
114
|
+
this.$el.find('#gplayer-mc-live').hide()
|
|
111
115
|
|
|
112
116
|
return this
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
private onMediacontrolRendered() {
|
|
116
|
-
this.render()
|
|
117
|
-
}
|
|
118
|
-
|
|
119
119
|
private onMetadataLoaded() {
|
|
120
120
|
this.mount()
|
|
121
121
|
this.toggleState(this.core.activeContainer.isDvrInUse())
|
|
@@ -128,10 +128,10 @@ export class DvrControls extends UICorePlugin {
|
|
|
128
128
|
}
|
|
129
129
|
const mediaControl = this.core.getPlugin('media_control') as MediaControl
|
|
130
130
|
assert(mediaControl, 'media_control plugin is required')
|
|
131
|
-
// TODO -> to MediaControl
|
|
131
|
+
// TODO -> to MediaControl (auto hide)
|
|
132
132
|
mediaControl.toggleElement('duration', false)
|
|
133
133
|
mediaControl.toggleElement('position', false)
|
|
134
|
-
mediaControl.mount('
|
|
134
|
+
mediaControl.mount('left', this.$el) // TODO use independent mount point
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
private onDvrStateChanged(dvrInUse: boolean) {
|
|
@@ -140,11 +140,11 @@ export class DvrControls extends UICorePlugin {
|
|
|
140
140
|
|
|
141
141
|
private toggleState(dvrInUse: boolean) {
|
|
142
142
|
if (dvrInUse) {
|
|
143
|
-
this.$el.find('#
|
|
144
|
-
this.$el.find('#
|
|
143
|
+
this.$el.find('#gplayer-mc-back-to-live').show()
|
|
144
|
+
this.$el.find('#gplayer-mc-live').hide()
|
|
145
145
|
} else {
|
|
146
|
-
this.$el.find('#
|
|
147
|
-
this.$el.find('#
|
|
146
|
+
this.$el.find('#gplayer-mc-back-to-live').hide()
|
|
147
|
+
this.$el.find('#gplayer-mc-live').show()
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -33,10 +33,7 @@ describe('DvrControls', () => {
|
|
|
33
33
|
})
|
|
34
34
|
describe('while playback type is unknown', () => {
|
|
35
35
|
it('should not mount', () => {
|
|
36
|
-
expect(mediaControl.
|
|
37
|
-
'dvr',
|
|
38
|
-
dvrControls.$el,
|
|
39
|
-
)
|
|
36
|
+
expect(mediaControl.slot).not.toHaveBeenCalledWith('dvr', dvrControls.$el)
|
|
40
37
|
})
|
|
41
38
|
})
|
|
42
39
|
describe('live stream', () => {
|
|
@@ -75,33 +72,29 @@ describe('DvrControls', () => {
|
|
|
75
72
|
)
|
|
76
73
|
})
|
|
77
74
|
it('should mount to the media control', () => {
|
|
78
|
-
expect(mediaControl.mount).toHaveBeenCalledWith('
|
|
75
|
+
expect(mediaControl.mount).toHaveBeenCalledWith('left', dvrControls.$el)
|
|
79
76
|
})
|
|
80
77
|
if (dvrEnabled) {
|
|
81
78
|
if (dvrInUse) {
|
|
82
79
|
it('should show back_to_live button', () => {
|
|
83
80
|
expect(
|
|
84
|
-
dvrControls.$el
|
|
85
|
-
.find('#media-control-back-to-live')
|
|
86
|
-
.css('display'),
|
|
81
|
+
dvrControls.$el.find('#gplayer-mc-back-to-live').css('display'),
|
|
87
82
|
).not.toBe('none')
|
|
88
83
|
})
|
|
89
|
-
it('should hide live
|
|
84
|
+
it('should hide live indicator', () => {
|
|
90
85
|
expect(
|
|
91
|
-
dvrControls.$el.find('#
|
|
86
|
+
dvrControls.$el.find('#gplayer-mc-live').css('display'),
|
|
92
87
|
).toBe('none')
|
|
93
88
|
})
|
|
94
89
|
} else {
|
|
95
|
-
it('should show live
|
|
90
|
+
it('should show live indicator', () => {
|
|
96
91
|
expect(
|
|
97
|
-
dvrControls.$el.find('#
|
|
92
|
+
dvrControls.$el.find('#gplayer-mc-live').css('display'),
|
|
98
93
|
).not.toBe('none')
|
|
99
94
|
})
|
|
100
95
|
it('should hide back_to_live button', () => {
|
|
101
96
|
expect(
|
|
102
|
-
dvrControls.$el
|
|
103
|
-
.find('#media-control-back-to-live')
|
|
104
|
-
.css('display'),
|
|
97
|
+
dvrControls.$el.find('#gplayer-mc-back-to-live').css('display'),
|
|
105
98
|
).toBe('none')
|
|
106
99
|
})
|
|
107
100
|
}
|
|
@@ -118,7 +111,7 @@ describe('DvrControls', () => {
|
|
|
118
111
|
Events.CONTAINER_PLAYBACKDVRSTATECHANGED,
|
|
119
112
|
true,
|
|
120
113
|
)
|
|
121
|
-
dvrControls.$el.find('#
|
|
114
|
+
dvrControls.$el.find('#gplayer-mc-back-to-live').click()
|
|
122
115
|
})
|
|
123
116
|
it('should play stream', () => {
|
|
124
117
|
expect(core.activeContainer.play).toHaveBeenCalled()
|
|
@@ -139,9 +132,19 @@ describe('DvrControls', () => {
|
|
|
139
132
|
// TODO handle mount points in MediaControl
|
|
140
133
|
it('should not mount', () => {
|
|
141
134
|
expect(mediaControl.mount).not.toHaveBeenCalledWith(
|
|
142
|
-
'
|
|
135
|
+
'left',
|
|
143
136
|
expect.anything(),
|
|
144
137
|
)
|
|
145
138
|
})
|
|
139
|
+
it('should not alter position and duration indicators', () => {
|
|
140
|
+
expect(mediaControl.toggleElement).not.toHaveBeenCalledWith(
|
|
141
|
+
'duration',
|
|
142
|
+
false,
|
|
143
|
+
)
|
|
144
|
+
expect(mediaControl.toggleElement).not.toHaveBeenCalledWith(
|
|
145
|
+
'position',
|
|
146
|
+
false,
|
|
147
|
+
)
|
|
148
|
+
})
|
|
146
149
|
})
|
|
147
150
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`DvrControls > basically > should render 1`] = `
|
|
4
|
-
"<div class="live-info" id="
|
|
5
|
-
<button type="button" class="live-button" aria-label="back_to_live" id="
|
|
4
|
+
"<div class="live-info" id="gplayer-mc-live" style="display: none;">live</div>
|
|
5
|
+
<button type="button" class="live-button" aria-label="back_to_live" id="gplayer-mc-back-to-live" style="display: none;">
|
|
6
|
+
back_to_live
|
|
7
|
+
</button>
|
|
6
8
|
"
|
|
7
9
|
`;
|
|
@@ -56,7 +56,7 @@ export type MediaControlLeftElement =
|
|
|
56
56
|
* Media control elements that appear in main layer, spanning the entire width of the player.
|
|
57
57
|
* @beta
|
|
58
58
|
*/
|
|
59
|
-
export type MediaControlLayerElement = 'seekbar' | 'seekBarContainer' // TODO
|
|
59
|
+
export type MediaControlLayerElement = 'seekbar' | 'seekBarContainer' | '_' // TODO remove seekbar
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Media control elements that appear in the right area.
|
|
@@ -91,6 +91,14 @@ const MANAGED_ELEMENTS: MediaControlElement[] = [
|
|
|
91
91
|
'volume',
|
|
92
92
|
]
|
|
93
93
|
|
|
94
|
+
export type MediaControlSlotSelector =
|
|
95
|
+
| 'root'
|
|
96
|
+
| 'base'
|
|
97
|
+
| 'left'
|
|
98
|
+
| 'right'
|
|
99
|
+
| 'center'
|
|
100
|
+
| 'seekbar'
|
|
101
|
+
|
|
94
102
|
/**
|
|
95
103
|
* Specifies the allowed media control elements in each area.
|
|
96
104
|
* Can be used to restrict rendered media control elements.
|
|
@@ -723,7 +731,9 @@ export class MediaControl extends UICorePlugin {
|
|
|
723
731
|
}
|
|
724
732
|
|
|
725
733
|
private togglePlayStop() {
|
|
726
|
-
this.container.isPlaying()
|
|
734
|
+
this.container.isPlaying()
|
|
735
|
+
? this.container.stop({ ui: true })
|
|
736
|
+
: this.container.play()
|
|
727
737
|
}
|
|
728
738
|
|
|
729
739
|
private startSeekDrag(event: MouseEvent) {
|
|
@@ -897,19 +907,19 @@ export class MediaControl extends UICorePlugin {
|
|
|
897
907
|
}
|
|
898
908
|
|
|
899
909
|
private hideVolumeBar(timeout = 400) {
|
|
900
|
-
if (!this.$volumeBarContainer) {
|
|
901
|
-
return
|
|
902
|
-
}
|
|
903
910
|
if (this.hideVolumeId) {
|
|
904
911
|
clearTimeout(this.hideVolumeId)
|
|
905
912
|
}
|
|
906
913
|
if (this.draggingVolumeBar) {
|
|
907
|
-
this.hideVolumeId = setTimeout(() =>
|
|
914
|
+
this.hideVolumeId = setTimeout(() => {
|
|
915
|
+
this.hideVolumeId = null
|
|
916
|
+
this.hideVolumeBar()
|
|
917
|
+
}, timeout)
|
|
908
918
|
} else {
|
|
909
|
-
this.hideVolumeId = setTimeout(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
)
|
|
919
|
+
this.hideVolumeId = setTimeout(() => {
|
|
920
|
+
this.hideVolumeId = null
|
|
921
|
+
this.$volumeBarContainer.addClass('volume-bar-hide')
|
|
922
|
+
}, timeout)
|
|
913
923
|
}
|
|
914
924
|
}
|
|
915
925
|
|
|
@@ -1133,8 +1143,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1133
1143
|
newSettings.seekEnabled = this.isSeekEnabledForHtml5Playback()
|
|
1134
1144
|
}
|
|
1135
1145
|
|
|
1136
|
-
const settingsChanged =
|
|
1137
|
-
serializeSettings(this.settings) !== serializeSettings(newSettings)
|
|
1146
|
+
const settingsChanged = !isEqualSettings(this.settings, newSettings)
|
|
1138
1147
|
|
|
1139
1148
|
if (settingsChanged) {
|
|
1140
1149
|
this.settings = newSettings
|
|
@@ -1182,34 +1191,52 @@ export class MediaControl extends UICorePlugin {
|
|
|
1182
1191
|
/**
|
|
1183
1192
|
* Get a media control element DOM node
|
|
1184
1193
|
* @param name - The name of the media control element
|
|
1185
|
-
* @
|
|
1194
|
+
* @param element - The DOM node/fragment to mount
|
|
1195
|
+
* @deprecated Use {@link MediaControl.mount} instead
|
|
1196
|
+
*/
|
|
1197
|
+
slot(name: MediaControlElement, element: ZeptoResult): void {
|
|
1198
|
+
const panel = this.getElementLocation(name)
|
|
1199
|
+
if (panel) {
|
|
1200
|
+
element.attr(`data-${name}`, '')
|
|
1201
|
+
mountTo(panel, element)
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Mounts a media control element at a specific location
|
|
1207
|
+
* @param name - The location to mount the element
|
|
1208
|
+
* @param element - The element to mount
|
|
1186
1209
|
* @remarks
|
|
1187
1210
|
* Use this method to render custom media control UI in a plugin
|
|
1188
1211
|
* @example
|
|
1189
1212
|
* ```ts
|
|
1190
1213
|
* class MyPlugin extends UICorePlugin {
|
|
1191
1214
|
* override render() {
|
|
1192
|
-
* this.$el.html('<div
|
|
1193
|
-
* this.core.getPlugin('media_control').mount('
|
|
1215
|
+
* this.$el.html('<div id="my-element" class="my-class">Here we go</div>')
|
|
1216
|
+
* this.core.getPlugin('media_control').mount('left', this.$el)
|
|
1194
1217
|
* return this
|
|
1195
1218
|
* }
|
|
1196
1219
|
* }
|
|
1197
1220
|
* ```
|
|
1198
1221
|
*/
|
|
1199
|
-
mount(name:
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1222
|
+
mount(name: MediaControlSlotSelector, element: ZeptoResult) {
|
|
1223
|
+
mountTo(this.getMountParent(name), element)
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
private getMountParent(name: MediaControlSlotSelector): ZeptoResult {
|
|
1227
|
+
switch (name) {
|
|
1228
|
+
case 'root':
|
|
1229
|
+
return this.$el
|
|
1230
|
+
case 'base':
|
|
1231
|
+
return this.$el.find('.media-control-layer')
|
|
1232
|
+
case 'center':
|
|
1233
|
+
return this.getCenterPanel()
|
|
1234
|
+
case 'left':
|
|
1235
|
+
return this.getLeftPanel()
|
|
1236
|
+
case 'right':
|
|
1237
|
+
return this.getRightPanel()
|
|
1238
|
+
default:
|
|
1239
|
+
assert.fail(`Invalid mount parent name: ${name}`)
|
|
1213
1240
|
}
|
|
1214
1241
|
}
|
|
1215
1242
|
|
|
@@ -1452,17 +1479,14 @@ export class MediaControl extends UICorePlugin {
|
|
|
1452
1479
|
}
|
|
1453
1480
|
}
|
|
1454
1481
|
|
|
1482
|
+
// TODO check if needed
|
|
1455
1483
|
this.$seekBarPosition?.addClass('media-control-notransition')
|
|
1456
1484
|
this.$seekBarScrubber?.addClass('media-control-notransition')
|
|
1457
1485
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
if (this.displayedSeekBarPercentage) {
|
|
1461
|
-
previousSeekPercentage = this.displayedSeekBarPercentage
|
|
1462
|
-
}
|
|
1486
|
+
const prevSeekPercentage = this.displayedSeekBarPercentage || 0
|
|
1463
1487
|
|
|
1464
1488
|
this.displayedSeekBarPercentage = null
|
|
1465
|
-
this.setSeekPercentage(
|
|
1489
|
+
this.setSeekPercentage(prevSeekPercentage)
|
|
1466
1490
|
|
|
1467
1491
|
setTimeout(() => {
|
|
1468
1492
|
!this.settings.seekEnabled &&
|
|
@@ -1609,3 +1633,13 @@ function serializeSettings(s: MediaControlSettings) {
|
|
|
1609
1633
|
.concat([s.seekEnabled as any])
|
|
1610
1634
|
.join(',')
|
|
1611
1635
|
}
|
|
1636
|
+
|
|
1637
|
+
function isEqualSettings(a: MediaControlSettings, b: MediaControlSettings) {
|
|
1638
|
+
return serializeSettings(a) === serializeSettings(b)
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
function mountTo(parent: ZeptoResult, element: ZeptoResult) {
|
|
1642
|
+
if (!parent.find(element).length) {
|
|
1643
|
+
parent.append(element)
|
|
1644
|
+
}
|
|
1645
|
+
}
|