@gcorevideo/player 2.21.1 → 2.21.4

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.
Files changed (97) hide show
  1. package/assets/audio-selector/style.scss +1 -1
  2. package/assets/audio-selector/track-selector.ejs +3 -3
  3. package/assets/bottom-gear/bottomgear.ejs +2 -2
  4. package/assets/media-control/container.scss +1 -1
  5. package/assets/media-control/media-control.ejs +1 -11
  6. package/assets/media-control/media-control.scss +49 -57
  7. package/assets/media-control/width270.scss +1 -1
  8. package/assets/media-control/width370.scss +7 -9
  9. package/assets/playback-rate/button.ejs +2 -2
  10. package/assets/playback-rate/list.ejs +4 -4
  11. package/assets/subtitles/combobox.ejs +10 -12
  12. package/assets/subtitles/string.ejs +1 -1
  13. package/assets/subtitles/style.scss +9 -16
  14. package/dist/core.js +5 -1
  15. package/dist/index.css +782 -794
  16. package/dist/index.js +240 -244
  17. package/dist/player.d.ts +141 -119
  18. package/dist/plugins/index.css +862 -874
  19. package/dist/plugins/index.js +222 -238
  20. package/docs/api/player.bottomgear.getelement.md +2 -2
  21. package/docs/api/player.bottomgear.md +1 -1
  22. package/docs/api/{player.subtitles.hide.md → player.closedcaptions.hide.md} +2 -2
  23. package/docs/api/{player.subtitles.md → player.closedcaptions.md} +11 -11
  24. package/docs/api/{player.subtitles.show.md → player.closedcaptions.show.md} +2 -2
  25. package/docs/api/player.closedcaptionspluginsettings.md +13 -0
  26. package/docs/api/player.gearitemelement.md +6 -4
  27. package/docs/api/player.gearoptionsitem.md +16 -0
  28. package/docs/api/player.md +48 -12
  29. package/docs/api/player.mediacontrol.putelement.md +2 -2
  30. package/docs/api/player.mediacontrolelement.md +1 -1
  31. package/docs/api/player.playbackrate.md +1 -1
  32. package/docs/api/player.subtitlespluginsettings.md +18 -0
  33. package/docs/api/player.texttrackitem.id.md +11 -0
  34. package/docs/api/player.texttrackitem.md +87 -0
  35. package/docs/api/player.texttrackitem.name.md +11 -0
  36. package/docs/api/player.texttrackitem.track.md +11 -0
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.js +1 -1
  39. package/lib/index.plugins.d.ts +2 -1
  40. package/lib/index.plugins.d.ts.map +1 -1
  41. package/lib/index.plugins.js +2 -1
  42. package/lib/playback/BasePlayback.d.ts +1 -0
  43. package/lib/playback/BasePlayback.d.ts.map +1 -1
  44. package/lib/playback/BasePlayback.js +3 -0
  45. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  46. package/lib/playback/dash-playback/DashPlayback.js +1 -0
  47. package/lib/playback.types.d.ts +5 -0
  48. package/lib/playback.types.d.ts.map +1 -1
  49. package/lib/plugins/audio-selector/AudioSelector.d.ts +2 -3
  50. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  51. package/lib/plugins/audio-selector/AudioSelector.js +6 -7
  52. package/lib/plugins/bottom-gear/BottomGear.d.ts +7 -3
  53. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  54. package/lib/plugins/bottom-gear/BottomGear.js +4 -2
  55. package/lib/plugins/media-control/MediaControl.d.ts +5 -6
  56. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  57. package/lib/plugins/media-control/MediaControl.js +48 -39
  58. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +1 -0
  59. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  60. package/lib/plugins/picture-in-picture/PictureInPicture.js +4 -4
  61. package/lib/plugins/playback-rate/PlaybackRate.d.ts +1 -1
  62. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  63. package/lib/plugins/playback-rate/PlaybackRate.js +24 -14
  64. package/lib/plugins/subtitles/ClosedCaptions.d.ts +118 -0
  65. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -0
  66. package/lib/plugins/subtitles/ClosedCaptions.js +348 -0
  67. package/lib/plugins/subtitles/Subtitles.d.ts +31 -26
  68. package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
  69. package/lib/plugins/subtitles/Subtitles.js +138 -169
  70. package/lib/testUtils.d.ts +22 -18
  71. package/lib/testUtils.d.ts.map +1 -1
  72. package/lib/testUtils.js +22 -36
  73. package/package.json +1 -1
  74. package/src/index.plugins.ts +2 -1
  75. package/src/index.ts +1 -1
  76. package/src/playback/BasePlayback.ts +4 -0
  77. package/src/playback/dash-playback/DashPlayback.ts +1 -0
  78. package/src/playback.types.ts +6 -0
  79. package/src/plugins/audio-selector/AudioSelector.ts +9 -8
  80. package/src/plugins/bottom-gear/BottomGear.ts +14 -5
  81. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -1
  82. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +2 -2
  83. package/src/plugins/media-control/MediaControl.ts +84 -60
  84. package/src/plugins/media-control/__tests__/MediaControl.test.ts +43 -0
  85. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +175 -0
  86. package/src/plugins/picture-in-picture/PictureInPicture.ts +5 -5
  87. package/src/plugins/playback-rate/PlaybackRate.ts +143 -100
  88. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +65 -0
  89. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +11 -0
  90. package/src/plugins/subtitles/ClosedCaptions.ts +469 -0
  91. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +58 -0
  92. package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +25 -0
  93. package/src/testUtils.ts +22 -36
  94. package/temp/player.api.json +269 -89
  95. package/tsconfig.tsbuildinfo +1 -1
  96. package/src/plugins/index.ts +0 -39
  97. package/src/plugins/subtitles/Subtitles.ts +0 -496
