@gcorevideo/player 2.21.3 → 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.
- package/assets/audio-selector/style.scss +1 -1
- package/assets/audio-selector/track-selector.ejs +3 -3
- package/assets/bottom-gear/bottomgear.ejs +2 -2
- package/assets/media-control/container.scss +1 -1
- package/assets/media-control/media-control.ejs +1 -6
- package/assets/media-control/media-control.scss +6 -4
- package/assets/media-control/width270.scss +1 -1
- package/assets/media-control/width370.scss +4 -4
- package/assets/playback-rate/button.ejs +2 -2
- package/assets/playback-rate/list.ejs +4 -4
- package/assets/subtitles/combobox.ejs +5 -5
- package/assets/subtitles/string.ejs +1 -1
- package/assets/subtitles/style.scss +2 -2
- package/dist/core.js +2 -1
- package/dist/index.css +972 -967
- package/dist/index.js +126 -106
- package/dist/player.d.ts +141 -119
- package/dist/plugins/index.css +801 -796
- package/dist/plugins/index.js +119 -104
- package/docs/api/player.bottomgear.getelement.md +2 -2
- package/docs/api/player.bottomgear.md +1 -1
- package/docs/api/{player.subtitles.hide.md → player.closedcaptions.hide.md} +2 -2
- package/docs/api/{player.subtitles.md → player.closedcaptions.md} +11 -11
- package/docs/api/{player.subtitles.show.md → player.closedcaptions.show.md} +2 -2
- package/docs/api/player.closedcaptionspluginsettings.md +13 -0
- package/docs/api/player.gearitemelement.md +6 -4
- package/docs/api/player.gearoptionsitem.md +16 -0
- package/docs/api/player.md +48 -12
- package/docs/api/player.mediacontrol.putelement.md +2 -2
- package/docs/api/player.mediacontrolelement.md +1 -1
- package/docs/api/player.playbackrate.md +1 -1
- package/docs/api/player.subtitlespluginsettings.md +18 -0
- package/docs/api/player.texttrackitem.id.md +11 -0
- package/docs/api/player.texttrackitem.md +87 -0
- package/docs/api/player.texttrackitem.name.md +11 -0
- package/docs/api/player.texttrackitem.track.md +11 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/index.plugins.d.ts +2 -1
- package/lib/index.plugins.d.ts.map +1 -1
- package/lib/index.plugins.js +2 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +1 -0
- package/lib/plugins/audio-selector/AudioSelector.d.ts +2 -3
- package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
- package/lib/plugins/audio-selector/AudioSelector.js +6 -7
- package/lib/plugins/bottom-gear/BottomGear.d.ts +6 -2
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +2 -1
- package/lib/plugins/media-control/MediaControl.d.ts +5 -5
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +46 -39
- package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +1 -0
- package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
- package/lib/plugins/picture-in-picture/PictureInPicture.js +4 -4
- package/lib/plugins/playback-rate/PlaybackRate.d.ts +0 -1
- package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
- package/lib/plugins/playback-rate/PlaybackRate.js +23 -14
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +118 -0
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -0
- package/lib/plugins/subtitles/ClosedCaptions.js +348 -0
- package/lib/plugins/subtitles/Subtitles.d.ts +12 -9
- package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
- package/lib/plugins/subtitles/Subtitles.js +31 -32
- package/lib/testUtils.d.ts +22 -18
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +22 -36
- package/package.json +1 -1
- package/src/index.plugins.ts +2 -1
- package/src/index.ts +1 -1
- package/src/playback/dash-playback/DashPlayback.ts +1 -0
- package/src/plugins/audio-selector/AudioSelector.ts +9 -8
- package/src/plugins/bottom-gear/BottomGear.ts +11 -4
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -1
- package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +2 -2
- package/src/plugins/media-control/MediaControl.ts +53 -46
- package/src/plugins/media-control/__tests__/MediaControl.test.ts +43 -0
- package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +175 -0
- package/src/plugins/picture-in-picture/PictureInPicture.ts +5 -5
- package/src/plugins/playback-rate/PlaybackRate.ts +142 -100
- package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +65 -0
- package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +11 -0
- package/src/plugins/subtitles/{Subtitles.ts → ClosedCaptions.ts} +42 -34
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +58 -0
- package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +25 -0
- package/src/testUtils.ts +22 -36
- package/temp/player.api.json +269 -89
- package/tsconfig.tsbuildinfo +1 -1
- package/src/plugins/index.ts +0 -39
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { Events, UICorePlugin, Browser, template, $ } from '@clappr/core';
|
|
2
|
+
import { reportError, trace } from '@gcorevideo/utils';
|
|
3
|
+
import assert from 'assert';
|
|
4
|
+
import { CLAPPR_VERSION } from '../../build.js';
|
|
5
|
+
import '../../../assets/subtitles/style.scss';
|
|
6
|
+
import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg';
|
|
7
|
+
import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg';
|
|
8
|
+
import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
|
|
9
|
+
import stringHTML from '../../../assets/subtitles/string.ejs';
|
|
10
|
+
import { isFullscreen } from '../utils.js';
|
|
11
|
+
const VERSION = '2.19.14';
|
|
12
|
+
const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected';
|
|
13
|
+
const T = 'plugins.cc';
|
|
14
|
+
/**
|
|
15
|
+
* `PLUGIN` that provides a UI to select the subtitles when available.
|
|
16
|
+
* @beta
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* The plugin is activated when closed captions tracks are provided with the media source.
|
|
20
|
+
* It shows a familiar "CC" button with a dropdown menu to select the subtitles language.
|
|
21
|
+
*
|
|
22
|
+
* Depends on:
|
|
23
|
+
*
|
|
24
|
+
* - {@link MediaControl}
|
|
25
|
+
*
|
|
26
|
+
* Configuration options - {@link ClosedCaptionsPluginSettings}
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { ClosedCaptions } from '@gcorevideo/player'
|
|
30
|
+
*
|
|
31
|
+
* Player.registerPlugin(ClosedCaptions)
|
|
32
|
+
*
|
|
33
|
+
* new Player({
|
|
34
|
+
* ...
|
|
35
|
+
* cc: {
|
|
36
|
+
* language: 'en',
|
|
37
|
+
* },
|
|
38
|
+
* })
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export class ClosedCaptions extends UICorePlugin {
|
|
42
|
+
isPreselectedApplied = false;
|
|
43
|
+
isShowing = false;
|
|
44
|
+
track = null;
|
|
45
|
+
tracks = [];
|
|
46
|
+
$line = null;
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
get name() {
|
|
51
|
+
return 'cc';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
get supportedVersion() {
|
|
57
|
+
return { min: CLAPPR_VERSION };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
static get version() {
|
|
63
|
+
return VERSION;
|
|
64
|
+
}
|
|
65
|
+
static template = template(comboboxHTML);
|
|
66
|
+
static templateString = template(stringHTML);
|
|
67
|
+
/**
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
get attributes() {
|
|
71
|
+
return {
|
|
72
|
+
class: 'media-control-cc',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
get events() {
|
|
79
|
+
return {
|
|
80
|
+
'click [data-cc-select]': 'onItemSelect',
|
|
81
|
+
'click [data-cc-button]': 'toggleMenu',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
get preselectedLanguage() {
|
|
85
|
+
return this.core.options.cc?.language ?? this.core.options.subtitles?.language ?? '';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
bindEvents() {
|
|
91
|
+
this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
|
|
92
|
+
this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize);
|
|
93
|
+
this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onContainerChanged);
|
|
94
|
+
}
|
|
95
|
+
onCoreReady() {
|
|
96
|
+
trace(`${T} onCoreReady`);
|
|
97
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
98
|
+
assert(mediaControl, 'media_control plugin is required');
|
|
99
|
+
this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
|
|
100
|
+
this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu);
|
|
101
|
+
}
|
|
102
|
+
onContainerChanged() {
|
|
103
|
+
trace(`${T} onContainerChanged`);
|
|
104
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
|
|
105
|
+
this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
|
|
106
|
+
this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_AVAILABLE, this.onSubtitleAvailable);
|
|
107
|
+
this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_CHANGED, this.onSubtitleChanged);
|
|
108
|
+
// fix for iOS
|
|
109
|
+
const video = this.core.activePlayback.el;
|
|
110
|
+
assert(video, 'video element is required');
|
|
111
|
+
video.addEventListener('webkitbeginfullscreen', () => {
|
|
112
|
+
if (Browser.isiOS) {
|
|
113
|
+
video.classList.add('ios-fullscreen');
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
video.addEventListener('webkitendfullscreen', () => {
|
|
117
|
+
if (Browser.isiOS) {
|
|
118
|
+
video.classList.remove('ios-fullscreen');
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
onSubtitleAvailable() {
|
|
123
|
+
trace(`${T} onSubtitleAvailable`);
|
|
124
|
+
this.applyTracks();
|
|
125
|
+
}
|
|
126
|
+
onSubtitleChanged({ id }) {
|
|
127
|
+
trace(`${T} onSubtitleChanged`, { id });
|
|
128
|
+
if (id === -1) {
|
|
129
|
+
this.clearSubtitleText();
|
|
130
|
+
}
|
|
131
|
+
for (const track of this.tracks) {
|
|
132
|
+
if (track.id === id) {
|
|
133
|
+
track.track.mode = 'showing';
|
|
134
|
+
this.setSubtitleText(this.getSubtitleText(track.track));
|
|
135
|
+
track.track.oncuechange = (e) => {
|
|
136
|
+
try {
|
|
137
|
+
if (track.track.activeCues?.length) {
|
|
138
|
+
const html = track.track.activeCues[0].getCueAsHTML();
|
|
139
|
+
this.setSubtitleText(html);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.clearSubtitleText();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
reportError(error);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
track.track.oncuechange = null;
|
|
152
|
+
track.track.mode = 'hidden';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
applyTracks() {
|
|
157
|
+
try {
|
|
158
|
+
this.tracks = this.core.activePlayback.closedCaptionsTracks;
|
|
159
|
+
this.applyPreselectedSubtitles();
|
|
160
|
+
this.render();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
reportError(error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
onStartAd() {
|
|
167
|
+
if (this.isShowing && this.core.activeContainer) {
|
|
168
|
+
this.hide();
|
|
169
|
+
this.listenTo(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
onFinishAd() {
|
|
173
|
+
this.show();
|
|
174
|
+
this.stopListening(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
|
|
175
|
+
}
|
|
176
|
+
playerResize() {
|
|
177
|
+
trace(`${T} playerResize`);
|
|
178
|
+
const shouldShow = this.core.activeContainer &&
|
|
179
|
+
isFullscreen(this.core.activeContainer.el) &&
|
|
180
|
+
this.track &&
|
|
181
|
+
this.track.track.mode &&
|
|
182
|
+
Browser.isiOS &&
|
|
183
|
+
this.isShowing;
|
|
184
|
+
if (shouldShow) {
|
|
185
|
+
this.show();
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
this.resizeFont();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
reportError(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Hides the subtitles menu and the subtitles.
|
|
196
|
+
*/
|
|
197
|
+
hide() {
|
|
198
|
+
this.isShowing = false;
|
|
199
|
+
this.renderIcon();
|
|
200
|
+
this.$line.hide();
|
|
201
|
+
if (this.tracks) {
|
|
202
|
+
for (const t of this.tracks) {
|
|
203
|
+
t.track.mode = 'hidden';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Shows the subtitles menu and the subtitles.
|
|
209
|
+
*/
|
|
210
|
+
show() {
|
|
211
|
+
this.isShowing = true;
|
|
212
|
+
this.renderIcon();
|
|
213
|
+
if (this.core.activeContainer &&
|
|
214
|
+
isFullscreen(this.core.activeContainer.el) &&
|
|
215
|
+
this.track &&
|
|
216
|
+
this.track.track.mode &&
|
|
217
|
+
Browser.isiOS) {
|
|
218
|
+
this.$line.hide();
|
|
219
|
+
this.track.track.mode = 'showing';
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.$line.show();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
shouldRender() {
|
|
226
|
+
return this.tracks?.length > 0;
|
|
227
|
+
}
|
|
228
|
+
resizeFont() {
|
|
229
|
+
if (!this.$line) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const skinWidth = this.core.activeContainer.$el.width();
|
|
233
|
+
this.$line.find('p').css('font-size', skinWidth * 0.03);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
render() {
|
|
239
|
+
if (!this.core.activeContainer) {
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
if (!this.shouldRender()) {
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
246
|
+
this.$el.html(ClosedCaptions.template({ tracks: this.tracks }));
|
|
247
|
+
this.core.activeContainer.$el.find('#cc-line').remove();
|
|
248
|
+
this.$line = $(ClosedCaptions.templateString());
|
|
249
|
+
this.resizeFont();
|
|
250
|
+
this.core.activeContainer.$el.append(this.$line);
|
|
251
|
+
mediaControl.putElement('cc', this.el);
|
|
252
|
+
this.updateSelection();
|
|
253
|
+
this.renderIcon();
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
findById(id) {
|
|
257
|
+
return this.tracks.find((track) => track.id === id) ?? null;
|
|
258
|
+
}
|
|
259
|
+
selectItem(item) {
|
|
260
|
+
this.clearSubtitleText();
|
|
261
|
+
this.track = item;
|
|
262
|
+
this.hideMenu();
|
|
263
|
+
this.updateSelection();
|
|
264
|
+
}
|
|
265
|
+
onItemSelect(event) {
|
|
266
|
+
const id = event.target.dataset.ccSelect ?? '-1';
|
|
267
|
+
trace(`${T} onItemSelect`, { id });
|
|
268
|
+
localStorage.setItem(LOCAL_STORAGE_CC_ID, id);
|
|
269
|
+
this.selectItem(this.findById(Number(id)));
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
applyPreselectedSubtitles() {
|
|
273
|
+
if (!this.isPreselectedApplied) {
|
|
274
|
+
this.isPreselectedApplied = true;
|
|
275
|
+
if (!this.preselectedLanguage) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
this.selectItem(this.tracks.find((t) => t.track.language === this.preselectedLanguage) ?? null);
|
|
280
|
+
}, 300); // TODO why delay?
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
hideMenu() {
|
|
284
|
+
;
|
|
285
|
+
this.$('[data-cc] ul').hide();
|
|
286
|
+
}
|
|
287
|
+
toggleMenu() {
|
|
288
|
+
trace(`${T} toggleMenu`);
|
|
289
|
+
this.$('[data-cc] ul').toggle();
|
|
290
|
+
}
|
|
291
|
+
itemElement(id) {
|
|
292
|
+
return this.$(`ul li a[data-cc-select="${id}"]`).parent();
|
|
293
|
+
}
|
|
294
|
+
allItemElements() {
|
|
295
|
+
return this.$('[data-cc] li');
|
|
296
|
+
}
|
|
297
|
+
selectSubtitles() {
|
|
298
|
+
const trackId = this.track ? this.track.id : -1;
|
|
299
|
+
this.core.activePlayback.closedCaptionsTrackId = trackId;
|
|
300
|
+
}
|
|
301
|
+
getSubtitleText(track) {
|
|
302
|
+
const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
|
|
303
|
+
const cues = track.cues;
|
|
304
|
+
const lines = [];
|
|
305
|
+
if (cues && cues.length) {
|
|
306
|
+
for (const cue of cues) {
|
|
307
|
+
if (currentTime >= cue.startTime && currentTime <= cue.endTime) {
|
|
308
|
+
lines.push(cue.getCueAsHTML().textContent);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return lines.join('\n');
|
|
313
|
+
}
|
|
314
|
+
setSubtitleText(text) {
|
|
315
|
+
this.$line.find('p').html(text);
|
|
316
|
+
}
|
|
317
|
+
clearSubtitleText() {
|
|
318
|
+
this.setSubtitleText('');
|
|
319
|
+
}
|
|
320
|
+
updateSelection() {
|
|
321
|
+
if (!this.track) {
|
|
322
|
+
this.hide();
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
this.show();
|
|
326
|
+
}
|
|
327
|
+
this.selectSubtitles();
|
|
328
|
+
this.highlightCurrentSubtitles();
|
|
329
|
+
}
|
|
330
|
+
highlightCurrentSubtitles() {
|
|
331
|
+
this.allItemElements()
|
|
332
|
+
.removeClass('current')
|
|
333
|
+
.find('a')
|
|
334
|
+
.removeClass('gcore-skin-active');
|
|
335
|
+
trace(`${T} highlightCurrentSubtitles`, {
|
|
336
|
+
track: this.track?.id,
|
|
337
|
+
});
|
|
338
|
+
const currentLevelElement = this.itemElement(this.track ? this.track.id : -1);
|
|
339
|
+
currentLevelElement
|
|
340
|
+
.addClass('current')
|
|
341
|
+
.find('a')
|
|
342
|
+
.addClass('gcore-skin-active');
|
|
343
|
+
}
|
|
344
|
+
renderIcon() {
|
|
345
|
+
const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon;
|
|
346
|
+
this.$el.find('span.cc-text').html(icon);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { UICorePlugin } from '@clappr/core';
|
|
2
2
|
import '../../../assets/subtitles/style.scss';
|
|
3
|
-
export type
|
|
3
|
+
export type ClosedCaptionsPluginSettings = {
|
|
4
4
|
/**
|
|
5
5
|
* Initially selected subtitles language
|
|
6
6
|
*/
|
|
7
7
|
language?: string;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Use {@link ClosedCaptionsPluginSettings} instead.
|
|
11
|
+
*/
|
|
12
|
+
export type SubtitlesPluginSettings = ClosedCaptionsPluginSettings;
|
|
9
13
|
/**
|
|
10
14
|
* `PLUGIN` that provides a UI to select the subtitles when available.
|
|
11
15
|
* @beta
|
|
@@ -15,16 +19,16 @@ export type SubtitlesPluginSettings = {
|
|
|
15
19
|
*
|
|
16
20
|
* - {@link MediaControl}
|
|
17
21
|
*
|
|
18
|
-
* Configuration options - {@link
|
|
22
|
+
* Configuration options - {@link ClosedCaptionsPluginSettings}
|
|
19
23
|
* @example
|
|
20
24
|
* ```ts
|
|
21
|
-
* import {
|
|
25
|
+
* import { ClosedCaptions } from '@gcorevideo/player'
|
|
22
26
|
*
|
|
23
|
-
* Player.registerPlugin(
|
|
27
|
+
* Player.registerPlugin(ClosedCaptions)
|
|
24
28
|
*
|
|
25
29
|
* new Player({
|
|
26
30
|
* ...
|
|
27
|
-
*
|
|
31
|
+
* cc: {
|
|
28
32
|
* language: 'en',
|
|
29
33
|
* },
|
|
30
34
|
* })
|
|
@@ -35,7 +39,7 @@ export declare class Subtitles extends UICorePlugin {
|
|
|
35
39
|
private isShowing;
|
|
36
40
|
private track;
|
|
37
41
|
private tracks;
|
|
38
|
-
private $
|
|
42
|
+
private $line;
|
|
39
43
|
/**
|
|
40
44
|
* @internal
|
|
41
45
|
*/
|
|
@@ -57,14 +61,13 @@ export declare class Subtitles extends UICorePlugin {
|
|
|
57
61
|
*/
|
|
58
62
|
get attributes(): {
|
|
59
63
|
class: string;
|
|
60
|
-
'data-subtitles': string;
|
|
61
64
|
};
|
|
62
65
|
/**
|
|
63
66
|
* @internal
|
|
64
67
|
*/
|
|
65
68
|
get events(): {
|
|
66
|
-
'click [data-
|
|
67
|
-
'click [data-
|
|
69
|
+
'click [data-cc-select]': string;
|
|
70
|
+
'click [data-cc-button]': string;
|
|
68
71
|
};
|
|
69
72
|
private get preselectedLanguage();
|
|
70
73
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Subtitles.d.ts","sourceRoot":"","sources":["../../../src/plugins/subtitles/Subtitles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAwB,MAAM,cAAc,CAAA;AAOzE,OAAO,sCAAsC,CAAA;AAe7C,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"Subtitles.d.ts","sourceRoot":"","sources":["../../../src/plugins/subtitles/Subtitles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAwB,MAAM,cAAc,CAAA;AAOzE,OAAO,sCAAsC,CAAA;AAe7C,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,4BAA4B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,SAAU,SAAQ,YAAY;IACzC,OAAO,CAAC,oBAAoB,CAAQ;IAEpC,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,KAAK,CAA6B;IAE1C,OAAO,CAAC,MAAM,CAAsB;IAEpC,OAAO,CAAC,KAAK,CAA2B;IAExC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAEzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAuB;IAE7D;;OAEG;IACH,IAAa,UAAU;;MAItB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED,OAAO,KAAK,mBAAmB,GAE9B;IAED;;OAEG;IACM,UAAU;IAUnB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,kBAAkB;IAwC1B,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,YAAY;IAqBpB;;OAEG;IACH,IAAI;IAWJ;;OAEG;IACH,IAAI;IAiBJ,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IAUlB;;OAEG;IACM,MAAM;IA0Bf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,yBAAyB;IAgBjC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,yBAAyB;IAgBjC,OAAO,CAAC,UAAU;CAKnB"}
|
|
@@ -9,8 +9,8 @@ import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
|
|
|
9
9
|
import stringHTML from '../../../assets/subtitles/string.ejs';
|
|
10
10
|
import { isFullscreen } from '../utils.js';
|
|
11
11
|
const VERSION = '2.19.14';
|
|
12
|
-
const
|
|
13
|
-
const T = 'plugins.
|
|
12
|
+
const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected';
|
|
13
|
+
const T = 'plugins.cc';
|
|
14
14
|
/**
|
|
15
15
|
* `PLUGIN` that provides a UI to select the subtitles when available.
|
|
16
16
|
* @beta
|
|
@@ -20,16 +20,16 @@ const T = 'plugins.subtitles';
|
|
|
20
20
|
*
|
|
21
21
|
* - {@link MediaControl}
|
|
22
22
|
*
|
|
23
|
-
* Configuration options - {@link
|
|
23
|
+
* Configuration options - {@link ClosedCaptionsPluginSettings}
|
|
24
24
|
* @example
|
|
25
25
|
* ```ts
|
|
26
|
-
* import {
|
|
26
|
+
* import { ClosedCaptions } from '@gcorevideo/player'
|
|
27
27
|
*
|
|
28
|
-
* Player.registerPlugin(
|
|
28
|
+
* Player.registerPlugin(ClosedCaptions)
|
|
29
29
|
*
|
|
30
30
|
* new Player({
|
|
31
31
|
* ...
|
|
32
|
-
*
|
|
32
|
+
* cc: {
|
|
33
33
|
* language: 'en',
|
|
34
34
|
* },
|
|
35
35
|
* })
|
|
@@ -40,12 +40,12 @@ export class Subtitles extends UICorePlugin {
|
|
|
40
40
|
isShowing = false;
|
|
41
41
|
track = null;
|
|
42
42
|
tracks = [];
|
|
43
|
-
$
|
|
43
|
+
$line = null;
|
|
44
44
|
/**
|
|
45
45
|
* @internal
|
|
46
46
|
*/
|
|
47
47
|
get name() {
|
|
48
|
-
return 'subtitles';
|
|
48
|
+
return 'subtitles'; // TODO rename to 'cc'
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* @internal
|
|
@@ -66,8 +66,7 @@ export class Subtitles extends UICorePlugin {
|
|
|
66
66
|
*/
|
|
67
67
|
get attributes() {
|
|
68
68
|
return {
|
|
69
|
-
class: 'media-control-
|
|
70
|
-
'data-subtitles': '',
|
|
69
|
+
class: 'media-control-cc',
|
|
71
70
|
};
|
|
72
71
|
}
|
|
73
72
|
/**
|
|
@@ -75,12 +74,12 @@ export class Subtitles extends UICorePlugin {
|
|
|
75
74
|
*/
|
|
76
75
|
get events() {
|
|
77
76
|
return {
|
|
78
|
-
'click [data-
|
|
79
|
-
'click [data-
|
|
77
|
+
'click [data-cc-select]': 'onItemSelect',
|
|
78
|
+
'click [data-cc-button]': 'toggleMenu',
|
|
80
79
|
};
|
|
81
80
|
}
|
|
82
81
|
get preselectedLanguage() {
|
|
83
|
-
return this.core.options.subtitles?.language ?? '';
|
|
82
|
+
return this.core.options.cc?.language ?? this.core.options.subtitles?.language ?? '';
|
|
84
83
|
}
|
|
85
84
|
/**
|
|
86
85
|
* @internal
|
|
@@ -195,7 +194,7 @@ export class Subtitles extends UICorePlugin {
|
|
|
195
194
|
hide() {
|
|
196
195
|
this.isShowing = false;
|
|
197
196
|
this.renderIcon();
|
|
198
|
-
this.$
|
|
197
|
+
this.$line.hide();
|
|
199
198
|
if (this.tracks) {
|
|
200
199
|
for (const t of this.tracks) {
|
|
201
200
|
t.track.mode = 'hidden';
|
|
@@ -213,22 +212,22 @@ export class Subtitles extends UICorePlugin {
|
|
|
213
212
|
this.track &&
|
|
214
213
|
this.track.track.mode &&
|
|
215
214
|
Browser.isiOS) {
|
|
216
|
-
this.$
|
|
215
|
+
this.$line.hide();
|
|
217
216
|
this.track.track.mode = 'showing';
|
|
218
217
|
}
|
|
219
218
|
else {
|
|
220
|
-
this.$
|
|
219
|
+
this.$line.show();
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
222
|
shouldRender() {
|
|
224
|
-
return this.tracks
|
|
223
|
+
return this.tracks?.length > 0;
|
|
225
224
|
}
|
|
226
225
|
resizeFont() {
|
|
227
|
-
if (!this.$
|
|
226
|
+
if (!this.$line) {
|
|
228
227
|
return;
|
|
229
228
|
}
|
|
230
229
|
const skinWidth = this.core.activeContainer.$el.width();
|
|
231
|
-
this.$
|
|
230
|
+
this.$line.find('p').css('font-size', skinWidth * 0.03);
|
|
232
231
|
}
|
|
233
232
|
/**
|
|
234
233
|
* @internal
|
|
@@ -242,11 +241,11 @@ export class Subtitles extends UICorePlugin {
|
|
|
242
241
|
}
|
|
243
242
|
const mediaControl = this.core.getPlugin('media_control');
|
|
244
243
|
this.$el.html(Subtitles.template({ tracks: this.tracks }));
|
|
245
|
-
this.core.activeContainer.$el.find('
|
|
246
|
-
this.$
|
|
244
|
+
this.core.activeContainer.$el.find('#cc-line').remove();
|
|
245
|
+
this.$line = $(Subtitles.templateString());
|
|
247
246
|
this.resizeFont();
|
|
248
|
-
this.core.activeContainer.$el.append(this.$
|
|
249
|
-
mediaControl.putElement('
|
|
247
|
+
this.core.activeContainer.$el.append(this.$line);
|
|
248
|
+
mediaControl.putElement('cc', this.el);
|
|
250
249
|
this.updateSelection();
|
|
251
250
|
this.renderIcon();
|
|
252
251
|
return this;
|
|
@@ -261,9 +260,9 @@ export class Subtitles extends UICorePlugin {
|
|
|
261
260
|
this.updateSelection();
|
|
262
261
|
}
|
|
263
262
|
onItemSelect(event) {
|
|
264
|
-
const id = event.target.dataset.
|
|
263
|
+
const id = event.target.dataset.ccSelect ?? '-1';
|
|
265
264
|
trace(`${T} onItemSelect`, { id });
|
|
266
|
-
localStorage.setItem(
|
|
265
|
+
localStorage.setItem(LOCAL_STORAGE_CC_ID, id);
|
|
267
266
|
this.selectItem(this.findById(Number(id)));
|
|
268
267
|
return false;
|
|
269
268
|
}
|
|
@@ -280,17 +279,17 @@ export class Subtitles extends UICorePlugin {
|
|
|
280
279
|
}
|
|
281
280
|
hideMenu() {
|
|
282
281
|
;
|
|
283
|
-
this.$('[data-
|
|
282
|
+
this.$('[data-cc] ul').hide();
|
|
284
283
|
}
|
|
285
284
|
toggleMenu() {
|
|
286
|
-
;
|
|
287
|
-
this.$('[data-
|
|
285
|
+
trace(`${T} toggleMenu`);
|
|
286
|
+
this.$('[data-cc] ul').toggle();
|
|
288
287
|
}
|
|
289
288
|
itemElement(id) {
|
|
290
|
-
return this.$(`ul li a[data-
|
|
289
|
+
return this.$(`ul li a[data-cc-select="${id}"]`).parent();
|
|
291
290
|
}
|
|
292
291
|
allItemElements() {
|
|
293
|
-
return this.$('[data-
|
|
292
|
+
return this.$('[data-cc] li');
|
|
294
293
|
}
|
|
295
294
|
selectSubtitles() {
|
|
296
295
|
const trackId = this.track ? this.track.id : -1;
|
|
@@ -310,7 +309,7 @@ export class Subtitles extends UICorePlugin {
|
|
|
310
309
|
return lines.join('\n');
|
|
311
310
|
}
|
|
312
311
|
setSubtitleText(text) {
|
|
313
|
-
this.$
|
|
312
|
+
this.$line.find('p').html(text);
|
|
314
313
|
}
|
|
315
314
|
clearSubtitleText() {
|
|
316
315
|
this.setSubtitleText('');
|
|
@@ -341,6 +340,6 @@ export class Subtitles extends UICorePlugin {
|
|
|
341
340
|
}
|
|
342
341
|
renderIcon() {
|
|
343
342
|
const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon;
|
|
344
|
-
this.$el.find('span.
|
|
343
|
+
this.$el.find('span.cc-text').html(icon);
|
|
345
344
|
}
|
|
346
345
|
}
|
package/lib/testUtils.d.ts
CHANGED
|
@@ -65,29 +65,31 @@ export declare function createSpinnerPlugin(): Events<string | symbol, any> & {
|
|
|
65
65
|
export declare function createMockPlayback(name?: string): Events<string | symbol, any> & {
|
|
66
66
|
name: string;
|
|
67
67
|
currentLevel: number;
|
|
68
|
+
dvrEnabled: boolean;
|
|
68
69
|
levels: never[];
|
|
69
70
|
consent(): void;
|
|
70
71
|
play(): void;
|
|
71
72
|
pause(): void;
|
|
72
73
|
stop(): void;
|
|
73
|
-
destroy():
|
|
74
|
-
seek():
|
|
75
|
-
seekPercentage():
|
|
76
|
-
getDuration():
|
|
77
|
-
enterPiP():
|
|
78
|
-
exitPiP():
|
|
79
|
-
getPlaybackType():
|
|
80
|
-
getStartTimeOffset():
|
|
81
|
-
getCurrentTime():
|
|
82
|
-
isHighDefinitionInUse():
|
|
83
|
-
mute():
|
|
84
|
-
unmute():
|
|
85
|
-
volume():
|
|
86
|
-
configure():
|
|
87
|
-
attemptAutoPlay():
|
|
88
|
-
canAutoPlay():
|
|
89
|
-
onResize():
|
|
90
|
-
|
|
74
|
+
destroy: import("vitest").Mock<(...args: any[]) => any>;
|
|
75
|
+
seek: import("vitest").Mock<(...args: any[]) => any>;
|
|
76
|
+
seekPercentage: import("vitest").Mock<(...args: any[]) => any>;
|
|
77
|
+
getDuration: import("vitest").Mock<(...args: any[]) => any>;
|
|
78
|
+
enterPiP: import("vitest").Mock<(...args: any[]) => any>;
|
|
79
|
+
exitPiP: import("vitest").Mock<(...args: any[]) => any>;
|
|
80
|
+
getPlaybackType: import("vitest").Mock<(...args: any[]) => any>;
|
|
81
|
+
getStartTimeOffset: import("vitest").Mock<(...args: any[]) => any>;
|
|
82
|
+
getCurrentTime: import("vitest").Mock<(...args: any[]) => any>;
|
|
83
|
+
isHighDefinitionInUse: import("vitest").Mock<(...args: any[]) => any>;
|
|
84
|
+
mute: import("vitest").Mock<(...args: any[]) => any>;
|
|
85
|
+
unmute: import("vitest").Mock<(...args: any[]) => any>;
|
|
86
|
+
volume: import("vitest").Mock<(...args: any[]) => any>;
|
|
87
|
+
configure: import("vitest").Mock<(...args: any[]) => any>;
|
|
88
|
+
attemptAutoPlay: import("vitest").Mock<(...args: any[]) => any>;
|
|
89
|
+
canAutoPlay: import("vitest").Mock<(...args: any[]) => any>;
|
|
90
|
+
onResize: import("vitest").Mock<(...args: any[]) => any>;
|
|
91
|
+
setPlaybackRate: import("vitest").Mock<(...args: any[]) => any>;
|
|
92
|
+
trigger: <T extends string | symbol>(event: T, ...args: any[]) => boolean;
|
|
91
93
|
};
|
|
92
94
|
export declare function createMockContainer(playback?: any): Events<string | symbol, any> & {
|
|
93
95
|
el: HTMLDivElement;
|
|
@@ -95,6 +97,8 @@ export declare function createMockContainer(playback?: any): Events<string | sym
|
|
|
95
97
|
$el: any;
|
|
96
98
|
getDuration: import("vitest").Mock<(...args: any[]) => any>;
|
|
97
99
|
getPlugin: import("vitest").Mock<(...args: any[]) => any>;
|
|
100
|
+
getPlaybackType: import("vitest").Mock<(...args: any[]) => any>;
|
|
101
|
+
isDvrInUse: import("vitest").Mock<(...args: any[]) => any>;
|
|
98
102
|
isPlaying: import("vitest").Mock<(...args: any[]) => any>;
|
|
99
103
|
play: import("vitest").Mock<(...args: any[]) => any>;
|
|
100
104
|
seek: import("vitest").Mock<(...args: any[]) => any>;
|
package/lib/testUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,MAAM,MAAM,eAAe,CAAA;AAElC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,MAAM;IAErC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAA2B;;;;;;;;;;;;;;;;EAqBvC;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS
|
|
1
|
+
{"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,MAAM,MAAM,eAAe,CAAA;AAElC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,MAAM;IAErC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAA2B;;;;;;;;;;;;;;;;EAqBvC;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B/C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,GAA0B;;;;;;;;;;;;EAgBvE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAqB/C"}
|