@gcorevideo/player 2.28.29 → 2.28.35
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 +400 -400
- package/dist/index.embed.js +92 -74
- package/dist/index.js +150 -134
- package/docs/api/player.closedcaptionspluginsettings.md +1 -0
- package/docs/api/player.md +9 -0
- package/docs/api/player.mediacontrol.md +16 -0
- package/docs/api/player.thumbnails.md +1 -1
- package/lib/Player.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.d.ts +5 -6
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +40 -38
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +6 -2
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +37 -23
- package/lib/plugins/utils/mobile.d.ts +2 -0
- package/lib/plugins/utils/mobile.d.ts.map +1 -0
- package/lib/plugins/utils/mobile.js +4 -0
- package/lib/testUtils.d.ts +1 -0
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +1 -0
- package/package.json +4 -1
- package/src/Player.ts +12 -12
- package/src/plugins/media-control/MediaControl.ts +45 -43
- package/src/plugins/media-control/__tests__/MediaControl.test.ts +63 -35
- package/src/plugins/subtitles/ClosedCaptions.ts +42 -28
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +293 -6
- package/src/plugins/utils/mobile.ts +5 -0
- package/src/testUtils.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -26,6 +26,8 @@ import { CLAPPR_VERSION } from '../../build.js'
|
|
|
26
26
|
import { ZeptoResult } from '../../types.js'
|
|
27
27
|
import { getPageX } from '../utils.js'
|
|
28
28
|
import { fullscreenEnabled, isFullscreen } from '../utils/fullscreen.js'
|
|
29
|
+
import { isMobile } from '../utils/mobile.js'
|
|
30
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
29
31
|
|
|
30
32
|
import '../../../assets/media-control/media-control.scss'
|
|
31
33
|
|
|
@@ -37,7 +39,6 @@ import volumeMaxIcon from '../../../assets/icons/new/volume-max.svg'
|
|
|
37
39
|
import volumeOffIcon from '../../../assets/icons/new/volume-off.svg'
|
|
38
40
|
import fullscreenOffIcon from '../../../assets/icons/new/fullscreen-off.svg'
|
|
39
41
|
import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg'
|
|
40
|
-
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
41
42
|
|
|
42
43
|
const STANDARD_MEDIA_CONTROL_ELEMENTS: string[] = [
|
|
43
44
|
'duration',
|
|
@@ -352,8 +353,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
352
353
|
'touchmove .bar-container[data-seekbar]': 'mousemoveOnSeekBar',
|
|
353
354
|
'mouseleave .bar-container[data-seekbar]': 'mouseleaveOnSeekBar',
|
|
354
355
|
'touchend .bar-container[data-seekbar]': 'mouseleaveOnSeekBar',
|
|
355
|
-
'mouseenter .media-control-layer[data-controls]': 'setUserKeepVisible',
|
|
356
|
-
'mouseleave .media-control-layer[data-controls]': 'resetUserKeepVisible',
|
|
357
356
|
}
|
|
358
357
|
}
|
|
359
358
|
|
|
@@ -470,9 +469,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
470
469
|
Events.CONTAINER_DBLCLICK,
|
|
471
470
|
this.toggleFullscreen,
|
|
472
471
|
)
|
|
473
|
-
this.listenTo(this.core.activeContainer, Events.CONTAINER_CLICK, () =>
|
|
474
|
-
this.clickaway(this.core.activeContainer.$el[0]),
|
|
475
|
-
)
|
|
476
472
|
this.listenTo(
|
|
477
473
|
this.core.activeContainer,
|
|
478
474
|
Events.CONTAINER_TIMEUPDATE,
|
|
@@ -526,6 +522,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
526
522
|
)
|
|
527
523
|
this.listenTo(this.core, Events.CONTAINER_DESTROYED, () => {
|
|
528
524
|
this.cancelRenderTimer()
|
|
525
|
+
this.setKeepVisible(false)
|
|
529
526
|
})
|
|
530
527
|
this.listenTo(
|
|
531
528
|
this.core.activeContainer,
|
|
@@ -537,10 +534,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
537
534
|
Events.CONTAINER_MOUSE_LEAVE,
|
|
538
535
|
this.delayHide,
|
|
539
536
|
)
|
|
540
|
-
|
|
541
|
-
this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
|
|
542
|
-
this.clickaway(null)
|
|
543
|
-
})
|
|
544
537
|
}
|
|
545
538
|
|
|
546
539
|
/**
|
|
@@ -692,7 +685,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
692
685
|
this.$playPauseToggle?.append(playIcon)
|
|
693
686
|
this.$playStopToggle?.append(playIcon)
|
|
694
687
|
this.trigger(Events.MEDIACONTROL_NOTPLAYING)
|
|
695
|
-
if (
|
|
688
|
+
if (isMobile()) {
|
|
696
689
|
this.show()
|
|
697
690
|
}
|
|
698
691
|
}
|
|
@@ -745,7 +738,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
745
738
|
width: this.container.$el.width(),
|
|
746
739
|
height: this.container.$el.height(),
|
|
747
740
|
hideVolumeBar: this.options.hideVolumeBar,
|
|
748
|
-
isMobile:
|
|
741
|
+
isMobile: isMobile(),
|
|
749
742
|
})
|
|
750
743
|
|
|
751
744
|
try {
|
|
@@ -754,7 +747,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
754
747
|
this.$el.addClass('w370')
|
|
755
748
|
}
|
|
756
749
|
|
|
757
|
-
if (skinWidth <= 270 && !
|
|
750
|
+
if (skinWidth <= 270 && !isMobile()) {
|
|
758
751
|
this.verticalVolume = true
|
|
759
752
|
this.$el.addClass('w270')
|
|
760
753
|
}
|
|
@@ -769,6 +762,18 @@ export class MediaControl extends UICorePlugin {
|
|
|
769
762
|
return false
|
|
770
763
|
}
|
|
771
764
|
|
|
765
|
+
private play() {
|
|
766
|
+
this.container && this.container.play()
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
private pause() {
|
|
770
|
+
this.container && this.container.pause()
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
private stop() {
|
|
774
|
+
this.container && this.container.stop()
|
|
775
|
+
}
|
|
776
|
+
|
|
772
777
|
private togglePlayStop() {
|
|
773
778
|
this.container.isPlaying()
|
|
774
779
|
? this.container.stop({ ui: true })
|
|
@@ -893,11 +898,11 @@ export class MediaControl extends UICorePlugin {
|
|
|
893
898
|
}
|
|
894
899
|
|
|
895
900
|
private toggleFullscreen() {
|
|
896
|
-
if (!
|
|
901
|
+
if (!isMobile()) {
|
|
897
902
|
this.trigger(Events.MEDIACONTROL_FULLSCREEN, this.name)
|
|
898
903
|
this.core.activeContainer.fullscreen()
|
|
899
904
|
this.core.toggleFullscreen()
|
|
900
|
-
this.resetUserKeepVisible()
|
|
905
|
+
// this.resetUserKeepVisible()
|
|
901
906
|
}
|
|
902
907
|
}
|
|
903
908
|
|
|
@@ -1048,15 +1053,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
1048
1053
|
this.setSeekPercentage(pos)
|
|
1049
1054
|
}
|
|
1050
1055
|
|
|
1051
|
-
private setUserKeepVisible(e?: MouseEvent) {
|
|
1052
|
-
this.userKeepVisible = true
|
|
1053
|
-
this.clickaway(this.core.activeContainer.$el[0])
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
private resetUserKeepVisible = (e?: MouseEvent) => {
|
|
1057
|
-
this.userKeepVisible = false
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
1056
|
private isVisible() {
|
|
1061
1057
|
return !this.$el.hasClass('media-control-hide')
|
|
1062
1058
|
}
|
|
@@ -1066,7 +1062,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
1066
1062
|
return
|
|
1067
1063
|
}
|
|
1068
1064
|
|
|
1069
|
-
const timeout = DEFAULT_HIDE_DELAY
|
|
1070
1065
|
const mousePointerMoved =
|
|
1071
1066
|
event &&
|
|
1072
1067
|
event.clientX !== this.lastMouseX &&
|
|
@@ -1077,6 +1072,8 @@ export class MediaControl extends UICorePlugin {
|
|
|
1077
1072
|
clearTimeout(this.hideId)
|
|
1078
1073
|
this.hideId = null
|
|
1079
1074
|
}
|
|
1075
|
+
this.hideId = setTimeout(() => this.hide(), DEFAULT_HIDE_DELAY)
|
|
1076
|
+
|
|
1080
1077
|
this.$el.show()
|
|
1081
1078
|
this.trigger(Events.MEDIACONTROL_SHOW, this.name)
|
|
1082
1079
|
this.core.activeContainer?.trigger(
|
|
@@ -1084,15 +1081,13 @@ export class MediaControl extends UICorePlugin {
|
|
|
1084
1081
|
this.name,
|
|
1085
1082
|
)
|
|
1086
1083
|
this.$el.removeClass('media-control-hide')
|
|
1087
|
-
this.hideId = setTimeout(() => this.hide(), timeout)
|
|
1088
1084
|
if (event) {
|
|
1089
1085
|
this.lastMouseX = event.clientX
|
|
1090
1086
|
this.lastMouseY = event.clientY
|
|
1091
1087
|
}
|
|
1092
1088
|
}
|
|
1093
|
-
const showing = true
|
|
1094
1089
|
|
|
1095
|
-
this.updateCursorStyle(
|
|
1090
|
+
this.updateCursorStyle(true)
|
|
1096
1091
|
}
|
|
1097
1092
|
|
|
1098
1093
|
private hide(delay = 0) {
|
|
@@ -1100,10 +1095,9 @@ export class MediaControl extends UICorePlugin {
|
|
|
1100
1095
|
return
|
|
1101
1096
|
}
|
|
1102
1097
|
|
|
1103
|
-
const timeout = delay || 2000
|
|
1104
|
-
|
|
1105
1098
|
if (this.hideId !== null) {
|
|
1106
1099
|
clearTimeout(this.hideId)
|
|
1100
|
+
this.hideId = null
|
|
1107
1101
|
}
|
|
1108
1102
|
|
|
1109
1103
|
if (!this.disabled && this.options.hideMediaControl === false) {
|
|
@@ -1117,7 +1111,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1117
1111
|
!this.disabled &&
|
|
1118
1112
|
(delay || hasKeepVisibleRequested || hasDraggingAction)
|
|
1119
1113
|
) {
|
|
1120
|
-
this.hideId = setTimeout(() => this.hide(),
|
|
1114
|
+
this.hideId = setTimeout(() => this.hide(), delay || 2000)
|
|
1121
1115
|
} else {
|
|
1122
1116
|
if (!this.options.controlsDontHide || isFullscreen(this.container.el)) {
|
|
1123
1117
|
this.trigger(Events.MEDIACONTROL_HIDE, this.name)
|
|
@@ -1278,18 +1272,13 @@ export class MediaControl extends UICorePlugin {
|
|
|
1278
1272
|
/**
|
|
1279
1273
|
* Set or reset the keep visibility state
|
|
1280
1274
|
*
|
|
1281
|
-
* Keep visibility state controls whether the media control is hidden automatically after a delay.
|
|
1282
|
-
* Keep visibility prevents the the auto-hide behaviour
|
|
1275
|
+
* Keep visibility state controls whether the media control is hidden automatically after a delay, which is a default behaviour.
|
|
1283
1276
|
*
|
|
1284
1277
|
* @param keepVisible - The state
|
|
1285
1278
|
*/
|
|
1286
1279
|
setKeepVisible(keepVisible: boolean) {
|
|
1287
1280
|
this.keepVisible = keepVisible
|
|
1288
|
-
|
|
1289
|
-
this.clickaway(this.core.activeContainer.$el[0])
|
|
1290
|
-
} else {
|
|
1291
|
-
this.clickaway(null)
|
|
1292
|
-
}
|
|
1281
|
+
this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null)
|
|
1293
1282
|
}
|
|
1294
1283
|
|
|
1295
1284
|
private getMountParent(name: MediaControlSlotMountPoint): ZeptoResult {
|
|
@@ -1390,7 +1379,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1390
1379
|
}
|
|
1391
1380
|
|
|
1392
1381
|
private bindKeyEvents() {
|
|
1393
|
-
if (
|
|
1382
|
+
if (isMobile() || this.options.disableKeyboardShortcuts) {
|
|
1394
1383
|
return
|
|
1395
1384
|
}
|
|
1396
1385
|
|
|
@@ -1491,6 +1480,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1491
1480
|
* @internal
|
|
1492
1481
|
*/
|
|
1493
1482
|
override destroy() {
|
|
1483
|
+
this.cancelTimers()
|
|
1494
1484
|
this.cancelRenderTimer()
|
|
1495
1485
|
$(document).unbind('mouseup', this.stopDrag)
|
|
1496
1486
|
$(document).unbind('mousemove', this.updateDrag)
|
|
@@ -1500,6 +1490,18 @@ export class MediaControl extends UICorePlugin {
|
|
|
1500
1490
|
return super.destroy()
|
|
1501
1491
|
}
|
|
1502
1492
|
|
|
1493
|
+
private cancelTimers() {
|
|
1494
|
+
if (this.hideId !== null) {
|
|
1495
|
+
clearTimeout(this.hideId)
|
|
1496
|
+
this.hideId = null
|
|
1497
|
+
}
|
|
1498
|
+
if (this.hideVolumeId !== null) {
|
|
1499
|
+
clearTimeout(this.hideVolumeId)
|
|
1500
|
+
this.hideVolumeId = null
|
|
1501
|
+
}
|
|
1502
|
+
this.cancelRenderTimer()
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1503
1505
|
private cancelRenderTimer() {
|
|
1504
1506
|
if (this.renderTimerId) {
|
|
1505
1507
|
clearTimeout(this.renderTimerId)
|
|
@@ -1537,7 +1539,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1537
1539
|
|
|
1538
1540
|
// Video volume cannot be changed with Safari on mobile devices
|
|
1539
1541
|
// Display mute/unmute icon only if Safari version >= 10
|
|
1540
|
-
if (Browser.isSafari &&
|
|
1542
|
+
if (Browser.isSafari && isMobile()) {
|
|
1541
1543
|
if (Browser.version < 10) {
|
|
1542
1544
|
this.$volumeContainer?.css({ display: 'none' })
|
|
1543
1545
|
} else {
|
|
@@ -1557,7 +1559,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1557
1559
|
setTimeout(() => {
|
|
1558
1560
|
!this.settings.seekEnabled &&
|
|
1559
1561
|
this.$seekBarContainer?.addClass('seek-disabled')
|
|
1560
|
-
!
|
|
1562
|
+
!isMobile() &&
|
|
1561
1563
|
!this.options.disableKeyboardShortcuts &&
|
|
1562
1564
|
this.bindKeyEvents()
|
|
1563
1565
|
this.playerResize({
|
|
@@ -1587,6 +1589,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1587
1589
|
this.container.fullscreen()
|
|
1588
1590
|
// TODO: fix after it full screen will be fixed on iOS
|
|
1589
1591
|
if (Browser.isiOS) {
|
|
1592
|
+
// TODO use isFullscreen utility function
|
|
1590
1593
|
if (this.core.isFullscreen()) {
|
|
1591
1594
|
Fullscreen.cancelFullscreen(this.core.el)
|
|
1592
1595
|
} else {
|
|
@@ -1595,7 +1598,6 @@ export class MediaControl extends UICorePlugin {
|
|
|
1595
1598
|
} else {
|
|
1596
1599
|
this.core.toggleFullscreen()
|
|
1597
1600
|
}
|
|
1598
|
-
this.resetUserKeepVisible()
|
|
1599
1601
|
}
|
|
1600
1602
|
|
|
1601
1603
|
private static getPageX(event: MouseEvent | TouchEvent): number {
|
|
@@ -1653,7 +1655,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1653
1655
|
this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY)
|
|
1654
1656
|
}
|
|
1655
1657
|
|
|
1656
|
-
private clickaway = mediaControlClickaway(() => this.
|
|
1658
|
+
private clickaway = mediaControlClickaway(() => this.setKeepVisible(false))
|
|
1657
1659
|
}
|
|
1658
1660
|
|
|
1659
1661
|
MediaControl.extend = function (properties) {
|
|
@@ -237,41 +237,38 @@ describe('MediaControl', () => {
|
|
|
237
237
|
)
|
|
238
238
|
expect(el.length).toEqual(0)
|
|
239
239
|
})
|
|
240
|
-
it(`should ${
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
expect(fullscreen.length).toEqual(0)
|
|
273
|
-
}
|
|
274
|
-
})
|
|
240
|
+
it(`should ${settings.seekEnabled ? '' : 'not '
|
|
241
|
+
}render the seek bar`, () => {
|
|
242
|
+
const seekbar = mediaControl.$el.find(
|
|
243
|
+
'.media-control-center-panel [data-seekbar]',
|
|
244
|
+
)
|
|
245
|
+
expect(seekbar.length).toBeGreaterThan(1)
|
|
246
|
+
if (settings.seekEnabled) {
|
|
247
|
+
expect(seekbar.hasClass('seek-disabled')).toBe(false)
|
|
248
|
+
} else {
|
|
249
|
+
expect(seekbar.hasClass('seek-disabled')).toBe(true)
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
it(`should ${settings.left.includes('volume') ? '' : 'not '
|
|
253
|
+
}render the volume control`, () => {
|
|
254
|
+
const volume = mediaControl.$el.find('.drawer-container[data-volume]')
|
|
255
|
+
if (settings.left.includes('volume')) {
|
|
256
|
+
expect(volume.length).toEqual(1)
|
|
257
|
+
} else {
|
|
258
|
+
expect(volume.length).toEqual(0)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
it(`should ${settings.right.includes('fullscreen') ? '' : 'not '
|
|
262
|
+
}render the fullscreen control`, () => {
|
|
263
|
+
const fullscreen = mediaControl.$el.find(
|
|
264
|
+
'.media-control-right-panel [data-fullscreen]',
|
|
265
|
+
)
|
|
266
|
+
if (settings.right.includes('fullscreen')) {
|
|
267
|
+
expect(fullscreen.length).toEqual(1)
|
|
268
|
+
} else {
|
|
269
|
+
expect(fullscreen.length).toEqual(0)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
275
272
|
})
|
|
276
273
|
describe('basically', () => {
|
|
277
274
|
let handler: MockedFunction<() => void>
|
|
@@ -338,6 +335,37 @@ describe('MediaControl', () => {
|
|
|
338
335
|
expect(mediaControl.el.innerHTML).toMatchSnapshot()
|
|
339
336
|
})
|
|
340
337
|
})
|
|
338
|
+
describe('auto-hide', () => {
|
|
339
|
+
beforeEach(async () => {
|
|
340
|
+
mediaControl = new MediaControl(core)
|
|
341
|
+
core.emit(Events.CORE_READY)
|
|
342
|
+
core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
343
|
+
await runMetadataLoaded(core)
|
|
344
|
+
})
|
|
345
|
+
describe('when rendered', () => {
|
|
346
|
+
it('should auto-hide', async () => {
|
|
347
|
+
expect(mediaControl.$el.hasClass('media-control-hide')).toBe(false)
|
|
348
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
349
|
+
expect(mediaControl.$el.hasClass('media-control-hide')).toBe(true)
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
describe('after rendered', () => {
|
|
353
|
+
beforeEach(async () => {
|
|
354
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
355
|
+
})
|
|
356
|
+
describe('after user interaction', () => {
|
|
357
|
+
beforeEach(async () => {
|
|
358
|
+
core.activeContainer.trigger(Events.CONTAINER_MOUSE_ENTER)
|
|
359
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
360
|
+
})
|
|
361
|
+
it('should auto-hide', async () => {
|
|
362
|
+
expect(mediaControl.$el.hasClass('media-control-hide')).toBe(false)
|
|
363
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
364
|
+
expect(mediaControl.$el.hasClass('media-control-hide')).toBe(true)
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
})
|
|
368
|
+
})
|
|
341
369
|
})
|
|
342
370
|
|
|
343
371
|
function arraySubtract<T extends string>(arr1: T[], arr2: T[]) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Events, UICorePlugin, Browser, template, $ } from '@clappr/core'
|
|
2
|
-
import { reportError
|
|
2
|
+
import { reportError } from '@gcorevideo/utils'
|
|
3
3
|
import assert from 'assert'
|
|
4
4
|
|
|
5
5
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
@@ -19,7 +19,8 @@ import { VTTCueInfo } from '../../playback.types.js'
|
|
|
19
19
|
|
|
20
20
|
const VERSION: string = '2.19.14'
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// TODO review
|
|
23
|
+
// const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected'
|
|
23
24
|
|
|
24
25
|
const T = 'plugins.cc'
|
|
25
26
|
|
|
@@ -35,6 +36,9 @@ export type ClosedCaptionsPluginSettings = {
|
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Whether to use builtin subtitles.
|
|
39
|
+
*
|
|
40
|
+
* native: video player element renders the subtitles
|
|
41
|
+
* custom: plugin manages the subtitles rendition
|
|
38
42
|
*/
|
|
39
43
|
mode?: 'native' | 'custom'
|
|
40
44
|
}
|
|
@@ -75,12 +79,14 @@ export type ClosedCaptionsPluginSettings = {
|
|
|
75
79
|
* ```
|
|
76
80
|
*/
|
|
77
81
|
export class ClosedCaptions extends UICorePlugin {
|
|
78
|
-
private
|
|
82
|
+
private isSelectedApplied = false
|
|
79
83
|
|
|
80
84
|
private active = false
|
|
81
85
|
|
|
82
86
|
private open = false
|
|
83
87
|
|
|
88
|
+
private userSelectedItemId: number = -1
|
|
89
|
+
|
|
84
90
|
private track: TextTrackItem | null = null
|
|
85
91
|
|
|
86
92
|
private tracks: TextTrackItem[] = []
|
|
@@ -223,11 +229,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
223
229
|
}
|
|
224
230
|
})
|
|
225
231
|
|
|
226
|
-
this.
|
|
232
|
+
this.isSelectedApplied = false
|
|
227
233
|
}
|
|
228
234
|
|
|
229
235
|
private onPlaybackReady() {
|
|
230
|
-
trace(`${T} onPlaybackReady`)
|
|
231
236
|
this.core.activePlayback.oncueenter = (e: VTTCueInfo) => {
|
|
232
237
|
this.setSubtitleText(e.text)
|
|
233
238
|
}
|
|
@@ -251,7 +256,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
251
256
|
}
|
|
252
257
|
|
|
253
258
|
private activateTrack(id: number) {
|
|
254
|
-
if (['dash', 'hls'].includes(this.core.activePlayback
|
|
259
|
+
if (this.core.activePlayback && ['dash', 'hls'].includes(this.core.activePlayback.name)) {
|
|
255
260
|
this.core.activePlayback.setTextTrack(id)
|
|
256
261
|
return
|
|
257
262
|
}
|
|
@@ -289,7 +294,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
289
294
|
try {
|
|
290
295
|
// TODO ensure to apply only once
|
|
291
296
|
this.currentTracks = this.core.activePlayback.closedCaptionsTracks
|
|
292
|
-
this.
|
|
297
|
+
this.applySelectedSubtitles()
|
|
293
298
|
this.render()
|
|
294
299
|
} catch (error) {
|
|
295
300
|
reportError(error)
|
|
@@ -418,29 +423,40 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
418
423
|
|
|
419
424
|
private onItemSelect(event: MouseEvent) {
|
|
420
425
|
// event.target does not exist for some reason in tests
|
|
421
|
-
const id =
|
|
426
|
+
const id = Number(
|
|
422
427
|
((event.target ?? event.currentTarget) as HTMLElement).dataset?.item ??
|
|
423
|
-
'-1'
|
|
428
|
+
'-1',
|
|
429
|
+
)
|
|
424
430
|
|
|
425
|
-
|
|
426
|
-
|
|
431
|
+
// TODO review, make configurable, and emit event in addition
|
|
432
|
+
// localStorage.setItem(LOCAL_STORAGE_CC_ID, id) // TODO store language instead?
|
|
433
|
+
this.userSelectedItemId = id
|
|
434
|
+
this.selectItem(this.findById(id))
|
|
427
435
|
this.hideMenu()
|
|
428
436
|
return false
|
|
429
437
|
}
|
|
430
438
|
|
|
431
|
-
private
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
private applySelectedSubtitles() {
|
|
440
|
+
if (this.isSelectedApplied) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.isSelectedApplied = true
|
|
444
|
+
// If user selected a language, activate that
|
|
445
|
+
// Otherwise, if there is no configured language, then let the engine decide
|
|
446
|
+
// To hide the subtitles initially forcefully, set the language to 'none'
|
|
447
|
+
let matcher: (track: TextTrackItem) => boolean;
|
|
448
|
+
if (this.userSelectedItemId !== -1) {
|
|
449
|
+
matcher = (track: TextTrackItem) => track.id === this.userSelectedItemId;
|
|
450
|
+
} else if (this.preselectedLanguage) {
|
|
451
|
+
matcher = (track: TextTrackItem) => this.isPreselectedLanguage(track.track.language);
|
|
452
|
+
} else {
|
|
453
|
+
return;
|
|
443
454
|
}
|
|
455
|
+
setTimeout(() => {
|
|
456
|
+
this.selectItem(
|
|
457
|
+
this.tracks.find(matcher) ?? null,
|
|
458
|
+
)
|
|
459
|
+
}, 0)
|
|
444
460
|
}
|
|
445
461
|
|
|
446
462
|
private hideMenu() {
|
|
@@ -479,11 +495,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
479
495
|
}
|
|
480
496
|
|
|
481
497
|
private selectSubtitles() {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
this.core.activePlayback.closedCaptionsTrackId = trackId
|
|
486
|
-
// this.core.activePlayback.closedCaptionsTrackId = -1
|
|
498
|
+
if (this.currentTrack) {
|
|
499
|
+
this.core.activePlayback.closedCaptionsTrackId = this.currentTrack.id
|
|
500
|
+
}
|
|
487
501
|
}
|
|
488
502
|
|
|
489
503
|
private getSubtitleText(track: TextTrack) {
|