@@ -1,39 +0,0 @@
1
- /**
2
- * @packageDocumentation
3
- * @beta
4
- * A collection of plugins for the Gcore video player
5
- */
6
-
7
- import '../assets/style/main.scss';
8
-
9
- export * from "./audio-selector/AudioSelector.js";
10
- export * from "./big-mute-button/BigMuteButton.js";
11
- export * from "./bottom-gear/BottomGear.js";
12
- export * from "./clappr-nerd-stats/ClapprNerdStats.js";
13
- export * from "./clappr-stats/ClapprStats.js";
14
- export * from "./click-to-pause/ClickToPause.js";
15
- export * from "./clips/Clips.js";
16
- export * from "./context-menu/ContextMenu.js";
17
- export * from "./dvr-controls/DvrControls.js";
18
- export * from "./error-screen/ErrorScreen.js";
19
- export * from "./favicon/Favicon.js";
20
- // _ ga-events
21
- export * from "./google-analytics/GoogleAnalytics.js";
22
- export * from "./level-selector/LevelSelector.js";
23
- export * from "./logo/Logo.js";
24
- export * from "./media-control/MediaControl.js";
25
- export * from "./multi-camera/MultiCamera.js";
26
- export * from "./picture-in-picture/PictureInPicture.js";
27
- export * from "./playback-rate/PlaybackRate.js";
28
- export * from "./poster/Poster.js";
29
- export * from "./seek-time/SeekTime.js";
30
- export * from "./share/Share.js";
31
- export * from "./skip-time/SkipTime.js";
32
- export * from "./source-controller/SourceController.js";
33
- export * from "./spinner-three-bounce/SpinnerThreeBounce.js";
34
- export * from "./subtitles/Subtitles.js";
35
- export * from "./telemetry/Telemetry.js";
36
- export * from "./thumbnails/Thumbnails.js";
37
- // _ vast-ads
38
- // _ video360
39
- export * from "./volume-fade/VolumeFade.js";
@@ -1,496 +0,0 @@
1
- import {
2
- Events,
3
- UICorePlugin,
4
- Browser,
5
- template,
6
- $,
7
- } from '@clappr/core'
8
- import { reportError, trace } from '@gcorevideo/utils'
9
- import assert from 'assert'
10
-
11
- import { CLAPPR_VERSION } from '../../build.js'
12
-
13
- import '../../../assets/subtitles/style.scss'
14
- import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg'
15
- import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg'
16
- import comboboxHTML from '../../../assets/subtitles/combobox.ejs'
17
- import stringHTML from '../../../assets/subtitles/string.ejs'
18
-
19
- import { isFullscreen } from '../utils.js'
20
- import type { ZeptoResult } from '../../types.js'
21
-
22
- const VERSION: string = '2.19.14'
23
-
24
- const LOCAL_STORAGE_SUBTITLES_ID =
25
- 'gplayer.plugins.subtitles.selected'
26
-
27
- const T = 'plugins.subtitles'
28
-
29
- type TextTrackInfo = {
30
- language: string
31
- mode?: 'showing' | 'hidden' | 'disabled'
32
- }
33
-
34
- const NO_TRACK = { language: 'off' }
35
-
36
- /**
37
- * `PLUGIN` that provides a UI to select the subtitles when available.
38
- * @beta
39
- *
40
- * @remarks
41
- * Depends on:
42
- *
43
- * - {@link MediaControl}
44
- *
45
- * Configuration options:
46
- *
47
- * - subtitles.language - The language of the subtitles to select by default.
48
- *
49
- * @example
50
- * ```ts
51
- * import { Subtitles } from '@gcorevideo/player'
52
- *
53
- * Player.registerPlugin(Subtitles)
54
- *
55
- * new Player({
56
- * ...
57
- * subtitles: {
58
- * language: 'en',
59
- * },
60
- * })
61
- * ```
62
- */
63
- export class Subtitles extends UICorePlugin {
64
- private currentLevel: TextTrackInfo | null = null
65
-
66
- private isPreselectedApplied = false
67
-
68
- private isShowing = false
69
-
70
- private track: TextTrackInfo = { ...NO_TRACK }
71
-
72
- private tracks: TextTrackList | null = null
73
-
74
- private $string: ZeptoResult | null = null
75
-
76
- /**
77
- * @internal
78
- */
79
- get name() {
80
- return 'subtitles'
81
- }
82
-
83
- /**
84
- * @internal
85
- */
86
- get supportedVersion() {
87
- return { min: CLAPPR_VERSION }
88
- }
89
-
90
- /**
91
- * @internal
92
- */
93
- static get version() {
94
- return VERSION
95
- }
96
-
97
- private static readonly template = template(comboboxHTML)
98
-
99
- private static readonly templateString = template(stringHTML)
100
-
101
- /**
102
- * @internal
103
- */
104
- override get attributes() {
105
- return {
106
- class: this.name,
107
- 'data-subtitles': '',
108
- }
109
- }
110
-
111
- /**
112
- * @internal
113
- */
114
- override get events() {
115
- return {
116
- 'click [data-subtitles-select]': 'onLevelSelect',
117
- 'click [data-subtitles-button]': 'onShowLevelSelectMenu',
118
- }
119
- }
120
-
121
- private get preselectedLanguage(): string {
122
- return this.core.options.subtitles?.language ?? 'off'
123
- }
124
-
125
- /**
126
- * @internal
127
- */
128
- override bindEvents() {
129
- const mediaControl = this.core.getPlugin('media_control')
130
- assert(mediaControl, 'media_control plugin is required')
131
- this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize)
132
- this.listenTo(
133
- this.core,
134
- Events.CORE_ACTIVE_CONTAINER_CHANGED,
135
- this.bindPlaybackEvents,
136
- )
137
- this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render)
138
- this.listenTo(
139
- mediaControl,
140
- Events.MEDIACONTROL_HIDE,
141
- this.hideSelectLevelMenu,
142
- )
143
- }
144
-
145
- private bindPlaybackEvents() {
146
- this.listenTo(
147
- this.core.activeContainer,
148
- Events.CONTAINER_FULLSCREEN,
149
- this.playerResize,
150
- )
151
- this.listenToOnce(
152
- this.core.activePlayback,
153
- Events.PLAYBACK_PLAY,
154
- this.getTracks,
155
- )
156
- this.listenTo(
157
- this.core.activeContainer,
158
- 'container:advertisement:start',
159
- this.onStartAd,
160
- )
161
-
162
- // fix for iOS
163
- const video = this.core.activePlayback.el
164
- assert(video, 'video element is required')
165
-
166
- video.addEventListener('webkitbeginfullscreen', () => {
167
- if (Browser.isiOS) {
168
- video.classList.add('ios-fullscreen')
169
- }
170
- })
171
-
172
- video.addEventListener('webkitendfullscreen', () => {
173
- if (Browser.isiOS) {
174
- video.classList.remove('ios-fullscreen')
175
- }
176
- })
177
- }
178
-
179
- private getTracks() {
180
- if (this.core.activePlayback) {
181
- try {
182
- const tracks = (this.core.activePlayback.el as HTMLMediaElement)
183
- .textTracks
184
- if (tracks.length > 0) {
185
- this.setTracks(tracks)
186
- }
187
- } catch (error) {
188
- reportError(error)
189
- }
190
- }
191
- }
192
-
193
- private onStartAd() {
194
- if (this.isShowing && this.core.activeContainer) {
195
- this.hide()
196
- this.listenTo(
197
- this.core.activeContainer,
198
- 'container:advertisement:finish',
199
- this.onFinishAd,
200
- )
201
- }
202
- }
203
-
204
- private onFinishAd() {
205
- this.show()
206
- this.stopListening(
207
- this.core.activeContainer,
208
- 'container:advertisement:finish',
209
- this.onFinishAd,
210
- )
211
- }
212
-
213
- private playerResize() {
214
- const shouldShow =
215
- this.core.activeContainer &&
216
- isFullscreen(this.core.activeContainer.el) &&
217
- this.currentLevel &&
218
- this.currentLevel.mode &&
219
- Browser.isiOS &&
220
- this.isShowing
221
-
222
- if (shouldShow) {
223
- this.show()
224
- }
225
-
226
- try {
227
- this.resizeFont()
228
- } catch (error) {
229
- reportError(error)
230
- }
231
- }
232
-
233
- /**
234
- * Hides the subtitles menu and the subtitles.
235
- */
236
- hide() {
237
- this.isShowing = false
238
- this.renderIcon()
239
- this.$string.hide()
240
- if (this.tracks) {
241
- for (const t of this.tracks) {
242
- t.mode = 'hidden'
243
- }
244
- }
245
- }
246
-
247
- /**
248
- * Shows the subtitles menu and the subtitles.
249
- */
250
- show() {
251
- this.isShowing = true
252
- this.renderIcon()
253
- if (
254
- this.core.activeContainer &&
255
- isFullscreen(this.core.activeContainer.el) &&
256
- this.currentLevel &&
257
- this.currentLevel.mode &&
258
- Browser.isiOS
259
- ) {
260
- this.$string.hide()
261
- this.currentLevel.mode = 'showing'
262
- } else {
263
- this.$string.show()
264
- }
265
- }
266
-
267
- private shouldRender() {
268
- return !!(this.tracks && this.tracks.length > 0)
269
- }
270
-
271
- private resizeFont() {
272
- if (!this.core.activeContainer) {
273
- return
274
- }
275
-
276
- if (!this.$string) {
277
- return
278
- }
279
-
280
- const skinWidth = this.core.activeContainer.$el.width()
281
-
282
- this.$string.find('p').css('font-size', skinWidth * 0.03)
283
- }
284
-
285
- /**
286
- * @internal
287
- */
288
- override render() {
289
- if (!this.core.activeContainer) {
290
- return this
291
- }
292
-
293
- if (!this.shouldRender()) {
294
- return this
295
- }
296
-
297
- trace(`${T} render`, {
298
- tracks: this.tracks?.length,
299
- track: this.track?.language,
300
- })
301
-
302
- const mediaControl = this.core.getPlugin('media_control')
303
- assert(mediaControl, 'media_control plugin is required')
304
-
305
- this.$el.html(Subtitles.template({ tracks: this.tracks }))
306
- this.core.activeContainer.$el.find('.subtitle-string').remove()
307
- this.$string = $(Subtitles.templateString())
308
- this.resizeFont()
309
-
310
- this.core.activeContainer.$el.append(this.$string[0])
311
- const ss = mediaControl.getElement('subtitlesSelector')
312
- if (ss && ss.length > 0) {
313
- ss.append(this.el)
314
- } else {
315
- mediaControl.getRightPanel().append(this.el)
316
- }
317
-
318
- this.updateCurrentLevel(this.track)
319
- this.highlightCurrentSubtitles()
320
-
321
- this.applyPreselectedSubtitles()
322
-
323
- this.renderIcon()
324
-
325
- return this
326
- }
327
-
328
- private setTracks(tracks: TextTrackList) {
329
- this.tracks = tracks
330
- this.render()
331
- }
332
-
333
- private findLevelBy(id: string) {
334
- if (this.tracks) {
335
- for (const track of this.tracks) {
336
- if (track.language === id) {
337
- return track // TODO TrackInfo?
338
- }
339
- }
340
- }
341
- }
342
-
343
- private selectLevel(id: string) {
344
- this.clearSubtitleText()
345
- this.track = this.findLevelBy(id) || { ...NO_TRACK }
346
-
347
- this.hideSelectLevelMenu()
348
- if (!this.track) {
349
- this.track = { language: 'off' }
350
- }
351
-
352
- this.updateCurrentLevel(this.track)
353
- }
354
-
355
- private onLevelSelect(event: MouseEvent) {
356
- const id = (event.target as HTMLElement).dataset.subtitlesSelect
357
-
358
- if (id) {
359
- localStorage.setItem(LOCAL_STORAGE_SUBTITLES_ID, id)
360
- this.selectLevel(id)
361
- }
362
-
363
- return false
364
- }
365
-
366
- private applyPreselectedSubtitles() {
367
- if (!this.isPreselectedApplied) {
368
- this.isPreselectedApplied = true
369
- setTimeout(() => {
370
- this.selectLevel(this.preselectedLanguage)
371
- }, 300)
372
- }
373
- }
374
-
375
- private onShowLevelSelectMenu() {
376
- trace(`${T} onShowLevelSelectMenu`)
377
- this.toggleContextMenu()
378
- }
379
-
380
- private hideSelectLevelMenu() {
381
- ;(this.$('[data-subtitles] ul') as ZeptoResult).hide()
382
- }
383
-
384
- private toggleContextMenu() {
385
- (this.$('[data-subtitles] ul') as ZeptoResult).toggle()
386
- }
387
-
388
- private buttonElement(): ZeptoResult {
389
- return this.$('[data-subtitles] button')
390
- }
391
-
392
- private levelElement(id?: string): ZeptoResult {
393
- return (
394
- this.$(
395
- '[data-subtitles] ul a' + (id ? '[data-subtitles-select="' + id + '"]' : ''),
396
- ) as ZeptoResult
397
- ).parent()
398
- }
399
-
400
- private startLevelSwitch() {
401
- this.buttonElement().addClass('changing')
402
- }
403
-
404
- private stopLevelSwitch() {
405
- this.buttonElement().removeClass('changing')
406
- }
407
-
408
- private selectSubtitles() {
409
- if (!this.currentLevel) {
410
- return
411
- }
412
-
413
- if (this.tracks) {
414
- for (let i = 0; i < this.tracks.length; i++) {
415
- const track = this.tracks[i]
416
- if (track.language === this.currentLevel.language) {
417
- track.mode = 'showing'
418
-
419
- const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0
420
- const cues = track.cues
421
- let subtitleText = ''
422
-
423
- if (cues && cues.length) {
424
- for (const cue of cues) {
425
- if (currentTime >= cue.startTime && currentTime <= cue.endTime) {
426
- subtitleText +=
427
- (cue as VTTCue).getCueAsHTML().textContent + '\n'
428
- }
429
- }
430
- }
431
-
432
- this.setSubtitleText(subtitleText)
433
-
434
- track.oncuechange = (e) => {
435
- try {
436
- if (track.activeCues?.length) {
437
- const html = (track.activeCues[0] as VTTCue).getCueAsHTML()
438
-
439
- this.setSubtitleText(html)
440
- } else {
441
- this.clearSubtitleText()
442
- }
443
- } catch (error) {
444
- // console.error(error);
445
- reportError(error)
446
- }
447
- }
448
- continue
449
- }
450
- this.tracks[i].oncuechange = null
451
- this.tracks[i].mode = 'hidden'
452
- }
453
- }
454
- }
455
-
456
- private setSubtitleText(text: string | DocumentFragment) {
457
- this.$string.find('p').html(text)
458
- }
459
-
460
- private clearSubtitleText() {
461
- this.setSubtitleText('')
462
- }
463
-
464
- private updateCurrentLevel(track: TextTrackInfo) {
465
- this.currentLevel = track
466
- if (track.language === 'off') {
467
- this.hide()
468
- } else {
469
- this.show()
470
- }
471
- this.selectSubtitles()
472
- this.highlightCurrentSubtitles()
473
- }
474
-
475
- private highlightCurrentSubtitles() {
476
- this.levelElement().removeClass('current')
477
- this.levelElement().find('a').removeClass('gcore-skin-active')
478
-
479
- if (this.currentLevel) {
480
- const currentLevelElement = this.levelElement(this.currentLevel.language)
481
-
482
- currentLevelElement.addClass('current')
483
- currentLevelElement.find('a').addClass('gcore-skin-active')
484
- }
485
- }
486
-
487
- private renderIcon() {
488
- const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon
489
-
490
- this.core
491
- .getPlugin('media_control')
492
- .getElement('subtitlesSelector')
493
- ?.find('span.subtitle-text')
494
- .html(icon)
495
- }
496
- }