@gcorevideo/player 2.28.35 → 2.29.0
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/assets/media-control/media-control.scss +8 -6
- package/assets/multi-camera/multicamera.ejs +27 -23
- package/assets/multi-camera/style.scss +7 -34
- package/assets/style/main.scss +2 -2
- package/dist/core.js +24 -28
- package/dist/index.css +384 -402
- package/dist/index.embed.js +54 -84
- package/dist/index.js +122 -219
- package/docs/api/player.md +22 -9
- package/docs/api/player.mediacontrol.setkeepvisible.md +56 -0
- package/docs/api/player.multicamera.md +0 -28
- package/docs/api/player.multiccamerasourceinfo.md +27 -0
- package/docs/api/{player.multicamera.unbindevents.md → player.multisourcesmode.md} +4 -7
- package/docs/api/player.sourcecontroller.md +0 -37
- 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 +3 -1
- 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 +2 -1
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +4 -0
- package/lib/playback/types.d.ts +9 -0
- package/lib/playback/types.d.ts.map +1 -1
- package/lib/playback.types.d.ts +0 -6
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/plugins/multi-camera/MultiCamera.d.ts +21 -4
- package/lib/plugins/multi-camera/MultiCamera.d.ts.map +1 -1
- package/lib/plugins/multi-camera/MultiCamera.js +70 -134
- package/lib/plugins/source-controller/SourceController.d.ts +0 -39
- package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.js +0 -39
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +32 -22
- package/lib/testUtils.d.ts +1 -0
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +3 -0
- package/lib/utils/mediaSources.d.ts +4 -0
- package/lib/utils/mediaSources.d.ts.map +1 -1
- package/lib/utils/mediaSources.js +8 -6
- package/package.json +1 -1
- package/src/playback/BasePlayback.ts +4 -0
- package/src/playback/dash-playback/DashPlayback.ts +11 -29
- package/src/playback/hls-playback/HlsPlayback.ts +5 -1
- package/src/playback/types.ts +10 -0
- package/src/playback.types.ts +0 -6
- package/src/plugins/multi-camera/MultiCamera.ts +103 -166
- package/src/plugins/source-controller/SourceController.ts +0 -39
- package/src/plugins/subtitles/ClosedCaptions.ts +35 -21
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +73 -112
- package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +3 -3
- package/src/testUtils.ts +3 -0
- package/src/utils/mediaSources.ts +10 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/docs/api/player.multicamera.activebyid.md +0 -67
- /package/assets/{subtitles → cc}/combobox.ejs +0 -0
- /package/assets/{subtitles → cc}/string.ejs +0 -0
|
@@ -5,24 +5,24 @@ import assert from 'assert'
|
|
|
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'
|
|
16
16
|
import { ExtendedEvents } from '../media-control/MediaControl.js'
|
|
17
17
|
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
18
|
-
import { VTTCueInfo } from '../../playback
|
|
18
|
+
import { VTTCueInfo } from '../../playback/types.js'
|
|
19
19
|
|
|
20
20
|
const VERSION: string = '2.19.14'
|
|
21
21
|
|
|
22
22
|
// TODO review
|
|
23
23
|
// const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected'
|
|
24
24
|
|
|
25
|
-
const T = 'plugins.cc'
|
|
25
|
+
// const T = 'plugins.cc'
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Configuration options for the {@link ClosedCaptions} plugin.
|
|
@@ -168,8 +168,12 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
168
168
|
const mediaControl = this.core.getPlugin('media_control')
|
|
169
169
|
assert(mediaControl, 'media_control plugin is required')
|
|
170
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
|
+
})
|
|
171
174
|
this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, () => {
|
|
172
175
|
this.hideMenu()
|
|
176
|
+
this.$line?.addClass('media-control-cc-pulled')
|
|
173
177
|
})
|
|
174
178
|
this.listenTo(
|
|
175
179
|
mediaControl,
|
|
@@ -256,24 +260,28 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
private activateTrack(id: number) {
|
|
259
|
-
|
|
260
|
-
|
|
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) {
|
|
261
269
|
return
|
|
262
270
|
}
|
|
263
|
-
for (const track of this.
|
|
264
|
-
if (
|
|
271
|
+
for (const [index, track] of (Array.from(this.core.activePlayback?.el.textTracks ?? []) as TextTrack[]).entries()) {
|
|
272
|
+
if (index === id) {
|
|
265
273
|
if (this.useNativeSubtitles) {
|
|
266
|
-
track.
|
|
274
|
+
track.mode = 'showing'
|
|
267
275
|
} else {
|
|
268
|
-
track.
|
|
276
|
+
track.mode = 'hidden'
|
|
269
277
|
}
|
|
270
278
|
|
|
271
|
-
this.setSubtitleText(this.getSubtitleText(track
|
|
279
|
+
this.setSubtitleText(this.getSubtitleText(track))
|
|
272
280
|
|
|
273
|
-
track.
|
|
281
|
+
track.oncuechange = () => {
|
|
274
282
|
try {
|
|
275
|
-
if (track.
|
|
276
|
-
const html = (track.
|
|
283
|
+
if (track.activeCues?.length) {
|
|
284
|
+
const html = (track.activeCues[0] as VTTCue).getCueAsHTML()
|
|
277
285
|
|
|
278
286
|
this.setSubtitleText(html)
|
|
279
287
|
} else {
|
|
@@ -284,8 +292,8 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
284
292
|
}
|
|
285
293
|
}
|
|
286
294
|
} else {
|
|
287
|
-
track.
|
|
288
|
-
track.
|
|
295
|
+
track.oncuechange = () => { }
|
|
296
|
+
track.mode = 'disabled'
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
}
|
|
@@ -332,8 +340,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
332
340
|
this.$el.find('#gplayer-cc-menu').hide()
|
|
333
341
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
|
|
334
342
|
this.$line.hide()
|
|
335
|
-
for (const track of this.
|
|
336
|
-
track.
|
|
343
|
+
for (const track of this.core.activePlayback.el.textTracks) {
|
|
344
|
+
if (track.mode === 'showing') {
|
|
345
|
+
track.mode = 'hidden'
|
|
346
|
+
}
|
|
337
347
|
}
|
|
338
348
|
}
|
|
339
349
|
|
|
@@ -347,7 +357,6 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
347
357
|
this.core.activeContainer &&
|
|
348
358
|
isFullscreen(this.core.activeContainer.el) &&
|
|
349
359
|
this.currentTrack &&
|
|
350
|
-
// this.currentTrack.track.mode &&
|
|
351
360
|
(Browser.isiOS || this.useNativeSubtitles)
|
|
352
361
|
) {
|
|
353
362
|
this.$line.hide()
|
|
@@ -398,6 +407,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
398
407
|
this.clampPopup()
|
|
399
408
|
|
|
400
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
|
+
}
|
|
401
414
|
|
|
402
415
|
this.updateSelection()
|
|
403
416
|
|
|
@@ -452,6 +465,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
452
465
|
} else {
|
|
453
466
|
return;
|
|
454
467
|
}
|
|
468
|
+
|
|
455
469
|
setTimeout(() => {
|
|
456
470
|
this.selectItem(
|
|
457
471
|
this.tracks.find(matcher) ?? null,
|
|
@@ -526,7 +540,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
526
540
|
}
|
|
527
541
|
|
|
528
542
|
private updateSelection() {
|
|
529
|
-
if (
|
|
543
|
+
if (this.core.activePlayback.closedCaptionsTrackId === -1) {
|
|
530
544
|
this.hide()
|
|
531
545
|
} else {
|
|
532
546
|
this.show()
|
|
@@ -16,6 +16,7 @@ describe('ClosedCaptions', () => {
|
|
|
16
16
|
let mediaControl: any
|
|
17
17
|
let cc: ClosedCaptions
|
|
18
18
|
beforeEach(() => {
|
|
19
|
+
vi.useFakeTimers()
|
|
19
20
|
core = createMockCore()
|
|
20
21
|
mediaControl = createMockMediaControl(core)
|
|
21
22
|
mediaControl.getAvailablePopupHeight = vi.fn().mockReturnValue(211)
|
|
@@ -27,8 +28,12 @@ describe('ClosedCaptions', () => {
|
|
|
27
28
|
})
|
|
28
29
|
cc = new ClosedCaptions(core)
|
|
29
30
|
})
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
vi.useRealTimers()
|
|
33
|
+
})
|
|
30
34
|
describe('basically', () => {
|
|
31
35
|
beforeEach(() => {
|
|
36
|
+
core.options.cc = { language: 'none' }
|
|
32
37
|
core.emit(Events.CORE_READY)
|
|
33
38
|
core.activePlayback.el = document.createElement('video')
|
|
34
39
|
core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
@@ -53,6 +58,7 @@ describe('ClosedCaptions', () => {
|
|
|
53
58
|
describe("when subtitle tracks are available", () => {
|
|
54
59
|
beforeEach(() => {
|
|
55
60
|
emitSubtitleAvailable(core)
|
|
61
|
+
vi.advanceTimersByTime(1)
|
|
56
62
|
})
|
|
57
63
|
it('should render', () => {
|
|
58
64
|
expect(cc.el.innerHTML).toMatchSnapshot()
|
|
@@ -64,7 +70,6 @@ describe('ClosedCaptions', () => {
|
|
|
64
70
|
width: 320,
|
|
65
71
|
height: 260,
|
|
66
72
|
})
|
|
67
|
-
// core.emit(Events.CORE_RESIZE, { width: 320, height: 260 })
|
|
68
73
|
})
|
|
69
74
|
it('should clamp popup height', () => {
|
|
70
75
|
expect(cc.$el.find('#gplayer-cc-menu').css('max-height')).toBe('197px')
|
|
@@ -131,9 +136,9 @@ describe('ClosedCaptions', () => {
|
|
|
131
136
|
})
|
|
132
137
|
})
|
|
133
138
|
describe('when subtitle is changed', () => {
|
|
134
|
-
beforeEach(
|
|
139
|
+
beforeEach(() => {
|
|
135
140
|
cc.$el.find('#gplayer-cc-button').click()
|
|
136
|
-
|
|
141
|
+
vi.advanceTimersByTime(100)
|
|
137
142
|
core.activePlayback.getCurrentTime = vi.fn().mockReturnValue(7)
|
|
138
143
|
core.activeContainer.getCurrentTime = vi.fn().mockReturnValue(7)
|
|
139
144
|
core.activePlayback.closedCaptionsTracks[1].track.cues = [
|
|
@@ -169,10 +174,10 @@ describe('ClosedCaptions', () => {
|
|
|
169
174
|
},
|
|
170
175
|
]
|
|
171
176
|
cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
172
|
-
|
|
177
|
+
vi.advanceTimersByTime(100)
|
|
173
178
|
// TODO test explicitly that PLAYBACK_SUBTITLE_CHANGED event does not cause track switch
|
|
174
179
|
core.activePlayback.emit(Events.PLAYBACK_SUBTITLE_CHANGED, { id: 2 })
|
|
175
|
-
|
|
180
|
+
vi.advanceTimersByTime(100)
|
|
176
181
|
})
|
|
177
182
|
it('should activate selected track', () => {
|
|
178
183
|
expect(core.activePlayback.closedCaptionsTracks[1].track.mode).toBe(
|
|
@@ -193,7 +198,8 @@ describe('ClosedCaptions', () => {
|
|
|
193
198
|
cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
194
199
|
})
|
|
195
200
|
it('should activate native subtitles track', () => {
|
|
196
|
-
expect(core.activePlayback.
|
|
201
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(1)
|
|
202
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(1)
|
|
197
203
|
})
|
|
198
204
|
it('should highlight selected menu item', () => {
|
|
199
205
|
expect(
|
|
@@ -257,7 +263,6 @@ describe('ClosedCaptions', () => {
|
|
|
257
263
|
describe('when language is configured', () => {
|
|
258
264
|
describe("and is set to 'none'", () => {
|
|
259
265
|
beforeEach(() => {
|
|
260
|
-
vi.useFakeTimers()
|
|
261
266
|
core.options.cc = { language: 'none' }
|
|
262
267
|
emitSubtitleAvailable(core)
|
|
263
268
|
vi.advanceTimersByTime(1)
|
|
@@ -281,47 +286,37 @@ describe('ClosedCaptions', () => {
|
|
|
281
286
|
})
|
|
282
287
|
})
|
|
283
288
|
describe('when language is undefined', () => {
|
|
284
|
-
// let spyClosedCaptionsTrackId: any // TODO
|
|
285
289
|
beforeEach(() => {
|
|
286
|
-
// spyClosedCaptionsTrackId = vi.spyOn(core.activePlayback, 'closedCaptionsTrackId')
|
|
287
290
|
core.options.cc = {}
|
|
288
291
|
core.activePlayback.closedCaptionsTrackId = undefined
|
|
289
292
|
emitSubtitleAvailable(core, 1)
|
|
293
|
+
vi.advanceTimersByTime(1)
|
|
290
294
|
})
|
|
291
295
|
// The native engine will decide
|
|
292
296
|
it('should not automatically select any track', () => {
|
|
293
297
|
expect(core.activePlayback.closedCaptionsTrackId).toEqual(undefined)
|
|
298
|
+
expect(core.activePlayback.setTextTrack).not.toHaveBeenCalled()
|
|
294
299
|
})
|
|
295
|
-
it('should not
|
|
296
|
-
expect(core.activePlayback.
|
|
297
|
-
|
|
298
|
-
)
|
|
299
|
-
expect(core.activePlayback.closedCaptionsTracks[1].track.mode).toBe(
|
|
300
|
-
'hidden',
|
|
301
|
-
)
|
|
300
|
+
it('should not touch any tracks', () => {
|
|
301
|
+
expect(core.activePlayback.el.textTracks[0].mode).toBe('hidden')
|
|
302
|
+
// Track #1 is set active (as if selected by the engine), @see {emitSubtitleAvailable}()
|
|
303
|
+
expect(core.activePlayback.el.textTracks[1].mode).toBe('showing')
|
|
302
304
|
})
|
|
303
305
|
})
|
|
304
306
|
describe('when language matches a track', () => {
|
|
305
307
|
beforeEach(() => {
|
|
306
|
-
vi.useFakeTimers()
|
|
307
308
|
core.options.cc = { language: 'en' }
|
|
308
309
|
emitSubtitleAvailable(core)
|
|
309
310
|
vi.advanceTimersByTime(1)
|
|
310
311
|
})
|
|
311
|
-
// afterEach(() => {
|
|
312
|
-
// vi.useRealTimers()
|
|
313
|
-
// })
|
|
314
312
|
it('should activate the matching track', () => {
|
|
315
|
-
expect(core.activePlayback.closedCaptionsTrackId).toEqual(
|
|
313
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(0)
|
|
314
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(0)
|
|
316
315
|
})
|
|
316
|
+
// TODO with options.cc.mode='native' the selected track's mode should be 'showing'
|
|
317
317
|
it('should set the matching track mode appropriately', () => {
|
|
318
|
-
|
|
319
|
-
expect(core.activePlayback.
|
|
320
|
-
'hidden',
|
|
321
|
-
)
|
|
322
|
-
expect(core.activePlayback.closedCaptionsTracks[1].track.mode).toBe(
|
|
323
|
-
'disabled',
|
|
324
|
-
)
|
|
318
|
+
expect(core.activePlayback.el.textTracks[0].mode).toBe('hidden')
|
|
319
|
+
expect(core.activePlayback.el.textTracks[1].mode).toBe('disabled')
|
|
325
320
|
})
|
|
326
321
|
it('should highlight the matching track in menu', () => {
|
|
327
322
|
expect(
|
|
@@ -331,18 +326,13 @@ describe('ClosedCaptions', () => {
|
|
|
331
326
|
})
|
|
332
327
|
describe('when language does not match any track', () => {
|
|
333
328
|
beforeEach(() => {
|
|
334
|
-
vi.useFakeTimers()
|
|
335
329
|
core.options.cc = { language: 'fr' }
|
|
336
|
-
cc = new ClosedCaptions(core)
|
|
337
330
|
core.emit(Events.CORE_READY)
|
|
338
331
|
core.activePlayback.el = document.createElement('video')
|
|
339
332
|
core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
340
333
|
emitSubtitleAvailable(core)
|
|
341
334
|
vi.advanceTimersByTime(1)
|
|
342
335
|
})
|
|
343
|
-
afterEach(() => {
|
|
344
|
-
vi.useRealTimers()
|
|
345
|
-
})
|
|
346
336
|
it('should disable subtitles', () => {
|
|
347
337
|
expect(core.activePlayback.closedCaptionsTrackId).toEqual(-1)
|
|
348
338
|
})
|
|
@@ -364,7 +354,6 @@ describe('ClosedCaptions', () => {
|
|
|
364
354
|
describe('when subtitle available event occurs after user selection', () => {
|
|
365
355
|
describe('if user selected track before event', () => {
|
|
366
356
|
beforeEach(() => {
|
|
367
|
-
vi.useFakeTimers()
|
|
368
357
|
core.options.cc = { language: 'en' }
|
|
369
358
|
// First subtitle available event
|
|
370
359
|
emitSubtitleAvailable(core)
|
|
@@ -382,12 +371,10 @@ describe('ClosedCaptions', () => {
|
|
|
382
371
|
emitSubtitleAvailable(core)
|
|
383
372
|
vi.advanceTimersByTime(1)
|
|
384
373
|
})
|
|
385
|
-
afterEach(() => {
|
|
386
|
-
vi.useRealTimers()
|
|
387
|
-
})
|
|
388
374
|
it('should run preselected language matching algorithm', () => {
|
|
389
375
|
// Should activate track selected by user
|
|
390
|
-
expect(core.activePlayback.
|
|
376
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(1)
|
|
377
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(1)
|
|
391
378
|
})
|
|
392
379
|
it('should select track based on configured language', () => {
|
|
393
380
|
expect(
|
|
@@ -406,18 +393,10 @@ describe('ClosedCaptions', () => {
|
|
|
406
393
|
})
|
|
407
394
|
describe('when multiple subtitle available events occur', () => {
|
|
408
395
|
beforeEach(() => {
|
|
409
|
-
vi.useFakeTimers()
|
|
410
396
|
core.options.cc = { language: 'en' }
|
|
411
|
-
// cc = new ClosedCaptions(core)
|
|
412
|
-
// core.emit(Events.CORE_READY)
|
|
413
|
-
// core.activePlayback.el = document.createElement('video')
|
|
414
|
-
// core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
415
397
|
// First subtitle available event
|
|
416
398
|
emitSubtitleAvailable(core)
|
|
417
399
|
vi.advanceTimersByTime(1)
|
|
418
|
-
// User changes selection
|
|
419
|
-
// cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
420
|
-
// Container changed (simulating source switch)
|
|
421
400
|
core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
422
401
|
vi.advanceTimersByTime(1)
|
|
423
402
|
core.activePlayback.closedCaptionsTrackId = undefined
|
|
@@ -425,68 +404,59 @@ describe('ClosedCaptions', () => {
|
|
|
425
404
|
emitSubtitleAvailable(core)
|
|
426
405
|
vi.advanceTimersByTime(1)
|
|
427
406
|
})
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
it('should reapply preselected language matching', () => {
|
|
432
|
-
expect(core.activePlayback.closedCaptionsTrackId).toEqual(1)
|
|
433
|
-
// Should select English track based on configured language
|
|
407
|
+
it('should reapply preselected language matching and activate the matching track', () => {
|
|
408
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(0)
|
|
409
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(0)
|
|
434
410
|
expect(
|
|
435
411
|
cc.$el.find('#gplayer-cc-menu li:nth-child(1)').hasClass('current'),
|
|
436
412
|
).toBe(true)
|
|
437
413
|
})
|
|
438
414
|
it('should activate the matching track', () => {
|
|
439
|
-
expect(core.activePlayback.
|
|
415
|
+
expect(core.activePlayback.el.textTracks[0].mode).toBe(
|
|
440
416
|
'hidden',
|
|
441
417
|
)
|
|
442
|
-
expect(core.activePlayback.
|
|
418
|
+
expect(core.activePlayback.el.textTracks[1].mode).toBe(
|
|
443
419
|
'disabled',
|
|
444
420
|
)
|
|
445
421
|
})
|
|
446
422
|
})
|
|
447
423
|
})
|
|
448
|
-
describe.each(['dash', 'hls'] as const)(
|
|
424
|
+
describe.each([['html5_video', false], ['dash', false], ['hls', true]] as const)(
|
|
449
425
|
'when playback engine is %s',
|
|
450
|
-
(playbackEngine) => {
|
|
426
|
+
(playbackEngine, isManaged) => {
|
|
451
427
|
beforeEach(() => {
|
|
452
|
-
// core.emit(Events.CORE_READY)
|
|
453
428
|
core.activePlayback.el = document.createElement('video')
|
|
454
429
|
core.activePlayback.name = playbackEngine
|
|
455
|
-
core.activePlayback.setTextTrack = vi.fn()
|
|
456
|
-
// core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
457
430
|
})
|
|
458
431
|
describe('when language is configured and matches a track', () => {
|
|
459
432
|
beforeEach(() => {
|
|
460
|
-
vi.useFakeTimers()
|
|
461
433
|
core.options.cc = { language: 'en' }
|
|
462
|
-
// cc = new ClosedCaptions(core)
|
|
463
|
-
// core.emit(Events.CORE_READY)
|
|
464
|
-
// core.activePlayback.el = document.createElement('video')
|
|
465
|
-
// core.activePlayback.name = playbackEngine
|
|
466
|
-
// core.activePlayback.setTextTrack = vi.fn()
|
|
467
|
-
// core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
|
|
468
434
|
emitSubtitleAvailable(core)
|
|
469
435
|
vi.advanceTimersByTime(1)
|
|
470
436
|
})
|
|
471
|
-
afterEach(() => {
|
|
472
|
-
vi.useRealTimers()
|
|
473
|
-
})
|
|
474
437
|
it('should call setTextTrack with matching track id', () => {
|
|
475
|
-
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(
|
|
438
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(0)
|
|
476
439
|
})
|
|
477
440
|
it('should set closedCaptionsTrackId to matching track id', () => {
|
|
478
|
-
expect(core.activePlayback.closedCaptionsTrackId).toEqual(
|
|
479
|
-
})
|
|
480
|
-
it('should not touch native tracks', () => {
|
|
481
|
-
expect(core.activePlayback.closedCaptionsTracks[0].track.mode).toBe('hidden')
|
|
482
|
-
expect(core.activePlayback.closedCaptionsTracks[1].track.mode).toBe('hidden')
|
|
441
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(0)
|
|
483
442
|
})
|
|
443
|
+
if (isManaged) {
|
|
444
|
+
it('should not touch native tracks', () => {
|
|
445
|
+
expect(core.activePlayback.el.textTracks[0].mode).toBe('hidden')
|
|
446
|
+
expect(core.activePlayback.el.textTracks[1].mode).toBe('hidden')
|
|
447
|
+
})
|
|
448
|
+
} else {
|
|
449
|
+
it('should disable inactive native track', () => {
|
|
450
|
+
expect(core.activePlayback.el.textTracks[1].mode).toBe('disabled')
|
|
451
|
+
})
|
|
452
|
+
it('should keep active native track hidden', () => {
|
|
453
|
+
expect(core.activePlayback.el.textTracks[0].mode).toBe('hidden')
|
|
454
|
+
})
|
|
455
|
+
}
|
|
484
456
|
})
|
|
485
457
|
describe('when language is configured but does not match any track', () => {
|
|
486
458
|
beforeEach(() => {
|
|
487
|
-
vi.useFakeTimers()
|
|
488
459
|
core.options.cc = { language: 'fr' }
|
|
489
|
-
cc = new ClosedCaptions(core)
|
|
490
460
|
core.emit(Events.CORE_READY)
|
|
491
461
|
core.activePlayback.el = document.createElement('video')
|
|
492
462
|
core.activePlayback.name = playbackEngine
|
|
@@ -495,9 +465,6 @@ describe('ClosedCaptions', () => {
|
|
|
495
465
|
emitSubtitleAvailable(core)
|
|
496
466
|
vi.advanceTimersByTime(1)
|
|
497
467
|
})
|
|
498
|
-
afterEach(() => {
|
|
499
|
-
vi.useRealTimers()
|
|
500
|
-
})
|
|
501
468
|
it('should disable subtitles', () => {
|
|
502
469
|
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(-1)
|
|
503
470
|
expect(core.activePlayback.closedCaptionsTrackId).toEqual(-1)
|
|
@@ -507,22 +474,20 @@ describe('ClosedCaptions', () => {
|
|
|
507
474
|
beforeEach(() => {
|
|
508
475
|
core.activePlayback.setTextTrack = vi.fn()
|
|
509
476
|
emitSubtitleAvailable(core)
|
|
477
|
+
vi.advanceTimersByTime(1)
|
|
510
478
|
cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
479
|
+
vi.advanceTimersByTime(1)
|
|
511
480
|
})
|
|
512
481
|
it('should call setTextTrack with selected track id', () => {
|
|
513
|
-
|
|
514
|
-
// expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(2)
|
|
482
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(1)
|
|
515
483
|
})
|
|
516
484
|
it('should update closedCaptionsTrackId', () => {
|
|
517
|
-
|
|
518
|
-
// expect(core.activePlayback.closedCaptionsTrackId).toEqual(2)
|
|
485
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(1)
|
|
519
486
|
})
|
|
520
487
|
})
|
|
521
488
|
describe('when language is set to none', () => {
|
|
522
489
|
beforeEach(() => {
|
|
523
|
-
vi.useFakeTimers()
|
|
524
490
|
core.options.cc = { language: 'none' }
|
|
525
|
-
cc = new ClosedCaptions(core)
|
|
526
491
|
core.emit(Events.CORE_READY)
|
|
527
492
|
core.activePlayback.el = document.createElement('video')
|
|
528
493
|
core.activePlayback.name = playbackEngine
|
|
@@ -531,12 +496,8 @@ describe('ClosedCaptions', () => {
|
|
|
531
496
|
emitSubtitleAvailable(core)
|
|
532
497
|
vi.advanceTimersByTime(1)
|
|
533
498
|
})
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
})
|
|
537
|
-
it('should call setTextTrack with -1 to disable subtitles', () => {
|
|
538
|
-
// TODO: Implement assertion
|
|
539
|
-
// expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(-1)
|
|
499
|
+
it('should disable subtitles', () => {
|
|
500
|
+
expect(core.activePlayback.setTextTrack).toHaveBeenCalledWith(-1)
|
|
540
501
|
})
|
|
541
502
|
})
|
|
542
503
|
},
|
|
@@ -545,30 +506,30 @@ describe('ClosedCaptions', () => {
|
|
|
545
506
|
})
|
|
546
507
|
|
|
547
508
|
function emitSubtitleAvailable(core: any, selectedTrackId?: number) {
|
|
548
|
-
|
|
509
|
+
const domTracks = [
|
|
549
510
|
{
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
mode: selectedTrackId === 1 ? 'showing' : 'hidden',
|
|
557
|
-
cues: [],
|
|
558
|
-
},
|
|
511
|
+
language: 'en',
|
|
512
|
+
kind: 'subtitles',
|
|
513
|
+
label: 'English',
|
|
514
|
+
mode: selectedTrackId === 0 ? 'showing' : 'hidden',
|
|
515
|
+
cues: [],
|
|
516
|
+
id: "01en"
|
|
559
517
|
},
|
|
560
518
|
{
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
cues: [],
|
|
569
|
-
},
|
|
570
|
-
},
|
|
519
|
+
language: 'es',
|
|
520
|
+
kind: 'subtitles',
|
|
521
|
+
label: 'Español',
|
|
522
|
+
mode: selectedTrackId === 1 ? 'showing' : 'hidden',
|
|
523
|
+
cues: [],
|
|
524
|
+
id: "02es"
|
|
525
|
+
}
|
|
571
526
|
]
|
|
527
|
+
core.activePlayback.closedCaptionsTracks = domTracks.map((t, index) => ({
|
|
528
|
+
id: index,
|
|
529
|
+
name: t.label,
|
|
530
|
+
track: t,
|
|
531
|
+
}))
|
|
532
|
+
vi.spyOn(core.activePlayback.el, 'textTracks', 'get').mockReturnValue(domTracks)
|
|
572
533
|
core.activePlayback.emit(Events.PLAYBACK_SUBTITLE_AVAILABLE)
|
|
573
534
|
core.activeContainer.emit(Events.CONTAINER_SUBTITLE_AVAILABLE)
|
|
574
535
|
}
|
|
@@ -6,14 +6,14 @@ exports[`ClosedCaptions > basically > when subtitle tracks are available > shoul
|
|
|
6
6
|
<ul class="gcore-skin-bg-color media-control-dd__popup" id="gplayer-cc-menu" role="menu" style="display: none; max-height: 211px;">
|
|
7
7
|
|
|
8
8
|
<li class="">
|
|
9
|
-
<a href="#" class="gcore-skin-text-color" data-item="
|
|
9
|
+
<a href="#" class="gcore-skin-text-color" data-item="0" role="menuitemradio" aria-checked="false">
|
|
10
10
|
English
|
|
11
11
|
</a>
|
|
12
12
|
</li>
|
|
13
13
|
|
|
14
14
|
<li class="">
|
|
15
|
-
<a href="#" class="gcore-skin-text-color" data-item="
|
|
16
|
-
|
|
15
|
+
<a href="#" class="gcore-skin-text-color" data-item="1" role="menuitemradio" aria-checked="false">
|
|
16
|
+
Español
|
|
17
17
|
</a>
|
|
18
18
|
</li>
|
|
19
19
|
|
package/src/testUtils.ts
CHANGED
|
@@ -81,6 +81,7 @@ export function createMockPlayback(
|
|
|
81
81
|
canAutoPlay: vi.fn().mockImplementation(() => true),
|
|
82
82
|
onResize: vi.fn().mockImplementation(() => true),
|
|
83
83
|
setPlaybackRate: vi.fn(),
|
|
84
|
+
setTextTrack: vi.fn(),
|
|
84
85
|
switchAudioTrack: vi.fn(),
|
|
85
86
|
trigger: emitter.emit,
|
|
86
87
|
})
|
|
@@ -139,6 +140,8 @@ export function createMockMediaControl(core: any) {
|
|
|
139
140
|
// @ts-ignore
|
|
140
141
|
mediaControl.getAvailablePopupHeight = vi.fn().mockReturnValue(286)
|
|
141
142
|
// @ts-ignore
|
|
143
|
+
mediaControl.isVisible = vi.fn().mockReturnValue(true)
|
|
144
|
+
// @ts-ignore
|
|
142
145
|
mediaControl.toggleElement = vi.fn()
|
|
143
146
|
// @ts-ignore
|
|
144
147
|
mediaControl.setKeepVisible = vi.fn()
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
} from '../types'
|
|
8
8
|
import { trace } from '@gcorevideo/utils'
|
|
9
9
|
|
|
10
|
+
export const MIME_TYPES_HLS = ['application/x-mpegurl', 'application/vnd.apple.mpegurl']
|
|
11
|
+
export const MIME_TYPE_HLS = MIME_TYPES_HLS[0]
|
|
12
|
+
export const MIME_TYPE_DASH = 'application/dash+xml'
|
|
13
|
+
|
|
10
14
|
// TODO rewrite using the Playback classes and canPlay static methods
|
|
11
15
|
export function buildMediaSourcesList(
|
|
12
16
|
sources: PlayerMediaSourceDesc[],
|
|
@@ -58,26 +62,26 @@ export function wrapSource(s: PlayerMediaSource): PlayerMediaSourceDesc {
|
|
|
58
62
|
return typeof s === 'string' ? { source: s, mimeType: guessMimeType(s) } : s
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
|
|
66
|
+
export function guessMimeType(s: string): string | undefined {
|
|
62
67
|
if (s.endsWith('.mpd')) {
|
|
63
|
-
return
|
|
68
|
+
return MIME_TYPE_DASH
|
|
64
69
|
}
|
|
65
70
|
if (s.endsWith('.m3u8')) {
|
|
66
|
-
|
|
67
|
-
return 'application/x-mpegurl'
|
|
71
|
+
return MIME_TYPE_HLS
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
export function isDashSource(source: string, mimeType?: string) {
|
|
72
76
|
if (mimeType) {
|
|
73
|
-
return mimeType ===
|
|
77
|
+
return mimeType === MIME_TYPE_DASH // TODO consider video/mp4
|
|
74
78
|
}
|
|
75
79
|
return source.endsWith('.mpd')
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export function isHlsSource(source: string, mimeType?: string) {
|
|
79
83
|
if (mimeType) {
|
|
80
|
-
return
|
|
84
|
+
return MIME_TYPES_HLS.includes(
|
|
81
85
|
mimeType.toLowerCase(),
|
|
82
86
|
)
|
|
83
87
|
}
|