@gcorevideo/player 2.28.30 → 2.28.36
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 +22 -1
- package/assets/{subtitles → cc}/style.scss +5 -0
- package/dist/core.js +17 -23
- package/dist/index.css +241 -237
- package/dist/index.embed.js +79 -59
- package/dist/index.js +137 -119
- 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/playback/BasePlayback.d.ts +1 -0
- package/lib/playback/BasePlayback.d.ts.map +1 -1
- package/lib/playback/BasePlayback.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 +9 -22
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +4 -0
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +7 -3
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +66 -42
- package/lib/testUtils.d.ts +1 -0
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +3 -0
- package/package.json +4 -1
- package/src/Player.ts +12 -12
- package/src/playback/BasePlayback.ts +4 -0
- package/src/playback/dash-playback/DashPlayback.ts +10 -27
- package/src/playback/hls-playback/HlsPlayback.ts +4 -0
- package/src/plugins/subtitles/ClosedCaptions.ts +75 -47
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +277 -29
- package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +3 -3
- package/src/testUtils.ts +3 -0
- package/tsconfig.tsbuildinfo +1 -1
- /package/assets/{subtitles → cc}/combobox.ejs +0 -0
- /package/assets/{subtitles → cc}/string.ejs +0 -0
|
@@ -61,6 +61,8 @@ export default class DashPlayback extends BasePlayback {
|
|
|
61
61
|
|
|
62
62
|
_currentLevel: number = AUTO
|
|
63
63
|
|
|
64
|
+
_currentTextTrackId: number = -1
|
|
65
|
+
|
|
64
66
|
// true when the actual duration is longer than hlsjs's live sync point
|
|
65
67
|
// when this is false playableRegionDuration will be the actual duration
|
|
66
68
|
// when this is true playableRegionDuration will exclude the time after the sync point
|
|
@@ -248,7 +250,11 @@ export default class DashPlayback extends BasePlayback {
|
|
|
248
250
|
streaming: {
|
|
249
251
|
text: {
|
|
250
252
|
defaultEnabled: false,
|
|
251
|
-
|
|
253
|
+
// NOTE: dispatchForManualRendering is not correctly implemented in DASH.js;
|
|
254
|
+
// it does not work when there are multiple text tracks.
|
|
255
|
+
// CUE_ENTER and CUE_EXIT events might be dispatched additionally
|
|
256
|
+
// for a track, other than the currently active one.
|
|
257
|
+
// dispatchForManualRendering: true, // TODO only when useNativeSubtitles is not true?
|
|
252
258
|
},
|
|
253
259
|
},
|
|
254
260
|
},
|
|
@@ -319,29 +325,6 @@ export default class DashPlayback extends BasePlayback {
|
|
|
319
325
|
this.trigger(PlaybackEvents.PLAYBACK_RATE_CHANGED, e.playbackRate)
|
|
320
326
|
},
|
|
321
327
|
)
|
|
322
|
-
|
|
323
|
-
this._dash.on(MediaPlayer.events.TRACK_CHANGE_RENDERED, (e: any) => {
|
|
324
|
-
if ((e as TrackChangeRenderedEvent).mediaType === 'audio') {
|
|
325
|
-
this.trigger(
|
|
326
|
-
Events.PLAYBACK_AUDIO_CHANGED,
|
|
327
|
-
toClapprTrack(e.newMediaInfo),
|
|
328
|
-
)
|
|
329
|
-
}
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
this._dash.on(MediaPlayer.events.CUE_ENTER, (e: CueEnterEvent) => {
|
|
333
|
-
this.oncueenter?.({
|
|
334
|
-
end: e.end,
|
|
335
|
-
id: e.id,
|
|
336
|
-
start: e.start,
|
|
337
|
-
text: e.text,
|
|
338
|
-
})
|
|
339
|
-
})
|
|
340
|
-
this._dash.on(MediaPlayer.events.CUE_EXIT, (e: CueExitEvent) => {
|
|
341
|
-
this.oncueexit?.({
|
|
342
|
-
id: e.id,
|
|
343
|
-
})
|
|
344
|
-
})
|
|
345
328
|
}
|
|
346
329
|
|
|
347
330
|
render() {
|
|
@@ -725,6 +708,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
725
708
|
}
|
|
726
709
|
|
|
727
710
|
setTextTrack(id: number) {
|
|
711
|
+
this._currentTextTrackId = id
|
|
728
712
|
this._dash?.setTextTrack(id)
|
|
729
713
|
}
|
|
730
714
|
|
|
@@ -732,8 +716,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
732
716
|
* @override
|
|
733
717
|
*/
|
|
734
718
|
get closedCaptionsTracks() {
|
|
735
|
-
|
|
736
|
-
return tt;
|
|
719
|
+
return this.getTextTracks()
|
|
737
720
|
}
|
|
738
721
|
|
|
739
722
|
private getTextTracks() {
|
|
@@ -744,7 +727,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
744
727
|
id: index,
|
|
745
728
|
label: getTextTrackLabel(t) || "",
|
|
746
729
|
language: t.lang,
|
|
747
|
-
mode: "hidden",
|
|
730
|
+
mode: this._currentTextTrackId === index ? "showing" : "hidden",
|
|
748
731
|
},
|
|
749
732
|
})) || []
|
|
750
733
|
}
|
|
@@ -1135,7 +1135,11 @@ export default class HlsPlayback extends BasePlayback {
|
|
|
1135
1135
|
}
|
|
1136
1136
|
|
|
1137
1137
|
setTextTrack(id: number) {
|
|
1138
|
+
if (id === this._hls!.subtitleTrack) {
|
|
1139
|
+
return
|
|
1140
|
+
}
|
|
1138
1141
|
this._hls!.subtitleTrack = id
|
|
1142
|
+
this.cues = []
|
|
1139
1143
|
}
|
|
1140
1144
|
|
|
1141
1145
|
/**
|
|
@@ -1,15 +1,15 @@
|
|
|
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'
|
|
6
6
|
import type { TextTrackItem } from '../../internal.types.js'
|
|
7
7
|
|
|
8
|
-
import '../../../assets/
|
|
8
|
+
import '../../../assets/cc/style.scss'
|
|
9
9
|
import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg'
|
|
10
10
|
import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg'
|
|
11
|
-
import comboboxHTML from '../../../assets/
|
|
12
|
-
import stringHTML from '../../../assets/
|
|
11
|
+
import comboboxHTML from '../../../assets/cc/combobox.ejs'
|
|
12
|
+
import stringHTML from '../../../assets/cc/string.ejs'
|
|
13
13
|
|
|
14
14
|
import { isFullscreen } from '../utils/fullscreen.js'
|
|
15
15
|
import type { ZeptoResult } from '../../types.js'
|
|
@@ -19,9 +19,10 @@ 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
|
-
const T = 'plugins.cc'
|
|
25
|
+
// const T = 'plugins.cc'
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Configuration options for the {@link ClosedCaptions} plugin.
|
|
@@ -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[] = []
|
|
@@ -162,8 +168,12 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
162
168
|
const mediaControl = this.core.getPlugin('media_control')
|
|
163
169
|
assert(mediaControl, 'media_control plugin is required')
|
|
164
170
|
this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.mount)
|
|
171
|
+
this.listenTo(mediaControl, Events.MEDIACONTROL_SHOW, () => {
|
|
172
|
+
this.$line?.removeClass('media-control-cc-pulled')
|
|
173
|
+
})
|
|
165
174
|
this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, () => {
|
|
166
175
|
this.hideMenu()
|
|
176
|
+
this.$line?.addClass('media-control-cc-pulled')
|
|
167
177
|
})
|
|
168
178
|
this.listenTo(
|
|
169
179
|
mediaControl,
|
|
@@ -223,11 +233,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
223
233
|
}
|
|
224
234
|
})
|
|
225
235
|
|
|
226
|
-
this.
|
|
236
|
+
this.isSelectedApplied = false
|
|
227
237
|
}
|
|
228
238
|
|
|
229
239
|
private onPlaybackReady() {
|
|
230
|
-
trace(`${T} onPlaybackReady`)
|
|
231
240
|
this.core.activePlayback.oncueenter = (e: VTTCueInfo) => {
|
|
232
241
|
this.setSubtitleText(e.text)
|
|
233
242
|
}
|
|
@@ -251,24 +260,28 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
251
260
|
}
|
|
252
261
|
|
|
253
262
|
private activateTrack(id: number) {
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
const isManaged = this.core.activePlayback?.name === 'hls'
|
|
264
|
+
this.core.activePlayback.setTextTrack(id)
|
|
265
|
+
if (isManaged) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
if (!this.core.activePlayback?.el.textTracks) {
|
|
256
269
|
return
|
|
257
270
|
}
|
|
258
|
-
for (const track of this.
|
|
259
|
-
if (
|
|
271
|
+
for (const [index, track] of (Array.from(this.core.activePlayback?.el.textTracks ?? []) as TextTrack[]).entries()) {
|
|
272
|
+
if (index === id) {
|
|
260
273
|
if (this.useNativeSubtitles) {
|
|
261
|
-
track.
|
|
274
|
+
track.mode = 'showing'
|
|
262
275
|
} else {
|
|
263
|
-
track.
|
|
276
|
+
track.mode = 'hidden'
|
|
264
277
|
}
|
|
265
278
|
|
|
266
|
-
this.setSubtitleText(this.getSubtitleText(track
|
|
279
|
+
this.setSubtitleText(this.getSubtitleText(track))
|
|
267
280
|
|
|
268
|
-
track.
|
|
281
|
+
track.oncuechange = () => {
|
|
269
282
|
try {
|
|
270
|
-
if (track.
|
|
271
|
-
const html = (track.
|
|
283
|
+
if (track.activeCues?.length) {
|
|
284
|
+
const html = (track.activeCues[0] as VTTCue).getCueAsHTML()
|
|
272
285
|
|
|
273
286
|
this.setSubtitleText(html)
|
|
274
287
|
} else {
|
|
@@ -279,8 +292,8 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
294
|
} else {
|
|
282
|
-
track.
|
|
283
|
-
track.
|
|
295
|
+
track.oncuechange = () => { }
|
|
296
|
+
track.mode = 'disabled'
|
|
284
297
|
}
|
|
285
298
|
}
|
|
286
299
|
}
|
|
@@ -289,7 +302,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
289
302
|
try {
|
|
290
303
|
// TODO ensure to apply only once
|
|
291
304
|
this.currentTracks = this.core.activePlayback.closedCaptionsTracks
|
|
292
|
-
this.
|
|
305
|
+
this.applySelectedSubtitles()
|
|
293
306
|
this.render()
|
|
294
307
|
} catch (error) {
|
|
295
308
|
reportError(error)
|
|
@@ -327,8 +340,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
327
340
|
this.$el.find('#gplayer-cc-menu').hide()
|
|
328
341
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
|
|
329
342
|
this.$line.hide()
|
|
330
|
-
for (const track of this.
|
|
331
|
-
track.
|
|
343
|
+
for (const track of this.core.activePlayback.el.textTracks) {
|
|
344
|
+
if (track.mode === 'showing') {
|
|
345
|
+
track.mode = 'hidden'
|
|
346
|
+
}
|
|
332
347
|
}
|
|
333
348
|
}
|
|
334
349
|
|
|
@@ -342,7 +357,6 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
342
357
|
this.core.activeContainer &&
|
|
343
358
|
isFullscreen(this.core.activeContainer.el) &&
|
|
344
359
|
this.currentTrack &&
|
|
345
|
-
// this.currentTrack.track.mode &&
|
|
346
360
|
(Browser.isiOS || this.useNativeSubtitles)
|
|
347
361
|
) {
|
|
348
362
|
this.$line.hide()
|
|
@@ -393,6 +407,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
393
407
|
this.clampPopup()
|
|
394
408
|
|
|
395
409
|
this.core.activeContainer.$el.append(this.$line)
|
|
410
|
+
const mc = this.core.getPlugin('media_control')
|
|
411
|
+
if (!mc?.isVisible()) {
|
|
412
|
+
this.$line?.addClass('media-control-cc-pulled')
|
|
413
|
+
}
|
|
396
414
|
|
|
397
415
|
this.updateSelection()
|
|
398
416
|
|
|
@@ -418,29 +436,41 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
418
436
|
|
|
419
437
|
private onItemSelect(event: MouseEvent) {
|
|
420
438
|
// event.target does not exist for some reason in tests
|
|
421
|
-
const id =
|
|
439
|
+
const id = Number(
|
|
422
440
|
((event.target ?? event.currentTarget) as HTMLElement).dataset?.item ??
|
|
423
|
-
'-1'
|
|
441
|
+
'-1',
|
|
442
|
+
)
|
|
424
443
|
|
|
425
|
-
|
|
426
|
-
|
|
444
|
+
// TODO review, make configurable, and emit event in addition
|
|
445
|
+
// localStorage.setItem(LOCAL_STORAGE_CC_ID, id) // TODO store language instead?
|
|
446
|
+
this.userSelectedItemId = id
|
|
447
|
+
this.selectItem(this.findById(id))
|
|
427
448
|
this.hideMenu()
|
|
428
449
|
return false
|
|
429
450
|
}
|
|
430
451
|
|
|
431
|
-
private
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
// if the language is undefined, then let the engine decide
|
|
435
|
-
// to hide the subtitles forcefully, set the language to 'none'
|
|
436
|
-
setTimeout(() => {
|
|
437
|
-
this.selectItem(
|
|
438
|
-
this.tracks.find((t) =>
|
|
439
|
-
this.isPreselectedLanguage(t.track.language),
|
|
440
|
-
) ?? null,
|
|
441
|
-
)
|
|
442
|
-
}, 0)
|
|
452
|
+
private applySelectedSubtitles() {
|
|
453
|
+
if (this.isSelectedApplied) {
|
|
454
|
+
return;
|
|
443
455
|
}
|
|
456
|
+
this.isSelectedApplied = true
|
|
457
|
+
// If user selected a language, activate that
|
|
458
|
+
// Otherwise, if there is no configured language, then let the engine decide
|
|
459
|
+
// To hide the subtitles initially forcefully, set the language to 'none'
|
|
460
|
+
let matcher: (track: TextTrackItem) => boolean;
|
|
461
|
+
if (this.userSelectedItemId !== -1) {
|
|
462
|
+
matcher = (track: TextTrackItem) => track.id === this.userSelectedItemId;
|
|
463
|
+
} else if (this.preselectedLanguage) {
|
|
464
|
+
matcher = (track: TextTrackItem) => this.isPreselectedLanguage(track.track.language);
|
|
465
|
+
} else {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
setTimeout(() => {
|
|
470
|
+
this.selectItem(
|
|
471
|
+
this.tracks.find(matcher) ?? null,
|
|
472
|
+
)
|
|
473
|
+
}, 0)
|
|
444
474
|
}
|
|
445
475
|
|
|
446
476
|
private hideMenu() {
|
|
@@ -479,11 +509,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
479
509
|
}
|
|
480
510
|
|
|
481
511
|
private selectSubtitles() {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
this.core.activePlayback.closedCaptionsTrackId = trackId
|
|
486
|
-
// this.core.activePlayback.closedCaptionsTrackId = -1
|
|
512
|
+
if (this.currentTrack) {
|
|
513
|
+
this.core.activePlayback.closedCaptionsTrackId = this.currentTrack.id
|
|
514
|
+
}
|
|
487
515
|
}
|
|
488
516
|
|
|
489
517
|
private getSubtitleText(track: TextTrack) {
|
|
@@ -512,7 +540,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
512
540
|
}
|
|
513
541
|
|
|
514
542
|
private updateSelection() {
|
|
515
|
-
if (
|
|
543
|
+
if (this.core.activePlayback.closedCaptionsTrackId === -1) {
|
|
516
544
|
this.hide()
|
|
517
545
|
} else {
|
|
518
546
|
this.show()
|