@gcorevideo/player 2.22.31 → 2.23.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/assets/thumbnails/scrub-thumbnails.ejs +5 -10
- package/assets/thumbnails/style.scss +4 -5
- package/dist/core.js +1 -1
- package/dist/index.css +1178 -1176
- package/dist/index.js +206 -281
- package/lib/plugins/clips/Clips.d.ts +7 -0
- package/lib/plugins/clips/Clips.d.ts.map +1 -1
- package/lib/plugins/clips/Clips.js +8 -0
- package/lib/plugins/media-control/MediaControl.d.ts +1 -7
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +9 -18
- package/lib/plugins/thumbnails/Thumbnails.d.ts +36 -33
- package/lib/plugins/thumbnails/Thumbnails.d.ts.map +1 -1
- package/lib/plugins/thumbnails/Thumbnails.js +174 -260
- package/lib/plugins/thumbnails/utils.d.ts +5 -0
- package/lib/plugins/thumbnails/utils.d.ts.map +1 -0
- package/lib/plugins/thumbnails/utils.js +12 -0
- package/lib/testUtils.js +2 -2
- package/package.json +5 -1
- package/src/plugins/clips/Clips.ts +10 -1
- package/src/plugins/media-control/MediaControl.ts +10 -21
- package/src/plugins/thumbnails/Thumbnails.ts +236 -331
- package/src/plugins/thumbnails/__tests__/Thumbnails.test.ts +72 -0
- package/src/plugins/thumbnails/__tests__/__snapshots__/Thumbnails.test.ts.snap +10 -0
- package/src/plugins/thumbnails/utils.ts +12 -0
- package/src/testUtils.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -43303,7 +43303,7 @@ class Player {
|
|
|
43303
43303
|
}
|
|
43304
43304
|
}
|
|
43305
43305
|
|
|
43306
|
-
var version$1 = "2.
|
|
43306
|
+
var version$1 = "2.23.1";
|
|
43307
43307
|
|
|
43308
43308
|
var packages = {
|
|
43309
43309
|
"node_modules/@clappr/core": {
|
|
@@ -43727,8 +43727,8 @@ class MediaControl extends UICorePlugin {
|
|
|
43727
43727
|
return this.userDisabled || playbackIsNOOP;
|
|
43728
43728
|
}
|
|
43729
43729
|
/**
|
|
43730
|
+
* Use in mediacontrol-based plugins to access the active container
|
|
43730
43731
|
* @internal
|
|
43731
|
-
* @deprecated Use core.activeContainer directly
|
|
43732
43732
|
*/
|
|
43733
43733
|
get container() {
|
|
43734
43734
|
return this.core.activeContainer;
|
|
@@ -43995,16 +43995,16 @@ class MediaControl extends UICorePlugin {
|
|
|
43995
43995
|
this.applyButtonStyle(this.$playStopToggle);
|
|
43996
43996
|
}
|
|
43997
43997
|
mousemoveOnSeekBar(event) {
|
|
43998
|
+
const offset = MediaControl.getPageX(event) -
|
|
43999
|
+
(this.$seekBarContainer.offset().left ?? 0); // TODO check if the result can be negative
|
|
44000
|
+
const hoverOffset = offset -
|
|
44001
|
+
(this.$seekBarHover.width() ?? 0) / 2;
|
|
44002
|
+
const pos = offset ? Math.min(1, Math.max(offset / this.$seekBarContainer.width(), 0)) : 0;
|
|
43998
44003
|
if (this.settings.seekEnabled) {
|
|
43999
|
-
//
|
|
44000
|
-
|
|
44001
|
-
const offsetX = MediaControl.getPageX(event) -
|
|
44002
|
-
this.$seekBarContainer.offset().left -
|
|
44003
|
-
this.$seekBarHover.width() / 2;
|
|
44004
|
-
this.$seekBarHover.css({ left: offsetX });
|
|
44005
|
-
}
|
|
44004
|
+
// TODO test that it works when the element does not exist
|
|
44005
|
+
this.$seekBarHover.css({ left: hoverOffset });
|
|
44006
44006
|
}
|
|
44007
|
-
this.trigger(Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, event);
|
|
44007
|
+
this.trigger(Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, event, pos);
|
|
44008
44008
|
}
|
|
44009
44009
|
mouseleaveOnSeekBar(event) {
|
|
44010
44010
|
this.trigger(Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, event);
|
|
@@ -44428,17 +44428,8 @@ class MediaControl extends UICorePlugin {
|
|
|
44428
44428
|
else {
|
|
44429
44429
|
panel.append(element);
|
|
44430
44430
|
}
|
|
44431
|
-
return;
|
|
44432
44431
|
}
|
|
44433
44432
|
}
|
|
44434
|
-
/**
|
|
44435
|
-
* @deprecated Use {@link MediaControl.mount} instead
|
|
44436
|
-
* @param name
|
|
44437
|
-
* @param element
|
|
44438
|
-
*/
|
|
44439
|
-
putElement(name, element) {
|
|
44440
|
-
this.mount(name, element);
|
|
44441
|
-
}
|
|
44442
44433
|
/**
|
|
44443
44434
|
* Toggle the visibility of a media control element
|
|
44444
44435
|
* @param name - The name of the media control element
|
|
@@ -48377,6 +48368,14 @@ class Clips extends UICorePlugin {
|
|
|
48377
48368
|
this.render();
|
|
48378
48369
|
return super.enable();
|
|
48379
48370
|
}
|
|
48371
|
+
/**
|
|
48372
|
+
* Get the text of the clip at the given time
|
|
48373
|
+
* @param time - The time to get the text for
|
|
48374
|
+
* @returns The text of the clip at the given time
|
|
48375
|
+
*/
|
|
48376
|
+
getText(time) {
|
|
48377
|
+
return this.clips.find((clip) => clip.start <= time && clip.end >= time)?.text;
|
|
48378
|
+
}
|
|
48380
48379
|
onCoreReady() {
|
|
48381
48380
|
trace(`${T$d} onCoreReady`);
|
|
48382
48381
|
const mediaControl = this.core.getPlugin('media_control');
|
|
@@ -51967,12 +51966,31 @@ function requireParseSrt () {
|
|
|
51967
51966
|
var parseSrtExports = requireParseSrt();
|
|
51968
51967
|
const parseSRT = /*@__PURE__*/getDefaultExportFromCjs$1(parseSrtExports);
|
|
51969
51968
|
|
|
51970
|
-
const pluginHtml = "<div class=\"thumbnails-text\"></div>\n
|
|
51969
|
+
const pluginHtml = "<div class=\"thumbnails-text\" id=\"thumbnails-text\"></div>\n<div class=\"backdrop\" id=\"thumbnails-backdrop\">\n <div class=\"carousel\" id=\"thumbnails-carousel\"></div>\n</div>\n<div class=\"spotlight\" id=\"thumbnails-spotlight\"></div>\n";
|
|
51970
|
+
|
|
51971
|
+
function loadImageDimensions(url) {
|
|
51972
|
+
return new Promise((resolve, reject) => {
|
|
51973
|
+
const img = new Image();
|
|
51974
|
+
img.src = url;
|
|
51975
|
+
img.onload = () => {
|
|
51976
|
+
resolve({ width: img.width, height: img.height });
|
|
51977
|
+
};
|
|
51978
|
+
img.onerror = () => {
|
|
51979
|
+
reject(new Error('Failed to load image'));
|
|
51980
|
+
};
|
|
51981
|
+
});
|
|
51982
|
+
}
|
|
51971
51983
|
|
|
51972
51984
|
const T$1 = 'plugins.thumbnails';
|
|
51973
51985
|
/**
|
|
51974
51986
|
* `PLUGIN` that displays the thumbnails of the video when available.
|
|
51975
51987
|
* @beta
|
|
51988
|
+
* @remarks
|
|
51989
|
+
* The plugin needs specially crafted VTT file with a thumbnail sprite sheet to work.
|
|
51990
|
+
* The VTT consist of timestamp records followed by a thumbnail area
|
|
51991
|
+
*
|
|
51992
|
+
* Configuration options - {@link ThumbnailsPluginSettings}
|
|
51993
|
+
*
|
|
51976
51994
|
* @example
|
|
51977
51995
|
* ```ts
|
|
51978
51996
|
* import { Thumbnails } from '@gcorevideo/player'
|
|
@@ -51993,19 +52011,15 @@ const T$1 = 'plugins.thumbnails';
|
|
|
51993
52011
|
* ```
|
|
51994
52012
|
*/
|
|
51995
52013
|
class Thumbnails extends UICorePlugin {
|
|
51996
|
-
|
|
51997
|
-
_$backdrop = null;
|
|
51998
|
-
$container = null;
|
|
51999
|
-
$img = null;
|
|
52000
|
-
_$carousel = null;
|
|
52001
|
-
$textThumbnail = null;
|
|
52002
|
-
_$backdropCarouselImgs = [];
|
|
52014
|
+
$backdropCarouselImgs = [];
|
|
52003
52015
|
spriteSheetHeight = 0;
|
|
52004
|
-
|
|
52005
|
-
|
|
52006
|
-
|
|
52007
|
-
|
|
52008
|
-
|
|
52016
|
+
spriteSheetWidth = 0;
|
|
52017
|
+
hoverPosition = 0;
|
|
52018
|
+
showing = false;
|
|
52019
|
+
thumbsLoaded = false;
|
|
52020
|
+
spotlightHeight = 0;
|
|
52021
|
+
backdropHeight = 0;
|
|
52022
|
+
thumbs = [];
|
|
52009
52023
|
/**
|
|
52010
52024
|
* @internal
|
|
52011
52025
|
*/
|
|
@@ -52023,10 +52037,14 @@ class Thumbnails extends UICorePlugin {
|
|
|
52023
52037
|
*/
|
|
52024
52038
|
get attributes() {
|
|
52025
52039
|
return {
|
|
52026
|
-
class:
|
|
52040
|
+
class: 'scrub-thumbnails',
|
|
52027
52041
|
};
|
|
52028
52042
|
}
|
|
52029
52043
|
static template = tmpl(pluginHtml);
|
|
52044
|
+
constructor(core) {
|
|
52045
|
+
super(core);
|
|
52046
|
+
this.backdropHeight = this.options.thumbnails?.backdropHeight ?? 0;
|
|
52047
|
+
}
|
|
52030
52048
|
/*
|
|
52031
52049
|
* Helper to build the "thumbs" property for a sprite sheet.
|
|
52032
52050
|
*
|
|
@@ -52038,25 +52056,21 @@ class Thumbnails extends UICorePlugin {
|
|
|
52038
52056
|
* timeInterval- The interval (in seconds) between the thumbnails.
|
|
52039
52057
|
* startTime- The time (in seconds) that the first thumbnail represents. (defaults to 0)
|
|
52040
52058
|
*/
|
|
52041
|
-
|
|
52042
|
-
buildSpriteConfig(vtt, spriteSheetUrl) {
|
|
52059
|
+
buildSpriteConfig(vtt, baseUrl) {
|
|
52043
52060
|
const thumbs = [];
|
|
52044
|
-
// let coor: string[] = [];
|
|
52045
52061
|
for (const vt of vtt) {
|
|
52046
52062
|
const el = vt.text;
|
|
52047
|
-
// if (el && el.search(/\d*,\d*,\d*,\d*/g) > -1) {
|
|
52048
|
-
// el = el.match(/\d*,\d*,\d*,\d*/g)[0];
|
|
52049
|
-
// coor = el.split(',');
|
|
52050
|
-
// }
|
|
52051
52063
|
if (el) {
|
|
52052
|
-
const m = el.match(/xywh
|
|
52064
|
+
const m = el.match(/(\w+)#xywh=(\d+,\d+,\d+,\d+)/);
|
|
52053
52065
|
if (m) {
|
|
52054
|
-
const coor = m[
|
|
52066
|
+
const coor = m[2].split(',');
|
|
52055
52067
|
const w = parseInt(coor[2], 10);
|
|
52056
52068
|
const h = parseInt(coor[3], 10);
|
|
52057
52069
|
if (w > 0 && h > 0) {
|
|
52058
52070
|
thumbs.push({
|
|
52059
|
-
|
|
52071
|
+
// TODO handle relative URLs
|
|
52072
|
+
// url: new URL(m[0], baseUrl).toString(),
|
|
52073
|
+
url: baseUrl,
|
|
52060
52074
|
time: vt.start,
|
|
52061
52075
|
w,
|
|
52062
52076
|
h,
|
|
@@ -52069,242 +52083,147 @@ class Thumbnails extends UICorePlugin {
|
|
|
52069
52083
|
}
|
|
52070
52084
|
return thumbs;
|
|
52071
52085
|
}
|
|
52072
|
-
// TODO check if seek enabled
|
|
52073
52086
|
/**
|
|
52074
52087
|
* @internal
|
|
52075
52088
|
*/
|
|
52076
52089
|
bindEvents() {
|
|
52077
|
-
this.listenToOnce(this.core, Events$1.CORE_READY, this.
|
|
52078
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this._onMouseMove);
|
|
52079
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this._onMouseLeave);
|
|
52080
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_RENDERED, this._init);
|
|
52081
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, this._onMediaControlContainerChanged);
|
|
52090
|
+
this.listenToOnce(this.core, Events$1.CORE_READY, this.onCoreReady);
|
|
52082
52091
|
}
|
|
52083
|
-
|
|
52084
|
-
|
|
52085
|
-
this.stopListening(this._oldContainer, Events$1.CONTAINER_TIMEUPDATE, this._renderPlugin);
|
|
52086
|
-
}
|
|
52087
|
-
this._oldContainer = this.core.mediaControl.container;
|
|
52088
|
-
this.listenTo(this.core.mediaControl.container, Events$1.CONTAINER_TIMEUPDATE, this._renderPlugin);
|
|
52092
|
+
bindContainerEvents(container) {
|
|
52093
|
+
this.listenTo(container, Events$1.CONTAINER_TIMEUPDATE, this.update);
|
|
52089
52094
|
}
|
|
52090
|
-
|
|
52091
|
-
|
|
52092
|
-
|
|
52093
|
-
|
|
52094
|
-
|
|
52095
|
-
|
|
52096
|
-
|
|
52097
|
-
|
|
52098
|
-
}
|
|
52099
|
-
catch (error) {
|
|
52100
|
-
reportError(error);
|
|
52095
|
+
onCoreReady() {
|
|
52096
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52097
|
+
assert(mediaControl, `MediaControl is required for ${this.name} plugin to work`);
|
|
52098
|
+
if (!this.options.thumbnails ||
|
|
52099
|
+
!this.options.thumbnails.sprite ||
|
|
52100
|
+
!this.options.thumbnails.vtt) {
|
|
52101
|
+
trace(`${T$1} misconfigured: options.thumbnails.sprite and options.thumbnails.vtt are required`);
|
|
52102
|
+
this.destroy();
|
|
52101
52103
|
return;
|
|
52102
52104
|
}
|
|
52103
|
-
|
|
52104
|
-
|
|
52105
|
-
|
|
52106
|
-
|
|
52105
|
+
const { sprite: spriteSheet, vtt } = this.options.thumbnails;
|
|
52106
|
+
this.thumbs = this.buildSpriteConfig(parseSRT(vtt), spriteSheet);
|
|
52107
|
+
if (!this.thumbs.length) {
|
|
52108
|
+
trace(`${T$1} failed to parse the sprite sheet`);
|
|
52107
52109
|
this.destroy();
|
|
52108
52110
|
return;
|
|
52109
52111
|
}
|
|
52112
|
+
this.spotlightHeight = this.options.thumbnails?.spotlightHeight ?? 0;
|
|
52110
52113
|
this.loadSpriteSheet(spriteSheet).then(() => {
|
|
52111
|
-
this.
|
|
52112
|
-
this.
|
|
52113
|
-
|
|
52114
|
+
this.thumbsLoaded = true;
|
|
52115
|
+
this.spotlightHeight = this.spotlightHeight
|
|
52116
|
+
? Math.min(this.spotlightHeight, this.thumbs[0].h)
|
|
52117
|
+
: this.thumbs[0].h;
|
|
52118
|
+
this.init();
|
|
52114
52119
|
});
|
|
52120
|
+
this.listenTo(mediaControl, Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this.onMouseMoveSeekbar);
|
|
52121
|
+
this.listenTo(mediaControl, Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this.onMouseLeave);
|
|
52122
|
+
this.listenTo(mediaControl, Events$1.MEDIACONTROL_RENDERED, this.init);
|
|
52123
|
+
this.listenTo(mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, () => this.onContainerChanged(mediaControl.container));
|
|
52115
52124
|
}
|
|
52116
52125
|
async loadSpriteSheet(spriteSheetUrl) {
|
|
52117
|
-
return
|
|
52118
|
-
|
|
52119
|
-
|
|
52120
|
-
this.spriteSheetHeight = img.height;
|
|
52121
|
-
resolve();
|
|
52122
|
-
};
|
|
52123
|
-
img.onerror = reject;
|
|
52124
|
-
img.src = spriteSheetUrl;
|
|
52126
|
+
return loadImageDimensions(spriteSheetUrl).then(({ height, width }) => {
|
|
52127
|
+
this.spriteSheetHeight = height;
|
|
52128
|
+
this.spriteSheetWidth = width;
|
|
52125
52129
|
});
|
|
52126
52130
|
}
|
|
52127
|
-
|
|
52128
|
-
this.
|
|
52131
|
+
onContainerChanged(container) {
|
|
52132
|
+
this.bindContainerEvents(container);
|
|
52129
52133
|
}
|
|
52130
|
-
|
|
52131
|
-
if (!this.
|
|
52132
|
-
//
|
|
52134
|
+
init() {
|
|
52135
|
+
if (!this.thumbsLoaded) {
|
|
52136
|
+
// init() will be called when the thumbs are loaded,
|
|
52133
52137
|
// and whenever the media control rendered event is fired as just before this the dom elements get wiped in IE (https://github.com/tjenkinson/clappr-thumbnails-plugin/issues/5)
|
|
52134
52138
|
return;
|
|
52135
52139
|
}
|
|
52136
52140
|
// Init the backdropCarousel as array to keep reference of thumbnail images
|
|
52137
|
-
this
|
|
52138
|
-
|
|
52139
|
-
this.
|
|
52140
|
-
this.
|
|
52141
|
-
|
|
52142
|
-
|
|
52143
|
-
|
|
52144
|
-
|
|
52145
|
-
|
|
52146
|
-
|
|
52147
|
-
|
|
52148
|
-
|
|
52149
|
-
|
|
52150
|
-
|
|
52151
|
-
|
|
52152
|
-
|
|
52153
|
-
}
|
|
52154
|
-
|
|
52155
|
-
|
|
52156
|
-
|
|
52157
|
-
|
|
52158
|
-
// t2: typeof arguments[1],
|
|
52159
|
-
// });
|
|
52160
|
-
this._calculateHoverPosition(e);
|
|
52161
|
-
this._show = true;
|
|
52162
|
-
this._renderPlugin();
|
|
52163
|
-
}
|
|
52164
|
-
_onMouseLeave() {
|
|
52165
|
-
this._show = false;
|
|
52166
|
-
this._renderPlugin();
|
|
52167
|
-
}
|
|
52168
|
-
_calculateHoverPosition(e) {
|
|
52169
|
-
const offset = getPageX(e) - this.core.mediaControl.$seekBarContainer.offset().left;
|
|
52170
|
-
// proportion into the seek bar that the mouse is hovered over 0-1
|
|
52171
|
-
this._hoverPosition = Math.min(1, Math.max(offset / this.core.mediaControl.$seekBarContainer.width(), 0));
|
|
52172
|
-
}
|
|
52173
|
-
// private _buildThumbsFromOptions() {
|
|
52174
|
-
// const thumbs = this._thumbs;
|
|
52175
|
-
// const promises = thumbs.map((thumb) => {
|
|
52176
|
-
// return this._addThumbFromSrc(thumb);
|
|
52177
|
-
// });
|
|
52178
|
-
// return Promise.all(promises);
|
|
52179
|
-
// }
|
|
52180
|
-
// private _addThumbFromSrc(thumbSrc) {
|
|
52181
|
-
// return new Promise((resolve, reject) => {
|
|
52182
|
-
// const img = new Image();
|
|
52183
|
-
// img.onload = () => {
|
|
52184
|
-
// resolve(img);
|
|
52185
|
-
// };
|
|
52186
|
-
// img.onerror = reject;
|
|
52187
|
-
// img.src = thumbSrc.url;
|
|
52188
|
-
// }).then((img) => {
|
|
52189
|
-
// const startTime = thumbSrc.time;
|
|
52190
|
-
// // determine the thumb index
|
|
52191
|
-
// let index = null;
|
|
52192
|
-
// this._thumbs.some((thumb, i) => {
|
|
52193
|
-
// if (startTime < thumb.time) {
|
|
52194
|
-
// index = i;
|
|
52195
|
-
// return true;
|
|
52196
|
-
// }
|
|
52197
|
-
// return false;
|
|
52198
|
-
// });
|
|
52199
|
-
// if (index === null) {
|
|
52200
|
-
// index = this._thumbs.length;
|
|
52201
|
-
// }
|
|
52202
|
-
// const next = index < this._thumbs.length ? this._thumbs[index] : null;
|
|
52203
|
-
// const prev = index > 0 ? this._thumbs[index - 1] : null;
|
|
52204
|
-
// if (prev) {
|
|
52205
|
-
// // update the duration of the previous thumbnail
|
|
52206
|
-
// prev.duration = startTime - prev.time;
|
|
52207
|
-
// }
|
|
52208
|
-
// // the duration this thumb lasts for
|
|
52209
|
-
// // if it is the last thumb then duration will be null
|
|
52210
|
-
// const duration = next ? next.time - thumbSrc.time : null;
|
|
52211
|
-
// const imageW = img.width;
|
|
52212
|
-
// const imageH = img.height;
|
|
52213
|
-
// const thumb = {
|
|
52214
|
-
// imageW: imageW, // actual width of image
|
|
52215
|
-
// imageH: imageH, // actual height of image
|
|
52216
|
-
// x: thumbSrc.x || 0, // x coord in image of sprite
|
|
52217
|
-
// y: thumbSrc.y || 0, // y coord in image of sprite
|
|
52218
|
-
// w: thumbSrc.w || imageW, // width of sprite
|
|
52219
|
-
// h: thumbSrc.h || imageH, // height of sprite
|
|
52220
|
-
// url: thumbSrc.url,
|
|
52221
|
-
// time: startTime, // time this thumb represents
|
|
52222
|
-
// duration: duration, // how long (from time) this thumb represents
|
|
52223
|
-
// src: thumbSrc
|
|
52224
|
-
// };
|
|
52225
|
-
// this._thumbs.splice(index, 0, thumb);
|
|
52226
|
-
// return thumb;
|
|
52227
|
-
// });
|
|
52228
|
-
// }
|
|
52141
|
+
this.$backdropCarouselImgs = [];
|
|
52142
|
+
this.fixElements();
|
|
52143
|
+
this.loadBackdrop();
|
|
52144
|
+
this.update();
|
|
52145
|
+
}
|
|
52146
|
+
mount() {
|
|
52147
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52148
|
+
mediaControl.$el.find('.seek-time').css('bottom', 56); // TODO check the offset
|
|
52149
|
+
mediaControl.$el.first().after(this.$el);
|
|
52150
|
+
}
|
|
52151
|
+
onMouseMoveSeekbar(_, pos) {
|
|
52152
|
+
if (Math.abs(pos - this.hoverPosition) >= 0.01) {
|
|
52153
|
+
this.hoverPosition = pos;
|
|
52154
|
+
this.showing = true;
|
|
52155
|
+
this.update();
|
|
52156
|
+
}
|
|
52157
|
+
}
|
|
52158
|
+
onMouseLeave() {
|
|
52159
|
+
this.showing = false;
|
|
52160
|
+
this.update();
|
|
52161
|
+
}
|
|
52229
52162
|
// builds a dom element which represents the thumbnail
|
|
52230
|
-
// scaled to the
|
|
52231
|
-
|
|
52163
|
+
// scaled to the given height
|
|
52164
|
+
buildThumbImage(thumb, height, $ref) {
|
|
52232
52165
|
const scaleFactor = height / thumb.h;
|
|
52233
|
-
|
|
52234
|
-
|
|
52235
|
-
|
|
52236
|
-
|
|
52237
|
-
|
|
52238
|
-
|
|
52239
|
-
|
|
52240
|
-
}
|
|
52241
|
-
this.$container.css('width', thumb.w * scaleFactor);
|
|
52242
|
-
this.$container.css('height', height);
|
|
52243
|
-
this.$img.css({
|
|
52244
|
-
height: this.spriteSheetHeight * scaleFactor,
|
|
52245
|
-
left: -1 * thumb.x * scaleFactor,
|
|
52246
|
-
top: -1 * thumb.y * scaleFactor,
|
|
52166
|
+
const $container = $ref && $ref.length ? $ref : $('<div />').addClass('thumbnail-container');
|
|
52167
|
+
$container.css('width', thumb.w * scaleFactor);
|
|
52168
|
+
$container.css('height', height);
|
|
52169
|
+
$container.css({
|
|
52170
|
+
backgroundImage: `url(${thumb.url})`,
|
|
52171
|
+
backgroundSize: `${Math.floor(this.spriteSheetWidth * scaleFactor)}px ${Math.floor(this.spriteSheetHeight * scaleFactor)}px`,
|
|
52172
|
+
backgroundPosition: `-${Math.floor(thumb.x * scaleFactor)}px -${Math.floor(thumb.y * scaleFactor)}px`,
|
|
52247
52173
|
});
|
|
52248
|
-
|
|
52249
|
-
this.$container.append(this.$img);
|
|
52250
|
-
}
|
|
52251
|
-
return this.$container;
|
|
52174
|
+
return $container;
|
|
52252
52175
|
}
|
|
52253
|
-
|
|
52254
|
-
if (!this.
|
|
52176
|
+
loadBackdrop() {
|
|
52177
|
+
if (!this.backdropHeight) {
|
|
52255
52178
|
// disabled
|
|
52256
52179
|
return;
|
|
52257
52180
|
}
|
|
52258
52181
|
// append each of the thumbnails to the backdrop carousel
|
|
52259
|
-
const $carousel = this.
|
|
52260
|
-
for (const thumb of this.
|
|
52261
|
-
const $img = this.
|
|
52262
|
-
// Keep reference to thumbnail
|
|
52263
|
-
this
|
|
52182
|
+
const $carousel = this.$el.find('#thumbnails-carousel');
|
|
52183
|
+
for (const thumb of this.thumbs) {
|
|
52184
|
+
const $img = this.buildThumbImage(thumb, this.backdropHeight);
|
|
52185
|
+
// Keep reference to the thumbnail
|
|
52186
|
+
this.$backdropCarouselImgs.push($img);
|
|
52264
52187
|
// Add thumbnail to DOM
|
|
52265
52188
|
$carousel.append($img);
|
|
52266
52189
|
}
|
|
52267
52190
|
}
|
|
52268
52191
|
setText(time) {
|
|
52269
|
-
|
|
52270
|
-
|
|
52271
|
-
|
|
52192
|
+
const clips = this.core.getPlugin('clips');
|
|
52193
|
+
if (clips) {
|
|
52194
|
+
const txt = clips.getText(time);
|
|
52195
|
+
this.$el.find('#thumbnails-text').text(txt ?? '');
|
|
52272
52196
|
}
|
|
52273
52197
|
}
|
|
52274
52198
|
// calculate how far along the carousel should currently be slid
|
|
52275
52199
|
// depending on where the user is hovering on the progress bar
|
|
52276
|
-
|
|
52277
|
-
|
|
52278
|
-
backdropHeight: this._getOptions().backdropHeight,
|
|
52279
|
-
});
|
|
52280
|
-
if (!this._getOptions().backdropHeight) {
|
|
52200
|
+
updateCarousel() {
|
|
52201
|
+
if (!this.backdropHeight) {
|
|
52281
52202
|
// disabled
|
|
52282
52203
|
return;
|
|
52283
52204
|
}
|
|
52284
|
-
const
|
|
52285
|
-
const videoDuration =
|
|
52286
|
-
const startTimeOffset =
|
|
52205
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52206
|
+
const videoDuration = mediaControl.container.getDuration();
|
|
52207
|
+
const startTimeOffset = mediaControl.container.getStartTimeOffset();
|
|
52287
52208
|
// the time into the video at the current hover position
|
|
52288
|
-
const hoverTime = startTimeOffset + videoDuration * hoverPosition;
|
|
52289
|
-
const
|
|
52290
|
-
const
|
|
52209
|
+
const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
|
|
52210
|
+
const $backdrop = this.$el.find('#thumbnails-backdrop');
|
|
52211
|
+
const backdropWidth = $backdrop.width();
|
|
52212
|
+
const $carousel = this.$el.find('#thumbnails-carousel');
|
|
52291
52213
|
const carouselWidth = $carousel.width();
|
|
52292
52214
|
// slide the carousel so that the image on the carousel that is above where the person
|
|
52293
52215
|
// is hovering maps to that position in time.
|
|
52294
52216
|
// Thumbnails may not be distributed at even times along the video
|
|
52295
|
-
const thumbs = this._thumbs;
|
|
52296
52217
|
// assuming that each thumbnail has the same width
|
|
52297
|
-
const thumbWidth = carouselWidth / thumbs.length;
|
|
52218
|
+
const thumbWidth = carouselWidth / this.thumbs.length;
|
|
52298
52219
|
// determine which thumbnail applies to the current time
|
|
52299
|
-
const thumbIndex = this.
|
|
52300
|
-
const thumb = thumbs[thumbIndex];
|
|
52301
|
-
|
|
52302
|
-
|
|
52303
|
-
|
|
52304
|
-
|
|
52305
|
-
|
|
52306
|
-
thumbDuration = Math.max(videoDuration + startTimeOffset - thumb.time, 0);
|
|
52307
|
-
}
|
|
52220
|
+
const thumbIndex = this.getThumbIndexForTime(hoverTime);
|
|
52221
|
+
const thumb = this.thumbs[thumbIndex];
|
|
52222
|
+
// the last thumbnail duration will be null as it can't be determined
|
|
52223
|
+
// e.g the duration of the video may increase over time (live stream)
|
|
52224
|
+
// so calculate the duration now so this last thumbnail lasts till the end
|
|
52225
|
+
const thumbDuration = thumb.duration ??
|
|
52226
|
+
Math.max(videoDuration + startTimeOffset - thumb.time, 0);
|
|
52308
52227
|
// determine how far accross that thumbnail we are
|
|
52309
52228
|
const timeIntoThumb = hoverTime - thumb.time;
|
|
52310
52229
|
const positionInThumb = timeIntoThumb / thumbDuration;
|
|
@@ -52312,12 +52231,12 @@ class Thumbnails extends UICorePlugin {
|
|
|
52312
52231
|
// now calculate the position along carousel that we want to be above the hover position
|
|
52313
52232
|
const xCoordInCarousel = thumbIndex * thumbWidth + xCoordInThumb;
|
|
52314
52233
|
// and finally the position of the carousel when the hover position is taken in to consideration
|
|
52315
|
-
const carouselXCoord = xCoordInCarousel - hoverPosition * backdropWidth;
|
|
52316
|
-
$carousel.css('left', -carouselXCoord);
|
|
52317
|
-
const maxOpacity = this.
|
|
52318
|
-
const minOpacity = this.
|
|
52234
|
+
const carouselXCoord = xCoordInCarousel - this.hoverPosition * backdropWidth;
|
|
52235
|
+
$carousel.css('left', -carouselXCoord); // TODO +px
|
|
52236
|
+
const maxOpacity = this.options.thumbnails.backdropMaxOpacity ?? 0.6;
|
|
52237
|
+
const minOpacity = this.options.thumbnails.backdropMinOpacity ?? 0.08;
|
|
52319
52238
|
// now update the transparencies so that they fade in around the active one
|
|
52320
|
-
for (let i = 0; i < thumbs.length; i++) {
|
|
52239
|
+
for (let i = 0; i < this.thumbs.length; i++) {
|
|
52321
52240
|
const thumbXCoord = thumbWidth * i;
|
|
52322
52241
|
let distance = thumbXCoord - xCoordInCarousel;
|
|
52323
52242
|
if (distance < 0) {
|
|
@@ -52329,47 +52248,42 @@ class Thumbnails extends UICorePlugin {
|
|
|
52329
52248
|
}
|
|
52330
52249
|
// fade over the width of 2 thumbnails
|
|
52331
52250
|
const opacity = Math.max(maxOpacity - Math.abs(distance) / (2 * thumbWidth), minOpacity);
|
|
52332
|
-
this
|
|
52251
|
+
this.$backdropCarouselImgs[i].css('opacity', opacity);
|
|
52333
52252
|
}
|
|
52334
52253
|
}
|
|
52335
|
-
|
|
52336
|
-
|
|
52337
|
-
spotlightHeight: this._getOptions().spotlightHeight,
|
|
52338
|
-
});
|
|
52339
|
-
if (!this._getOptions().spotlightHeight) {
|
|
52254
|
+
updateSpotlightThumb() {
|
|
52255
|
+
if (!this.spotlightHeight) {
|
|
52340
52256
|
// disabled
|
|
52341
52257
|
return;
|
|
52342
52258
|
}
|
|
52343
|
-
const
|
|
52344
|
-
const videoDuration =
|
|
52259
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52260
|
+
const videoDuration = mediaControl.container.getDuration();
|
|
52345
52261
|
// the time into the video at the current hover position
|
|
52346
|
-
const startTimeOffset =
|
|
52347
|
-
const hoverTime = startTimeOffset + videoDuration * hoverPosition;
|
|
52262
|
+
const startTimeOffset = mediaControl.container.getStartTimeOffset();
|
|
52263
|
+
const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
|
|
52348
52264
|
this.setText(hoverTime);
|
|
52349
52265
|
// determine which thumbnail applies to the current time
|
|
52350
|
-
const thumbIndex = this.
|
|
52351
|
-
const thumb = this.
|
|
52266
|
+
const thumbIndex = this.getThumbIndexForTime(hoverTime);
|
|
52267
|
+
const thumb = this.thumbs[thumbIndex];
|
|
52352
52268
|
// update thumbnail
|
|
52353
|
-
const $spotlight = this.
|
|
52354
|
-
$spotlight.
|
|
52355
|
-
$spotlight.append(this._buildImg(thumb, this._getOptions().spotlightHeight));
|
|
52269
|
+
const $spotlight = this.$el.find('#thumbnails-spotlight');
|
|
52270
|
+
this.buildThumbImage(thumb, this.spotlightHeight, $spotlight.find('.thumbnail-container')).appendTo($spotlight);
|
|
52356
52271
|
const elWidth = this.$el.width();
|
|
52357
52272
|
const thumbWidth = $spotlight.width();
|
|
52358
52273
|
const thumbHeight = $spotlight.height();
|
|
52359
|
-
let spotlightXPos = elWidth * hoverPosition - thumbWidth / 2;
|
|
52360
52274
|
// adjust so the entire thumbnail is always visible
|
|
52361
|
-
spotlightXPos = Math.max(Math.min(
|
|
52275
|
+
const spotlightXPos = Math.max(Math.min(elWidth * this.hoverPosition - thumbWidth / 2, elWidth - thumbWidth), 0);
|
|
52362
52276
|
$spotlight.css('left', spotlightXPos);
|
|
52363
|
-
this.$
|
|
52364
|
-
|
|
52365
|
-
|
|
52277
|
+
const $textThumbnail = this.$el.find('#thumbnails-text');
|
|
52278
|
+
$textThumbnail.css('left', spotlightXPos);
|
|
52279
|
+
$textThumbnail.css('width', thumbWidth);
|
|
52280
|
+
$textThumbnail.css('bottom', thumbHeight + 1);
|
|
52366
52281
|
}
|
|
52367
52282
|
// returns the thumbnail which represents a time in the video
|
|
52368
52283
|
// or null if there is no thumbnail that can represent the time
|
|
52369
|
-
|
|
52370
|
-
|
|
52371
|
-
|
|
52372
|
-
const thumb = thumbs[i];
|
|
52284
|
+
getThumbIndexForTime(time) {
|
|
52285
|
+
for (let i = this.thumbs.length - 1; i >= 0; i--) {
|
|
52286
|
+
const thumb = this.thumbs[i];
|
|
52373
52287
|
if (thumb.time <= time) {
|
|
52374
52288
|
return i;
|
|
52375
52289
|
}
|
|
@@ -52377,37 +52291,48 @@ class Thumbnails extends UICorePlugin {
|
|
|
52377
52291
|
// stretch the first thumbnail back to the start
|
|
52378
52292
|
return 0;
|
|
52379
52293
|
}
|
|
52380
|
-
|
|
52381
|
-
|
|
52382
|
-
show: this._show,
|
|
52383
|
-
thumbsLoaded: this._thumbsLoaded,
|
|
52384
|
-
thumbs: this._thumbs.length,
|
|
52385
|
-
});
|
|
52386
|
-
if (!this._thumbsLoaded) {
|
|
52294
|
+
update() {
|
|
52295
|
+
if (!this.thumbsLoaded) {
|
|
52387
52296
|
return;
|
|
52388
52297
|
}
|
|
52389
|
-
if (this.
|
|
52298
|
+
if (this.showing && this.thumbs.length > 0) {
|
|
52299
|
+
this.updateCarousel();
|
|
52300
|
+
this.updateSpotlightThumb();
|
|
52390
52301
|
this.$el.removeClass('hidden');
|
|
52391
|
-
this._updateCarousel();
|
|
52392
|
-
this._updateSpotlightThumb();
|
|
52393
52302
|
}
|
|
52394
52303
|
else {
|
|
52395
52304
|
this.$el.addClass('hidden');
|
|
52396
52305
|
}
|
|
52397
52306
|
}
|
|
52398
|
-
|
|
52399
|
-
|
|
52400
|
-
this
|
|
52401
|
-
|
|
52402
|
-
|
|
52403
|
-
|
|
52404
|
-
|
|
52405
|
-
|
|
52406
|
-
|
|
52407
|
-
|
|
52408
|
-
|
|
52307
|
+
fixElements() {
|
|
52308
|
+
const $spotlight = this.$el.find('#thumbnails-spotlight');
|
|
52309
|
+
if (this.spotlightHeight) {
|
|
52310
|
+
$spotlight.css('height', this.spotlightHeight);
|
|
52311
|
+
}
|
|
52312
|
+
else {
|
|
52313
|
+
$spotlight.remove();
|
|
52314
|
+
}
|
|
52315
|
+
const $backdrop = this.$el.find('#thumbnails-backdrop');
|
|
52316
|
+
if (this.backdropHeight) {
|
|
52317
|
+
$backdrop.css('height', this.backdropHeight);
|
|
52318
|
+
}
|
|
52319
|
+
else {
|
|
52320
|
+
$backdrop.remove();
|
|
52321
|
+
}
|
|
52322
|
+
this.mount();
|
|
52323
|
+
}
|
|
52324
|
+
get shouldRender() {
|
|
52325
|
+
return (this.options.thumbnails &&
|
|
52326
|
+
this.options.thumbnails.sprite &&
|
|
52327
|
+
this.options.thumbnails.vtt);
|
|
52328
|
+
}
|
|
52329
|
+
render() {
|
|
52330
|
+
if (!this.shouldRender) {
|
|
52331
|
+
return this;
|
|
52332
|
+
}
|
|
52333
|
+
this.$el.html(Thumbnails.template());
|
|
52409
52334
|
this.$el.addClass('hidden');
|
|
52410
|
-
this
|
|
52335
|
+
return this;
|
|
52411
52336
|
}
|
|
52412
52337
|
}
|
|
52413
52338
|
|