@vidispine/vdt-videojs 21.3.0 → 22.1.0-pre.1
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 +4 -4
- package/deprecated-app/README.md +21 -0
- package/{demo.html → deprecated-app/index.html} +1 -1
- package/{demo.js → deprecated-app/index.js} +69 -41
- package/{samples → deprecated-app/samples}/birds-es.vvt +0 -0
- package/{samples → deprecated-app/samples}/birds.vvt +0 -0
- package/{tests.js → deprecated-app/tests.js} +87 -54
- package/{lib/style.css → dist/index.css} +755 -55
- package/dist/index.es.js +2099 -0
- package/dist/index.js +2112 -0
- package/dist/index.umd.js +2115 -0
- package/package.json +29 -85
- package/rollup.config.js +54 -0
- package/src/constants.js +1 -1
- package/src/externalControls.js +14 -18
- package/src/helpOverlay.js +12 -16
- package/src/helpers.js +3 -3
- package/src/index.js +3 -9
- package/src/index.stories.mdx +59 -0
- package/src/menu.api.stories.mdx +59 -0
- package/src/menu.js +23 -19
- package/src/{menu.example.md → menu.usage.stories.mdx} +31 -21
- package/src/osd.js +24 -18
- package/src/player.api.stories.mdx +120 -0
- package/src/player.js +157 -108
- package/src/player.scss +2 -0
- package/src/{player.example.md → player.usage.stories.mdx} +18 -8
- package/src/seekBar.js +85 -48
- package/src/timeCodeDisplay.js +39 -31
- package/src/videoTime.js +4 -4
- package/build.js +0 -233
- package/lib/index.cjs.js +0 -3240
- package/lib/index.esm.js +0 -3227
- package/lib/index.umd.js +0 -3243
- package/lib/package.json +0 -13
- package/lib/src/constants.js +0 -51
- package/lib/src/externalControls.js +0 -307
- package/lib/src/helpOverlay.js +0 -106
- package/lib/src/helpers.js +0 -34
- package/lib/src/index.js +0 -13
- package/lib/src/menu.js +0 -365
- package/lib/src/osd.js +0 -91
- package/lib/src/player.js +0 -943
- package/lib/src/seekBar.js +0 -833
- package/lib/src/timeCodeDisplay.js +0 -253
- package/lib/src/videoTime.js +0 -66
- package/log.js +0 -24
- package/src/_index.stories.js +0 -24
- package/src/externalControls.stories.js +0 -6
- package/src/helpOverlay.stories.js +0 -6
- package/src/menu.classMethods.md +0 -47
- package/src/player.classMethods.md +0 -108
- package/src/player.stories.js +0 -14
- package/src/seekBar.stories.js +0 -8
- package/tmp/player.css +0 -1
package/src/player.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
/* eslint-disable no-param-reassign */
|
|
2
|
-
/* eslint-disable no-case-declarations */
|
|
3
1
|
/* eslint-disable no-underscore-dangle */
|
|
4
|
-
/* eslint-disable no-unused-expressions */
|
|
5
|
-
/* eslint-disable max-classes-per-file */
|
|
6
|
-
/* eslint-disable no-plusplus */
|
|
7
|
-
/* eslint-disable prefer-rest-params */
|
|
8
2
|
/* eslint-disable import/no-named-as-default-member */
|
|
9
|
-
|
|
10
3
|
/*
|
|
11
4
|
Eslint rules disabled for lines like these:
|
|
12
5
|
|
|
@@ -14,20 +7,21 @@ this.props.target && this.props.target.appendChild(this.videoEl); //validity che
|
|
|
14
7
|
document.getElementsByClassName('vjs-volume-panel')[0] //rule asks for destructuring
|
|
15
8
|
this.videojs.hasStarted_ //dangling underscore produced by video.js in this case
|
|
16
9
|
*/
|
|
17
|
-
|
|
10
|
+
// eslint-disable-next-line max-classes-per-file
|
|
18
11
|
import mousetrap from 'mousetrap';
|
|
12
|
+
import videojs from 'video.js/dist/alt/video.core.min';
|
|
19
13
|
|
|
20
14
|
import constants from './constants';
|
|
21
15
|
import { reactiveSetters } from './helpers';
|
|
22
|
-
|
|
23
|
-
import OSD from './osd';
|
|
24
|
-
import TimeCodeDisplay from './timeCodeDisplay';
|
|
25
16
|
import HelpOverlay from './helpOverlay';
|
|
26
17
|
import Menu from './menu';
|
|
18
|
+
import OSD from './osd';
|
|
27
19
|
import SeekBar from './seekBar';
|
|
28
|
-
|
|
20
|
+
import TimeCodeDisplay from './timeCodeDisplay';
|
|
29
21
|
import VideoTime from './videoTime';
|
|
30
22
|
|
|
23
|
+
import './player.scss';
|
|
24
|
+
|
|
31
25
|
const correctMimeTypes = {
|
|
32
26
|
'audio/x-aac': 'audio/aac',
|
|
33
27
|
};
|
|
@@ -38,11 +32,12 @@ export default class Player {
|
|
|
38
32
|
this.props = props; // save props
|
|
39
33
|
reactiveSetters.apply(this); // ability to handle new props via setters
|
|
40
34
|
|
|
41
|
-
const classNames = [
|
|
35
|
+
const classNames = [
|
|
36
|
+
// add conditional css classes here for the whole container
|
|
42
37
|
'video-js',
|
|
43
38
|
'vjs-big-play-centered',
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
this.props.posterUrl ? constants.classNames.poster : '',
|
|
40
|
+
this.props.subtitles && this.props.subtitles.length > 0 ? constants.classNames.subtitles : '',
|
|
46
41
|
'init', // make player height 0 while no metadata for video size available with css
|
|
47
42
|
];
|
|
48
43
|
|
|
@@ -70,7 +65,7 @@ export default class Player {
|
|
|
70
65
|
if (this.props.sources) {
|
|
71
66
|
// sort mp4 at the top
|
|
72
67
|
if (this.props.sources.length > 1) {
|
|
73
|
-
this.props.sources.sort((a) => (
|
|
68
|
+
this.props.sources.sort((a) => (a.type.indexOf('mp4') > 0 ? 1 : -1));
|
|
74
69
|
}
|
|
75
70
|
|
|
76
71
|
// construct <source> els
|
|
@@ -80,21 +75,20 @@ export default class Player {
|
|
|
80
75
|
aSource.type = sanitizeMimeType(source.type);
|
|
81
76
|
this.videoEl.appendChild(aSource);
|
|
82
77
|
});
|
|
83
|
-
}// handle else with err later
|
|
78
|
+
} // handle else with err later
|
|
84
79
|
|
|
85
|
-
this.videoEl.onloadedmetadata = () => {
|
|
80
|
+
this.videoEl.onloadedmetadata = () => {
|
|
81
|
+
// read-only duration timecode
|
|
86
82
|
if (this.durationTimeCode) {
|
|
87
83
|
this.setDurationTimeCode();
|
|
88
84
|
}
|
|
89
85
|
|
|
90
|
-
this.videojs.el_.classList.remove('init');
|
|
86
|
+
if (this.videojs.el_) this.videojs.el_.classList.remove('init');
|
|
91
87
|
};
|
|
92
88
|
|
|
93
89
|
// if video has not loaded by now, show the player w/ errors
|
|
94
90
|
setTimeout(() => {
|
|
95
|
-
if (this.videojs.el_)
|
|
96
|
-
this.videojs.el_.classList.remove('init');
|
|
97
|
-
}
|
|
91
|
+
if (this.videojs.el_) this.videojs.el_.classList.remove('init');
|
|
98
92
|
}, 2000);
|
|
99
93
|
|
|
100
94
|
// attach subtitle tracks (these should come in last in the <video /> node)
|
|
@@ -117,14 +111,13 @@ export default class Player {
|
|
|
117
111
|
}
|
|
118
112
|
|
|
119
113
|
// attach to dom and construct videojs player
|
|
120
|
-
if (this.props.target)
|
|
121
|
-
this.props.target && this.props.target.appendChild(this.videoEl);
|
|
122
|
-
}
|
|
114
|
+
if (this.props.target) this.props.target.appendChild(this.videoEl);
|
|
123
115
|
|
|
124
|
-
const showDefaultTimeDisplay =
|
|
125
|
-
const controlsEnabled =
|
|
116
|
+
const showDefaultTimeDisplay = this.props.timecode !== true;
|
|
117
|
+
const controlsEnabled = this.props.controls !== false;
|
|
126
118
|
const options = this.props.options || {};
|
|
127
119
|
|
|
120
|
+
// eslint-disable-next-line new-cap
|
|
128
121
|
this.videojs = new videojs('vdt-video', {
|
|
129
122
|
responsive: true,
|
|
130
123
|
fluid: true,
|
|
@@ -136,7 +129,9 @@ export default class Player {
|
|
|
136
129
|
durationDisplay: showDefaultTimeDisplay,
|
|
137
130
|
currentTimeDisplay: showDefaultTimeDisplay,
|
|
138
131
|
},
|
|
139
|
-
inactivityTimeout:
|
|
132
|
+
inactivityTimeout: this.audioOnly
|
|
133
|
+
? constants.defaults.audioInactivityTimeout
|
|
134
|
+
: constants.defaults.inactivityTimeout,
|
|
140
135
|
preload: 'auto',
|
|
141
136
|
captionsButton: false,
|
|
142
137
|
subsCapsButton: false,
|
|
@@ -155,7 +150,7 @@ export default class Player {
|
|
|
155
150
|
});
|
|
156
151
|
|
|
157
152
|
// set the poster
|
|
158
|
-
this.props.posterUrl
|
|
153
|
+
if (this.props.posterUrl) this.videojs.poster(this.props.posterUrl);
|
|
159
154
|
|
|
160
155
|
// set other props that are passed
|
|
161
156
|
this.handleNewProps(this.props);
|
|
@@ -164,7 +159,7 @@ export default class Player {
|
|
|
164
159
|
this.videojs.ready(() => {
|
|
165
160
|
this.bindHotkeys();
|
|
166
161
|
this.bindEvents();
|
|
167
|
-
this.props.onReady
|
|
162
|
+
if (this.props.onReady) this.props.onReady(this.videojs);
|
|
168
163
|
|
|
169
164
|
this.restoreSettings();
|
|
170
165
|
this.setupTimeCode();
|
|
@@ -193,14 +188,14 @@ export default class Player {
|
|
|
193
188
|
// show & hide markers when controls hide
|
|
194
189
|
this.videojs.on('userinactive', () => {
|
|
195
190
|
if (!this.controlsBelowPlayer) {
|
|
196
|
-
this.seekBar
|
|
191
|
+
if (this.seekBar) this.seekBar.hideMarkers?.();
|
|
197
192
|
} else {
|
|
198
|
-
this.seekBar
|
|
193
|
+
this.seekBar?.showMarkers?.();
|
|
199
194
|
}
|
|
200
195
|
});
|
|
201
196
|
|
|
202
197
|
this.videojs.on('useractive', () => {
|
|
203
|
-
this.seekBar
|
|
198
|
+
if (this.seekBar) this.seekBar.showMarkers();
|
|
204
199
|
});
|
|
205
200
|
}
|
|
206
201
|
}
|
|
@@ -230,10 +225,13 @@ export default class Player {
|
|
|
230
225
|
},
|
|
231
226
|
};
|
|
232
227
|
|
|
233
|
-
if (externalTimecodeInstance !== null) {
|
|
228
|
+
if (externalTimecodeInstance !== null) {
|
|
229
|
+
// setting up an external playback timecode
|
|
230
|
+
// eslint-disable-next-line no-param-reassign
|
|
234
231
|
externalTimecodeInstance.props = Object.assign(externalTimecodeInstance.props, props);
|
|
235
232
|
this.externalTimecodes.playback.push(externalTimecodeInstance);
|
|
236
|
-
} else if (this.props.timecode === true) {
|
|
233
|
+
} else if (this.props.timecode === true) {
|
|
234
|
+
// player uses timecode
|
|
237
235
|
// construct an in-frame timecode, if controls are enabled
|
|
238
236
|
if (this.props.controls !== false) {
|
|
239
237
|
const target = document.getElementsByClassName('vjs-volume-panel')[0];
|
|
@@ -260,11 +258,12 @@ export default class Player {
|
|
|
260
258
|
}
|
|
261
259
|
}
|
|
262
260
|
|
|
263
|
-
updateTimeCode() {
|
|
261
|
+
updateTimeCode() {
|
|
262
|
+
// HH:MM:SS (the usual)
|
|
264
263
|
const currentSecond = this.videojs.currentTime();
|
|
265
264
|
const props = this.timeConverter.secondsFromStartToSMPTE(currentSecond);
|
|
266
265
|
|
|
267
|
-
this.timeCode
|
|
266
|
+
if (this.timeCode) this.timeCode.update(props);
|
|
268
267
|
|
|
269
268
|
if (this.externalTimecodes.playback.length > 0) {
|
|
270
269
|
this.externalTimecodes.playback.forEach((timecode) => {
|
|
@@ -273,10 +272,11 @@ export default class Player {
|
|
|
273
272
|
}
|
|
274
273
|
}
|
|
275
274
|
|
|
276
|
-
updateTimeCodeFrame() {
|
|
275
|
+
updateTimeCodeFrame() {
|
|
276
|
+
// update the frame in this second -> 00:00:00:XX
|
|
277
277
|
const frame = this.timeConverter.currentFrame(this.videojs.currentTime()); // -> :XX
|
|
278
278
|
|
|
279
|
-
this.timeCode
|
|
279
|
+
if (this.timeCode) this.timeCode.update({ frame }); // TODO - figure out a way to drop frames
|
|
280
280
|
|
|
281
281
|
if (this.externalTimecodes.playback.length > 0) {
|
|
282
282
|
this.externalTimecodes.playback.forEach((timecode) => {
|
|
@@ -286,11 +286,14 @@ export default class Player {
|
|
|
286
286
|
|
|
287
287
|
// recursive invocation if still playing
|
|
288
288
|
if (!this.videojs.paused()) {
|
|
289
|
-
this.rafID = requestAnimationFrame(() => {
|
|
289
|
+
this.rafID = requestAnimationFrame(() => {
|
|
290
|
+
this.updateTimeCodeFrame();
|
|
291
|
+
});
|
|
290
292
|
}
|
|
291
293
|
}
|
|
292
294
|
|
|
293
|
-
setDurationTimeCode() {
|
|
295
|
+
setDurationTimeCode() {
|
|
296
|
+
// happens once
|
|
294
297
|
const durationInSeconds = this.videojs.duration();
|
|
295
298
|
const props = this.timeConverter.secondsFromStartToSMPTE(durationInSeconds);
|
|
296
299
|
props.frame = this.timeConverter.secondsToFrame(durationInSeconds);
|
|
@@ -299,26 +302,27 @@ export default class Player {
|
|
|
299
302
|
this.durationTimeCode.container.classList.add('duration');
|
|
300
303
|
}
|
|
301
304
|
|
|
302
|
-
bindHotkeys() {
|
|
305
|
+
bindHotkeys() {
|
|
306
|
+
// keyboard shortcuts
|
|
303
307
|
this.bindings = [
|
|
304
308
|
{
|
|
305
309
|
key: ['space', 'k'],
|
|
306
310
|
method: (e) => {
|
|
307
311
|
// prevents page scroll on space, just like YT
|
|
308
|
-
(e.preventDefault)
|
|
312
|
+
if (e.preventDefault) e.preventDefault();
|
|
309
313
|
this.togglePlayback();
|
|
310
314
|
},
|
|
311
315
|
},
|
|
312
316
|
{
|
|
313
317
|
key: 'j',
|
|
314
318
|
method: () => {
|
|
315
|
-
this.videojs.hasStarted_
|
|
319
|
+
if (this.videojs.hasStarted_) this.jumpBackward();
|
|
316
320
|
},
|
|
317
321
|
},
|
|
318
322
|
{
|
|
319
323
|
key: 'l',
|
|
320
324
|
method: () => {
|
|
321
|
-
this.videojs.hasStarted_
|
|
325
|
+
if (this.videojs.hasStarted_) this.jumpForward();
|
|
322
326
|
},
|
|
323
327
|
},
|
|
324
328
|
{
|
|
@@ -348,14 +352,14 @@ export default class Player {
|
|
|
348
352
|
{
|
|
349
353
|
key: 'up',
|
|
350
354
|
method: (e) => {
|
|
351
|
-
e.preventDefault
|
|
355
|
+
if (e.preventDefault) e.preventDefault(); // prevent page scroll
|
|
352
356
|
this.volumeUp();
|
|
353
357
|
},
|
|
354
358
|
},
|
|
355
359
|
{
|
|
356
360
|
key: 'down',
|
|
357
361
|
method: (e) => {
|
|
358
|
-
e.preventDefault
|
|
362
|
+
if (e.preventDefault) e.preventDefault(); // prevent page scroll
|
|
359
363
|
this.volumeDown();
|
|
360
364
|
},
|
|
361
365
|
},
|
|
@@ -368,7 +372,7 @@ export default class Player {
|
|
|
368
372
|
{
|
|
369
373
|
key: 'n',
|
|
370
374
|
method: () => {
|
|
371
|
-
this.menu
|
|
375
|
+
if (this.menu) this.menu.toggle();
|
|
372
376
|
},
|
|
373
377
|
},
|
|
374
378
|
];
|
|
@@ -392,22 +396,26 @@ export default class Player {
|
|
|
392
396
|
}
|
|
393
397
|
|
|
394
398
|
unbindHotKeys() {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
399
|
+
if (this.bindings)
|
|
400
|
+
this.bindings.forEach((binding) => {
|
|
401
|
+
mousetrap.unbind(binding.key);
|
|
402
|
+
});
|
|
398
403
|
}
|
|
399
404
|
|
|
400
405
|
bindEvents() {
|
|
401
|
-
this.videojs.on('volumechange', () => {
|
|
406
|
+
this.videojs.on('volumechange', () => {
|
|
407
|
+
// persist volume changes
|
|
402
408
|
this.persistSetting(constants.settings.volume, this.volume);
|
|
403
409
|
});
|
|
404
410
|
|
|
405
|
-
this.videojs.textTracks().on('change', () => {
|
|
411
|
+
this.videojs.textTracks().on('change', () => {
|
|
412
|
+
// determine subtitles state
|
|
406
413
|
const tracks = this.videojs.textTracks().tracks_;
|
|
407
|
-
const selected = tracks.filter((track) =>
|
|
414
|
+
const selected = tracks.filter((track) => track.mode === 'showing');
|
|
408
415
|
const areAnyTxtTracksActive = selected.length > 0; // -> true if any text tracks are showing
|
|
409
416
|
|
|
410
|
-
if (areAnyTxtTracksActive) {
|
|
417
|
+
if (areAnyTxtTracksActive) {
|
|
418
|
+
// update container with appropriate className
|
|
411
419
|
this.videojs.el_.classList.add(constants.classNames.subtitlesActive);
|
|
412
420
|
this.persistSetting(constants.settings.subs, true);
|
|
413
421
|
this.persistSetting(constants.settings.subLang, selected[0].language);
|
|
@@ -446,7 +454,8 @@ export default class Player {
|
|
|
446
454
|
}
|
|
447
455
|
}
|
|
448
456
|
|
|
449
|
-
seek(secs) {
|
|
457
|
+
seek(secs) {
|
|
458
|
+
// relative to current playback time
|
|
450
459
|
let time = this.videojs.currentTime() + secs;
|
|
451
460
|
|
|
452
461
|
if (time < 0) {
|
|
@@ -457,7 +466,7 @@ export default class Player {
|
|
|
457
466
|
}
|
|
458
467
|
|
|
459
468
|
seekTo(sec) {
|
|
460
|
-
!this.videojs.hasStarted()
|
|
469
|
+
if (!this.videojs.hasStarted()) this.videojs.hasStarted(true);
|
|
461
470
|
this.videojs.currentTime(sec);
|
|
462
471
|
}
|
|
463
472
|
|
|
@@ -492,9 +501,12 @@ export default class Player {
|
|
|
492
501
|
return frame; // we're on this frame, could be for example 23.45
|
|
493
502
|
}
|
|
494
503
|
|
|
495
|
-
doEsothericWork() {
|
|
496
|
-
|
|
497
|
-
|
|
504
|
+
doEsothericWork() {
|
|
505
|
+
// non critical parts, that are nice to have
|
|
506
|
+
if (this.props.color) {
|
|
507
|
+
// change the seekbar color from props
|
|
508
|
+
document.getElementsByClassName('vjs-play-progress')[0].style.backgroundColor =
|
|
509
|
+
this.props.color;
|
|
498
510
|
}
|
|
499
511
|
|
|
500
512
|
// feedback gizmo, mainly for feedback on hotkey-presses
|
|
@@ -548,7 +560,8 @@ export default class Player {
|
|
|
548
560
|
});
|
|
549
561
|
}
|
|
550
562
|
|
|
551
|
-
restoreSettings() {
|
|
563
|
+
restoreSettings() {
|
|
564
|
+
// restore settings, like volume level or language track
|
|
552
565
|
// get values from storage
|
|
553
566
|
const volume = localStorage.getItem(constants.settings.volume);
|
|
554
567
|
const subsEnabled = localStorage.getItem(constants.settings.subs);
|
|
@@ -557,15 +570,16 @@ export default class Player {
|
|
|
557
570
|
// values vs default behaviours in a neatly readable map
|
|
558
571
|
this.settings = {
|
|
559
572
|
// avoid .99999999 and other float pecularities in js
|
|
560
|
-
[constants.settings.volume]:
|
|
561
|
-
[constants.settings.subLang]:
|
|
562
|
-
[constants.settings.subs]:
|
|
573
|
+
[constants.settings.volume]: volume !== null ? Math.round(volume * 100) / 100 : 1,
|
|
574
|
+
[constants.settings.subLang]: subLang !== null ? subLang : 'en',
|
|
575
|
+
[constants.settings.subs]: subsEnabled !== null ? JSON.parse(subsEnabled) : false,
|
|
563
576
|
};
|
|
564
577
|
|
|
565
578
|
// apply values (settings)
|
|
566
579
|
this.volume = this.settings[constants.settings.volume]; // volume
|
|
567
580
|
|
|
568
|
-
if (this.settings[constants.settings.subs]) {
|
|
581
|
+
if (this.settings[constants.settings.subs]) {
|
|
582
|
+
// subtitles
|
|
569
583
|
this.enableSubs();
|
|
570
584
|
}
|
|
571
585
|
}
|
|
@@ -578,7 +592,7 @@ export default class Player {
|
|
|
578
592
|
enableSubs() {
|
|
579
593
|
const subs = this.videojs.textTracks();
|
|
580
594
|
|
|
581
|
-
for (let i = 0; i < subs.length; i
|
|
595
|
+
for (let i = 0; i < subs.length; i += 1) {
|
|
582
596
|
const sub = subs[i];
|
|
583
597
|
if (sub.language === this.settings[constants.settings.subLang]) {
|
|
584
598
|
sub.mode = 'showing';
|
|
@@ -591,15 +605,19 @@ export default class Player {
|
|
|
591
605
|
disableSubs() {
|
|
592
606
|
const subs = this.videojs.textTracks();
|
|
593
607
|
|
|
594
|
-
for (let i = 0; i < subs.length; i
|
|
608
|
+
for (let i = 0; i < subs.length; i += 1) {
|
|
595
609
|
const sub = subs[i];
|
|
596
610
|
sub.mode = 'hidden';
|
|
597
611
|
}
|
|
598
612
|
}
|
|
599
613
|
|
|
600
614
|
toggleSubs() {
|
|
601
|
-
if (this.videojs.textTracks().length > 0) {
|
|
602
|
-
|
|
615
|
+
if (this.videojs.textTracks().length > 0) {
|
|
616
|
+
// check if subtitles exist
|
|
617
|
+
if (
|
|
618
|
+
this.videojs.textTracks().tracks_.filter((track) => track.mode === 'showing').length > 0
|
|
619
|
+
) {
|
|
620
|
+
// a certain subtitle track is enabled
|
|
603
621
|
this.disableSubs();
|
|
604
622
|
} else {
|
|
605
623
|
this.enableSubs();
|
|
@@ -620,12 +638,15 @@ export default class Player {
|
|
|
620
638
|
const CustomButton = videojs.extend(Button, {
|
|
621
639
|
/* eslint-disable func-names, object-shorthand */
|
|
622
640
|
constructor: function () {
|
|
641
|
+
// eslint-disable-next-line prefer-rest-params
|
|
623
642
|
Button.apply(this, arguments);
|
|
624
|
-
options.className
|
|
625
|
-
if (options.title) {
|
|
643
|
+
if (options.className) this.el_.classList.add(options.className);
|
|
644
|
+
if (options.title) {
|
|
645
|
+
this.el_.title = options.title;
|
|
646
|
+
}
|
|
626
647
|
},
|
|
627
648
|
handleClick: () => {
|
|
628
|
-
options.onClick
|
|
649
|
+
if (options.onClick) options.onClick();
|
|
629
650
|
},
|
|
630
651
|
});
|
|
631
652
|
|
|
@@ -650,7 +671,8 @@ export default class Player {
|
|
|
650
671
|
setupMenu() {
|
|
651
672
|
const menuItems = this.props.menuItems || [];
|
|
652
673
|
const sourcesMenuItems = [];
|
|
653
|
-
if (this.props.sources.length > 1) {
|
|
674
|
+
if (this.props.sources.length > 1) {
|
|
675
|
+
// show only if more than 1 source
|
|
654
676
|
this.props.sources.forEach((source) => {
|
|
655
677
|
if (source.label) {
|
|
656
678
|
sourcesMenuItems.push({
|
|
@@ -665,8 +687,12 @@ export default class Player {
|
|
|
665
687
|
menuItems.push({
|
|
666
688
|
title: 'Sources',
|
|
667
689
|
getActiveIndex: () => {
|
|
668
|
-
const matchingSource = this.props.sources.find(
|
|
669
|
-
|
|
690
|
+
const matchingSource = this.props.sources.find(
|
|
691
|
+
(source) => source.src === this.videojs.src(),
|
|
692
|
+
);
|
|
693
|
+
return sourcesMenuItems.indexOf(
|
|
694
|
+
sourcesMenuItems.find((menuItem) => menuItem.title === matchingSource.label),
|
|
695
|
+
);
|
|
670
696
|
},
|
|
671
697
|
items: sourcesMenuItems,
|
|
672
698
|
});
|
|
@@ -675,12 +701,14 @@ export default class Player {
|
|
|
675
701
|
// subtitles
|
|
676
702
|
if (this.props.subtitles && this.props.subtitles.length > 0) {
|
|
677
703
|
// off option for subs
|
|
678
|
-
const subtitleMenuItems = [
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
704
|
+
const subtitleMenuItems = [
|
|
705
|
+
{
|
|
706
|
+
title: 'Off',
|
|
707
|
+
onClick: () => {
|
|
708
|
+
this.disableSubs();
|
|
709
|
+
},
|
|
682
710
|
},
|
|
683
|
-
|
|
711
|
+
];
|
|
684
712
|
|
|
685
713
|
// quick toggle in the control bar
|
|
686
714
|
// this.addButtonToControlBar({
|
|
@@ -697,7 +725,7 @@ export default class Player {
|
|
|
697
725
|
title: subtitle.label,
|
|
698
726
|
onClick: () => {
|
|
699
727
|
const subs = this.videojs.textTracks();
|
|
700
|
-
for (let i = 0; i < subs.length; i
|
|
728
|
+
for (let i = 0; i < subs.length; i += 1) {
|
|
701
729
|
const sub = subs[i];
|
|
702
730
|
if (i === index) {
|
|
703
731
|
sub.mode = 'showing';
|
|
@@ -709,23 +737,21 @@ export default class Player {
|
|
|
709
737
|
});
|
|
710
738
|
});
|
|
711
739
|
|
|
712
|
-
menuItems.push(
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
return i + 1; // offset "off" option
|
|
721
|
-
}
|
|
740
|
+
menuItems.push({
|
|
741
|
+
title: 'Subtitles',
|
|
742
|
+
getActiveIndex: () => {
|
|
743
|
+
const subs = this.videojs.textTracks();
|
|
744
|
+
for (let i = 0; i < subs.length; i += 1) {
|
|
745
|
+
const sub = subs[i];
|
|
746
|
+
if (sub.mode === 'showing') {
|
|
747
|
+
return i + 1; // offset "off" option
|
|
722
748
|
}
|
|
749
|
+
}
|
|
723
750
|
|
|
724
|
-
|
|
725
|
-
},
|
|
726
|
-
items: subtitleMenuItems,
|
|
751
|
+
return 0; // if none are showing, then probably "off"
|
|
727
752
|
},
|
|
728
|
-
|
|
753
|
+
items: subtitleMenuItems,
|
|
754
|
+
});
|
|
729
755
|
}
|
|
730
756
|
|
|
731
757
|
if (menuItems.length > 0) {
|
|
@@ -735,10 +761,10 @@ export default class Player {
|
|
|
735
761
|
target,
|
|
736
762
|
items: menuItems,
|
|
737
763
|
onOpen: () => {
|
|
738
|
-
!this.controlsBelowPlayer
|
|
764
|
+
if (!this.controlsBelowPlayer) this.hideControls();
|
|
739
765
|
},
|
|
740
766
|
onClose: () => {
|
|
741
|
-
!this.controlsBelowPlayer
|
|
767
|
+
if (!this.controlsBelowPlayer) this.videojs.controls(true);
|
|
742
768
|
},
|
|
743
769
|
});
|
|
744
770
|
|
|
@@ -755,7 +781,6 @@ export default class Player {
|
|
|
755
781
|
});
|
|
756
782
|
} else {
|
|
757
783
|
// no menu items, no menu
|
|
758
|
-
|
|
759
784
|
}
|
|
760
785
|
}
|
|
761
786
|
|
|
@@ -811,25 +836,47 @@ export default class Player {
|
|
|
811
836
|
}
|
|
812
837
|
|
|
813
838
|
pause() {
|
|
814
|
-
!this.videojs.paused()
|
|
839
|
+
if (!this.videojs.paused()) this.videojs.pause();
|
|
815
840
|
}
|
|
816
841
|
|
|
817
842
|
togglePlayback() {
|
|
818
843
|
if (this.videojs.paused()) {
|
|
819
844
|
this.play();
|
|
820
|
-
this.videojs.hasStarted_
|
|
845
|
+
if (this.videojs.hasStarted_) this.osd.feedback(constants.osd.play);
|
|
821
846
|
} else {
|
|
822
847
|
this.pause();
|
|
823
848
|
this.osd.feedback(constants.osd.pause);
|
|
824
849
|
}
|
|
825
850
|
}
|
|
826
851
|
|
|
827
|
-
|
|
852
|
+
// TODO: Check that this works, I took what was in timeCodeDisplay.js
|
|
853
|
+
updateSmpte(valuesObj) {
|
|
854
|
+
// Object.keys and array.forEach are too slow, classic for is faster
|
|
855
|
+
const startCount = this.nodeNames.length - 1;
|
|
856
|
+
let i;
|
|
857
|
+
for (i = startCount; i > -1; i -= 1) {
|
|
858
|
+
// updates follow frame -> seconds -> minutes -> hours order
|
|
859
|
+
const key = this.nodeNames[i];
|
|
860
|
+
|
|
861
|
+
if (
|
|
862
|
+
typeof valuesObj[key] !== 'undefined' &&
|
|
863
|
+
valuesObj[key] !== parseInt(this.nodes[key].value, 10) &&
|
|
864
|
+
key !== this.state.selectedNode
|
|
865
|
+
) {
|
|
866
|
+
// re-render node if value is defineed and is different from current value
|
|
867
|
+
const str = valuesObj[key].toString();
|
|
868
|
+
this.nodes[key].value = this.toDigitPairStr(str);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
destroy() {
|
|
874
|
+
// clean up
|
|
828
875
|
this.videojs.paused(true);
|
|
829
|
-
this.rafID
|
|
876
|
+
if (this.rafID) cancelAnimationFrame(this.rafID);
|
|
830
877
|
this.videojs.dispose();
|
|
831
878
|
this.unbindHotKeys();
|
|
832
|
-
this.seekBar
|
|
879
|
+
if (this.seekBar) this.seekBar.destroy();
|
|
833
880
|
}
|
|
834
881
|
|
|
835
882
|
get container() {
|
|
@@ -873,15 +920,16 @@ export default class Player {
|
|
|
873
920
|
return this.timeConverter.secondsFromStartToSMPTE(currentSecond);
|
|
874
921
|
}
|
|
875
922
|
|
|
876
|
-
set smpte(
|
|
877
|
-
|
|
878
|
-
|
|
923
|
+
set smpte(o) {
|
|
924
|
+
// TODO: Verify that this works.
|
|
925
|
+
// Prior to this it used the getter as setter here...
|
|
926
|
+
this.updateSmpte(o);
|
|
879
927
|
}
|
|
880
928
|
|
|
881
929
|
set src({ src, type = 'video/mp4', label = null }) {
|
|
882
930
|
const currentTime = this.videojs.currentTime();
|
|
883
931
|
// iterate trough sources, find existing matches
|
|
884
|
-
const source = this.props.sources.find((aSource) =>
|
|
932
|
+
const source = this.props.sources.find((aSource) => aSource.src === src);
|
|
885
933
|
// if no match try adding source
|
|
886
934
|
|
|
887
935
|
const onReady = () => {
|
|
@@ -902,7 +950,7 @@ export default class Player {
|
|
|
902
950
|
});
|
|
903
951
|
};
|
|
904
952
|
|
|
905
|
-
if (source &&
|
|
953
|
+
if (source && Array.isArray(source) && source.length > 0) {
|
|
906
954
|
// TODO - if(source.length > 1) - try one by one until a successful load happens
|
|
907
955
|
this.videojs.addClass('vjs-waiting');
|
|
908
956
|
this.videojs.src({
|
|
@@ -931,6 +979,7 @@ export default class Player {
|
|
|
931
979
|
return this.props.sources[index];
|
|
932
980
|
}
|
|
933
981
|
|
|
982
|
+
// eslint-disable-next-line class-methods-use-this, no-unused-vars
|
|
934
983
|
removeSrc(url) {
|
|
935
984
|
// should remove all url matching sources
|
|
936
985
|
}
|
package/src/player.scss
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { Meta } from '@storybook/addon-docs';
|
|
2
|
+
|
|
3
|
+
<Meta
|
|
4
|
+
title="vdt-videojs/Player/Example usage"
|
|
5
|
+
parameters={{
|
|
6
|
+
viewMode: 'docs',
|
|
7
|
+
previewTabs: {
|
|
8
|
+
canvas: { hidden: true },
|
|
9
|
+
},
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
## Example Usage
|
|
2
14
|
|
|
3
15
|
`import { Player } from '@vidispine/vdt-videojs'`
|
|
4
16
|
|
|
@@ -7,12 +19,12 @@ Here's an example how to use the Player class in a pure js way in your project:
|
|
|
7
19
|
```
|
|
8
20
|
const playerReference = new Player({
|
|
9
21
|
target: document.getElementById('player-node'), //HTML mount point
|
|
10
|
-
|
|
22
|
+
|
|
11
23
|
sources: [
|
|
12
24
|
{ src: 'https://some.domain/video-file.mp4', type: 'video/mp4'},
|
|
13
25
|
{ src: 'https://some.domain/video-file.webm', type: 'video/webm'}
|
|
14
26
|
],
|
|
15
|
-
|
|
27
|
+
|
|
16
28
|
posterUrl: 'https://some.domain/poster-image.jpg', //optional
|
|
17
29
|
color: '#e84393', //optional - replaces primary color
|
|
18
30
|
|
|
@@ -34,14 +46,14 @@ const playerReference = new Player({
|
|
|
34
46
|
label: 'Danish'
|
|
35
47
|
}
|
|
36
48
|
],
|
|
37
|
-
|
|
49
|
+
|
|
38
50
|
timecode: false, //optional - replaces time display with SMPTE timecode
|
|
39
51
|
frameRate: (24000/1001), //optional - use together with timecode option
|
|
40
|
-
|
|
52
|
+
|
|
41
53
|
onReady: ()=>{ //optional
|
|
42
54
|
// invoke methods after player has done initializing
|
|
43
55
|
},
|
|
44
|
-
|
|
56
|
+
|
|
45
57
|
/*
|
|
46
58
|
optional - will add a screenshot button to player
|
|
47
59
|
control bar and invoke method callback on click with player's
|
|
@@ -53,5 +65,3 @@ const playerReference = new Player({
|
|
|
53
65
|
})
|
|
54
66
|
|
|
55
67
|
```
|
|
56
|
-
|
|
57
|
-
See the [vdt-videojs-react](/?path=/story/vdt-videojs-react-videoplayer--basic) package for use with React.
|