@gcorevideo/player 2.22.30 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/assets/media-control/container.scss +2 -3
  2. package/assets/poster/poster.ejs +3 -1
  3. package/assets/poster/poster.scss +3 -3
  4. package/assets/style/main.scss +1 -1
  5. package/assets/thumbnails/scrub-thumbnails.ejs +5 -10
  6. package/assets/thumbnails/style.scss +4 -5
  7. package/dist/core.js +1 -1
  8. package/dist/index.css +533 -532
  9. package/dist/index.js +273 -377
  10. package/dist/player.d.ts +63 -33
  11. package/docs/api/{player.seektime.bindevents.md → player.clapprstats.clearmetrics.md} +3 -3
  12. package/docs/api/player.clapprstats.md +14 -0
  13. package/docs/api/player.extendedevents.md +14 -0
  14. package/docs/api/player.md +13 -2
  15. package/docs/api/player.seektime.attributes.md +0 -1
  16. package/docs/api/player.seektime.md +6 -197
  17. package/docs/api/{player.seektime.render.md → player.seektimesettings.md} +7 -7
  18. package/docs/api/player.skiptime.md +3 -184
  19. package/lib/plugins/clips/Clips.d.ts +7 -0
  20. package/lib/plugins/clips/Clips.d.ts.map +1 -1
  21. package/lib/plugins/clips/Clips.js +8 -0
  22. package/lib/plugins/media-control/MediaControl.d.ts +1 -7
  23. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  24. package/lib/plugins/media-control/MediaControl.js +9 -18
  25. package/lib/plugins/poster/Poster.d.ts +24 -14
  26. package/lib/plugins/poster/Poster.d.ts.map +1 -1
  27. package/lib/plugins/poster/Poster.js +67 -97
  28. package/lib/plugins/thumbnails/Thumbnails.d.ts +36 -33
  29. package/lib/plugins/thumbnails/Thumbnails.d.ts.map +1 -1
  30. package/lib/plugins/thumbnails/Thumbnails.js +174 -259
  31. package/lib/plugins/thumbnails/utils.d.ts +5 -0
  32. package/lib/plugins/thumbnails/utils.d.ts.map +1 -0
  33. package/lib/plugins/thumbnails/utils.js +12 -0
  34. package/lib/testUtils.d.ts +13 -39
  35. package/lib/testUtils.d.ts.map +1 -1
  36. package/lib/testUtils.js +15 -67
  37. package/package.json +2 -1
  38. package/src/plugins/clips/Clips.ts +10 -1
  39. package/src/plugins/media-control/MediaControl.ts +10 -21
  40. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +1 -1
  41. package/src/plugins/poster/Poster.ts +91 -110
  42. package/src/plugins/poster/__tests__/Poster.test.ts +119 -0
  43. package/src/plugins/poster/__tests__/__snapshots__/Poster.test.ts.snap +8 -0
  44. package/src/plugins/source-controller/__tests__/SourceController.test.ts +1 -2
  45. package/src/plugins/thumbnails/Thumbnails.ts +228 -330
  46. package/src/plugins/thumbnails/__tests__/Thumbnails.test.ts +72 -0
  47. package/src/plugins/thumbnails/__tests__/__snapshots__/Thumbnails.test.ts.snap +10 -0
  48. package/src/plugins/thumbnails/utils.ts +12 -0
  49. package/src/testUtils.ts +15 -88
  50. package/temp/player.api.json +295 -829
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/docs/api/player.seektime.durationshown.md +0 -14
  53. package/docs/api/player.seektime.getseektime.md +0 -20
  54. package/docs/api/player.seektime.islivestreamwithdvr.md +0 -14
  55. package/docs/api/player.seektime.mediacontrol.md +0 -14
  56. package/docs/api/player.seektime.mediacontrolcontainer.md +0 -14
  57. package/docs/api/player.seektime.shouldbevisible.md +0 -18
  58. package/docs/api/player.seektime.template.md +0 -14
  59. package/docs/api/player.seektime.update.md +0 -18
  60. package/docs/api/player.skiptime.attributes.md +0 -17
  61. package/docs/api/player.skiptime.bindevents.md +0 -18
  62. package/docs/api/player.skiptime.events.md +0 -18
  63. package/docs/api/player.skiptime.handlerewindclicks.md +0 -18
  64. package/docs/api/player.skiptime.render.md +0 -18
  65. package/docs/api/player.skiptime.setback.md +0 -18
  66. package/docs/api/player.skiptime.setforward.md +0 -18
  67. package/docs/api/player.skiptime.setmidclick.md +0 -18
  68. package/docs/api/player.skiptime.template.md +0 -14
  69. package/docs/api/player.skiptime.togglefullscreen.md +0 -18
@@ -1,14 +1,21 @@
1
- import { UICorePlugin, Events, template, $ } from '@clappr/core';
2
- import { reportError, trace } from '@gcorevideo/utils';
1
+ import { UICorePlugin, Events, template, $, } from '@clappr/core';
2
+ import { trace } from '@gcorevideo/utils';
3
3
  import parseSRT from 'parse-srt';
4
+ import assert from 'assert';
4
5
  import { CLAPPR_VERSION } from '../../build.js';
5
6
  import pluginHtml from '../../../assets/thumbnails/scrub-thumbnails.ejs';
6
7
  import '../../../assets/thumbnails/style.scss';
7
- import { getPageX } from '../utils.js';
8
+ import { loadImageDimensions } from './utils.js';
8
9
  const T = 'plugins.thumbnails';
9
10
  /**
10
11
  * `PLUGIN` that displays the thumbnails of the video when available.
11
12
  * @beta
13
+ * @remarks
14
+ * The plugin needs specially crafted VTT file with a thumbnail sprite sheet to work.
15
+ * The VTT consist of timestamp records followed by a thumbnail area
16
+ *
17
+ * Configuration options - {@link ThumbnailsPluginSettings}
18
+ *
12
19
  * @example
13
20
  * ```ts
14
21
  * import { Thumbnails } from '@gcorevideo/player'
@@ -29,19 +36,15 @@ const T = 'plugins.thumbnails';
29
36
  * ```
30
37
  */
31
38
  export class Thumbnails extends UICorePlugin {
32
- _$spotlight = null;
33
- _$backdrop = null;
34
- $container = null;
35
- $img = null;
36
- _$carousel = null;
37
- $textThumbnail = null;
38
- _$backdropCarouselImgs = [];
39
+ $backdropCarouselImgs = [];
39
40
  spriteSheetHeight = 0;
40
- _hoverPosition = 0;
41
- _show = false;
42
- _thumbsLoaded = false;
43
- _oldContainer = null;
44
- _thumbs = [];
41
+ spriteSheetWidth = 0;
42
+ hoverPosition = 0;
43
+ showing = false;
44
+ thumbsLoaded = false;
45
+ spotlightHeight = 0;
46
+ backdropHeight = 0;
47
+ thumbs = [];
45
48
  /**
46
49
  * @internal
47
50
  */
@@ -59,10 +62,14 @@ export class Thumbnails extends UICorePlugin {
59
62
  */
60
63
  get attributes() {
61
64
  return {
62
- class: this.name,
65
+ class: 'scrub-thumbnails',
63
66
  };
64
67
  }
65
68
  static template = template(pluginHtml);
69
+ constructor(core) {
70
+ super(core);
71
+ this.backdropHeight = this.options.thumbnails?.backdropHeight ?? 0;
72
+ }
66
73
  /*
67
74
  * Helper to build the "thumbs" property for a sprite sheet.
68
75
  *
@@ -74,25 +81,21 @@ export class Thumbnails extends UICorePlugin {
74
81
  * timeInterval- The interval (in seconds) between the thumbnails.
75
82
  * startTime- The time (in seconds) that the first thumbnail represents. (defaults to 0)
76
83
  */
77
- // buildSpriteConfig(vtt, spriteSheetUrl, numThumbs, thumbWidth, thumbHeight, numColumns, timeInterval, startTime) {
78
- buildSpriteConfig(vtt, spriteSheetUrl) {
84
+ buildSpriteConfig(vtt, baseUrl) {
79
85
  const thumbs = [];
80
- // let coor: string[] = [];
81
86
  for (const vt of vtt) {
82
87
  const el = vt.text;
83
- // if (el && el.search(/\d*,\d*,\d*,\d*/g) > -1) {
84
- // el = el.match(/\d*,\d*,\d*,\d*/g)[0];
85
- // coor = el.split(',');
86
- // }
87
88
  if (el) {
88
- const m = el.match(/xywh=\d*,\d*,\d*,\d*/g);
89
+ const m = el.match(/(\w+)#xywh=(\d+,\d+,\d+,\d+)/);
89
90
  if (m) {
90
- const coor = m[0].split(',');
91
+ const coor = m[2].split(',');
91
92
  const w = parseInt(coor[2], 10);
92
93
  const h = parseInt(coor[3], 10);
93
94
  if (w > 0 && h > 0) {
94
95
  thumbs.push({
95
- url: spriteSheetUrl,
96
+ // TODO handle relative URLs
97
+ // url: new URL(m[0], baseUrl).toString(),
98
+ url: baseUrl,
96
99
  time: vt.start,
97
100
  w,
98
101
  h,
@@ -105,242 +108,147 @@ export class Thumbnails extends UICorePlugin {
105
108
  }
106
109
  return thumbs;
107
110
  }
108
- // TODO check if seek enabled
109
111
  /**
110
112
  * @internal
111
113
  */
112
114
  bindEvents() {
113
- this.listenToOnce(this.core, Events.CORE_READY, this._onCoreReady);
114
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this._onMouseMove);
115
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this._onMouseLeave);
116
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this._init);
117
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this._onMediaControlContainerChanged);
115
+ this.listenToOnce(this.core, Events.CORE_READY, this.onCoreReady);
118
116
  }
119
- _bindContainerEvents() {
120
- if (this._oldContainer) {
121
- this.stopListening(this._oldContainer, Events.CONTAINER_TIMEUPDATE, this._renderPlugin);
122
- }
123
- this._oldContainer = this.core.mediaControl.container;
124
- this.listenTo(this.core.mediaControl.container, Events.CONTAINER_TIMEUPDATE, this._renderPlugin);
117
+ bindContainerEvents(container) {
118
+ this.listenTo(container, Events.CONTAINER_TIMEUPDATE, this.update);
125
119
  }
126
- _onCoreReady() {
127
- try {
128
- if (!this.options.thumbnails ||
129
- !this.options.thumbnails.sprite ||
130
- !this.options.thumbnails.vtt) {
131
- this.destroy();
132
- return;
133
- }
134
- }
135
- catch (error) {
136
- reportError(error);
120
+ onCoreReady() {
121
+ const mediaControl = this.core.getPlugin('media_control');
122
+ assert(mediaControl, `MediaControl is required for ${this.name} plugin to work`);
123
+ if (!this.options.thumbnails ||
124
+ !this.options.thumbnails.sprite ||
125
+ !this.options.thumbnails.vtt) {
126
+ trace(`${T} misconfigured: options.thumbnails.sprite and options.thumbnails.vtt are required`);
127
+ this.destroy();
137
128
  return;
138
129
  }
139
- // TODO options
140
- const spriteSheet = this.options.thumbnails.sprite;
141
- this._thumbs = this.buildSpriteConfig(parseSRT(this.options.thumbnails.vtt), spriteSheet);
142
- if (!this._thumbs.length) {
130
+ const { sprite: spriteSheet, vtt } = this.options.thumbnails;
131
+ this.thumbs = this.buildSpriteConfig(parseSRT(vtt), spriteSheet);
132
+ if (!this.thumbs.length) {
133
+ trace(`${T} failed to parse the sprite sheet`);
143
134
  this.destroy();
144
135
  return;
145
136
  }
137
+ this.spotlightHeight = this.options.thumbnails?.spotlightHeight ?? 0;
146
138
  this.loadSpriteSheet(spriteSheet).then(() => {
147
- this._thumbsLoaded = true;
148
- this.core.options.thumbnails.spotlightHeight = this._thumbs[0].h;
149
- this._init();
139
+ this.thumbsLoaded = true;
140
+ this.spotlightHeight = this.spotlightHeight
141
+ ? Math.min(this.spotlightHeight, this.thumbs[0].h)
142
+ : this.thumbs[0].h;
143
+ this.init();
150
144
  });
145
+ this.listenTo(mediaControl, Events.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this.onMouseMoveSeekbar);
146
+ this.listenTo(mediaControl, Events.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this.onMouseLeave);
147
+ this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.init);
148
+ this.listenTo(mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, () => this.onContainerChanged(mediaControl.container));
151
149
  }
152
150
  async loadSpriteSheet(spriteSheetUrl) {
153
- return new Promise((resolve, reject) => {
154
- const img = new Image();
155
- img.onload = () => {
156
- this.spriteSheetHeight = img.height;
157
- resolve();
158
- };
159
- img.onerror = reject;
160
- img.src = spriteSheetUrl;
151
+ return loadImageDimensions(spriteSheetUrl).then(({ height, width }) => {
152
+ this.spriteSheetHeight = height;
153
+ this.spriteSheetWidth = width;
161
154
  });
162
155
  }
163
- _onMediaControlContainerChanged() {
164
- this._bindContainerEvents();
156
+ onContainerChanged(container) {
157
+ this.bindContainerEvents(container);
165
158
  }
166
- _init() {
167
- if (!this._thumbsLoaded) {
168
- // _init() will be called when the thumbs are loaded,
159
+ init() {
160
+ if (!this.thumbsLoaded) {
161
+ // init() will be called when the thumbs are loaded,
169
162
  // 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)
170
163
  return;
171
164
  }
172
165
  // Init the backdropCarousel as array to keep reference of thumbnail images
173
- this._$backdropCarouselImgs = [];
174
- // create/recreate the dom elements for the plugin
175
- this._createElements();
176
- this._loadBackdrop();
177
- this._renderPlugin();
166
+ this.$backdropCarouselImgs = [];
167
+ this.fixElements();
168
+ this.loadBackdrop();
169
+ this.update();
178
170
  }
179
- _getOptions() {
180
- if (!('thumbnails' in this.core.options)) {
181
- throw "'thumbnail property missing from options object.";
182
- }
183
- return this.core.options.thumbnails;
184
- }
185
- _appendElToMediaControl() {
186
- // insert after the background
187
- this.core.mediaControl.$el.find('.seek-time').css('bottom', 56);
188
- this.core.mediaControl.$el.first().after(this.el);
189
- }
190
- _onMouseMove(e) {
191
- // trace(`${T} _onMouseMove`, {
192
- // e: (e as any).name,
193
- // t: typeof e,
194
- // t2: typeof arguments[1],
195
- // });
196
- this._calculateHoverPosition(e);
197
- this._show = true;
198
- this._renderPlugin();
171
+ mount() {
172
+ // insert after the background TODO figure out why
173
+ const mediaControl = this.core.getPlugin('media_control');
174
+ mediaControl.$el.find('.seek-time').css('bottom', 56); // TODO check
175
+ // TODO use mediaControl.mount? into the `layer`
176
+ mediaControl.$el.append(this.$el);
199
177
  }
200
- _onMouseLeave() {
201
- this._show = false;
202
- this._renderPlugin();
178
+ onMouseMoveSeekbar(_, pos) {
179
+ this.hoverPosition = pos;
180
+ this.showing = true;
181
+ this.update();
203
182
  }
204
- _calculateHoverPosition(e) {
205
- const offset = getPageX(e) - this.core.mediaControl.$seekBarContainer.offset().left;
206
- // proportion into the seek bar that the mouse is hovered over 0-1
207
- this._hoverPosition = Math.min(1, Math.max(offset / this.core.mediaControl.$seekBarContainer.width(), 0));
183
+ onMouseLeave() {
184
+ this.showing = false;
185
+ this.update();
208
186
  }
209
- // private _buildThumbsFromOptions() {
210
- // const thumbs = this._thumbs;
211
- // const promises = thumbs.map((thumb) => {
212
- // return this._addThumbFromSrc(thumb);
213
- // });
214
- // return Promise.all(promises);
215
- // }
216
- // private _addThumbFromSrc(thumbSrc) {
217
- // return new Promise((resolve, reject) => {
218
- // const img = new Image();
219
- // img.onload = () => {
220
- // resolve(img);
221
- // };
222
- // img.onerror = reject;
223
- // img.src = thumbSrc.url;
224
- // }).then((img) => {
225
- // const startTime = thumbSrc.time;
226
- // // determine the thumb index
227
- // let index = null;
228
- // this._thumbs.some((thumb, i) => {
229
- // if (startTime < thumb.time) {
230
- // index = i;
231
- // return true;
232
- // }
233
- // return false;
234
- // });
235
- // if (index === null) {
236
- // index = this._thumbs.length;
237
- // }
238
- // const next = index < this._thumbs.length ? this._thumbs[index] : null;
239
- // const prev = index > 0 ? this._thumbs[index - 1] : null;
240
- // if (prev) {
241
- // // update the duration of the previous thumbnail
242
- // prev.duration = startTime - prev.time;
243
- // }
244
- // // the duration this thumb lasts for
245
- // // if it is the last thumb then duration will be null
246
- // const duration = next ? next.time - thumbSrc.time : null;
247
- // const imageW = img.width;
248
- // const imageH = img.height;
249
- // const thumb = {
250
- // imageW: imageW, // actual width of image
251
- // imageH: imageH, // actual height of image
252
- // x: thumbSrc.x || 0, // x coord in image of sprite
253
- // y: thumbSrc.y || 0, // y coord in image of sprite
254
- // w: thumbSrc.w || imageW, // width of sprite
255
- // h: thumbSrc.h || imageH, // height of sprite
256
- // url: thumbSrc.url,
257
- // time: startTime, // time this thumb represents
258
- // duration: duration, // how long (from time) this thumb represents
259
- // src: thumbSrc
260
- // };
261
- // this._thumbs.splice(index, 0, thumb);
262
- // return thumb;
263
- // });
264
- // }
265
187
  // builds a dom element which represents the thumbnail
266
- // scaled to the provided height
267
- _buildImg(thumb, height) {
188
+ // scaled to the given height
189
+ buildThumbImage(thumb, height) {
268
190
  const scaleFactor = height / thumb.h;
269
- if (!this.$img) {
270
- this.$img = $('<img />').addClass('thumbnail-img').attr('src', thumb.url);
271
- }
272
- // the container will contain the image positioned so that the correct sprite
273
- // is visible
274
- if (!this.$container) {
275
- this.$container = $('<div />').addClass('thumbnail-container');
276
- }
277
- this.$container.css('width', thumb.w * scaleFactor);
278
- this.$container.css('height', height);
279
- this.$img.css({
280
- height: this.spriteSheetHeight * scaleFactor,
281
- left: -1 * thumb.x * scaleFactor,
282
- top: -1 * thumb.y * scaleFactor,
191
+ const $container = $('<div />').addClass('thumbnail-container');
192
+ $container.css('width', thumb.w * scaleFactor);
193
+ $container.css('height', height);
194
+ $container.css({
195
+ backgroundImage: `url(${thumb.url})`,
196
+ backgroundSize: `${Math.floor(this.spriteSheetWidth * scaleFactor)}px ${Math.floor(this.spriteSheetHeight * scaleFactor)}px`,
197
+ backgroundPosition: `-${Math.floor(thumb.x * scaleFactor)}px -${Math.floor(thumb.y * scaleFactor)}px`,
283
198
  });
284
- if (this.$container.find(this.$img).length === 0) {
285
- this.$container.append(this.$img);
286
- }
287
- return this.$container;
199
+ return $container;
288
200
  }
289
- _loadBackdrop() {
290
- if (!this._getOptions().backdropHeight) {
201
+ loadBackdrop() {
202
+ if (!this.backdropHeight) {
291
203
  // disabled
292
204
  return;
293
205
  }
294
206
  // append each of the thumbnails to the backdrop carousel
295
- const $carousel = this._$carousel;
296
- for (const thumb of this._thumbs) {
297
- const $img = this._buildImg(thumb, this._getOptions().backdropHeight);
298
- // Keep reference to thumbnail
299
- this._$backdropCarouselImgs.push($img);
207
+ const $carousel = this.$el.find('#thumbnails-carousel');
208
+ for (const thumb of this.thumbs) {
209
+ const $img = this.buildThumbImage(thumb, this.backdropHeight);
210
+ // Keep reference to the thumbnail
211
+ this.$backdropCarouselImgs.push($img);
300
212
  // Add thumbnail to DOM
301
213
  $carousel.append($img);
302
214
  }
303
215
  }
304
216
  setText(time) {
305
- if (this.core.getPlugin('clips')) {
306
- const txt = this.core.getPlugin('clips').getText(time);
307
- this.$textThumbnail.text(txt);
217
+ const clips = this.core.getPlugin('clips');
218
+ if (clips) {
219
+ const txt = clips.getText(time);
220
+ this.$el.find('#thumbnails-text').text(txt ?? '');
308
221
  }
309
222
  }
310
223
  // calculate how far along the carousel should currently be slid
311
224
  // depending on where the user is hovering on the progress bar
312
- _updateCarousel() {
313
- trace(`${T} _updateCarousel`, {
314
- backdropHeight: this._getOptions().backdropHeight,
315
- });
316
- if (!this._getOptions().backdropHeight) {
225
+ updateCarousel() {
226
+ if (!this.backdropHeight) {
317
227
  // disabled
318
228
  return;
319
229
  }
320
- const hoverPosition = this._hoverPosition;
321
- const videoDuration = this.core.mediaControl.container.getDuration();
322
- const startTimeOffset = this.core.mediaControl.container.getStartTimeOffset();
230
+ const mediaControl = this.core.getPlugin('media_control');
231
+ const videoDuration = mediaControl.container.getDuration();
232
+ const startTimeOffset = mediaControl.container.getStartTimeOffset();
323
233
  // the time into the video at the current hover position
324
- const hoverTime = startTimeOffset + videoDuration * hoverPosition;
325
- const backdropWidth = this._$backdrop.width();
326
- const $carousel = this._$carousel;
234
+ const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
235
+ const $backdrop = this.$el.find('#thumbnails-backdrop');
236
+ const backdropWidth = $backdrop.width();
237
+ const $carousel = this.$el.find('#thumbnails-carousel');
327
238
  const carouselWidth = $carousel.width();
328
239
  // slide the carousel so that the image on the carousel that is above where the person
329
240
  // is hovering maps to that position in time.
330
241
  // Thumbnails may not be distributed at even times along the video
331
- const thumbs = this._thumbs;
332
242
  // assuming that each thumbnail has the same width
333
- const thumbWidth = carouselWidth / thumbs.length;
243
+ const thumbWidth = carouselWidth / this.thumbs.length;
334
244
  // determine which thumbnail applies to the current time
335
- const thumbIndex = this._getThumbIndexForTime(hoverTime);
336
- const thumb = thumbs[thumbIndex];
337
- let thumbDuration = thumb.duration;
338
- if (!thumbDuration) {
339
- // the last thumbnail duration will be null as it can't be determined
340
- // e.g the duration of the video may increase over time (live stream)
341
- // so calculate the duration now so this last thumbnail lasts till the end
342
- thumbDuration = Math.max(videoDuration + startTimeOffset - thumb.time, 0);
343
- }
245
+ const thumbIndex = this.getThumbIndexForTime(hoverTime);
246
+ const thumb = this.thumbs[thumbIndex];
247
+ // the last thumbnail duration will be null as it can't be determined
248
+ // e.g the duration of the video may increase over time (live stream)
249
+ // so calculate the duration now so this last thumbnail lasts till the end
250
+ const thumbDuration = thumb.duration ??
251
+ Math.max(videoDuration + startTimeOffset - thumb.time, 0);
344
252
  // determine how far accross that thumbnail we are
345
253
  const timeIntoThumb = hoverTime - thumb.time;
346
254
  const positionInThumb = timeIntoThumb / thumbDuration;
@@ -348,12 +256,12 @@ export class Thumbnails extends UICorePlugin {
348
256
  // now calculate the position along carousel that we want to be above the hover position
349
257
  const xCoordInCarousel = thumbIndex * thumbWidth + xCoordInThumb;
350
258
  // and finally the position of the carousel when the hover position is taken in to consideration
351
- const carouselXCoord = xCoordInCarousel - hoverPosition * backdropWidth;
352
- $carousel.css('left', -carouselXCoord);
353
- const maxOpacity = this._getOptions().backdropMaxOpacity || 0.6;
354
- const minOpacity = this._getOptions().backdropMinOpacity || 0.08;
259
+ const carouselXCoord = xCoordInCarousel - this.hoverPosition * backdropWidth;
260
+ $carousel.css('left', -carouselXCoord); // TODO +px
261
+ const maxOpacity = this.options.thumbnails.backdropMaxOpacity ?? 0.6;
262
+ const minOpacity = this.options.thumbnails.backdropMinOpacity ?? 0.08;
355
263
  // now update the transparencies so that they fade in around the active one
356
- for (let i = 0; i < thumbs.length; i++) {
264
+ for (let i = 0; i < this.thumbs.length; i++) {
357
265
  const thumbXCoord = thumbWidth * i;
358
266
  let distance = thumbXCoord - xCoordInCarousel;
359
267
  if (distance < 0) {
@@ -365,47 +273,43 @@ export class Thumbnails extends UICorePlugin {
365
273
  }
366
274
  // fade over the width of 2 thumbnails
367
275
  const opacity = Math.max(maxOpacity - Math.abs(distance) / (2 * thumbWidth), minOpacity);
368
- this._$backdropCarouselImgs[i].css('opacity', opacity);
276
+ this.$backdropCarouselImgs[i].css('opacity', opacity);
369
277
  }
370
278
  }
371
- _updateSpotlightThumb() {
372
- trace(`${T} _updateSpotlightThumb`, {
373
- spotlightHeight: this._getOptions().spotlightHeight,
374
- });
375
- if (!this._getOptions().spotlightHeight) {
279
+ updateSpotlightThumb() {
280
+ if (!this.spotlightHeight) {
376
281
  // disabled
377
282
  return;
378
283
  }
379
- const hoverPosition = this._hoverPosition;
380
- const videoDuration = this.core.mediaControl.container.getDuration();
284
+ const mediaControl = this.core.getPlugin('media_control');
285
+ const videoDuration = mediaControl.container.getDuration();
381
286
  // the time into the video at the current hover position
382
- const startTimeOffset = this.core.mediaControl.container.getStartTimeOffset();
383
- const hoverTime = startTimeOffset + videoDuration * hoverPosition;
287
+ const startTimeOffset = mediaControl.container.getStartTimeOffset();
288
+ const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
384
289
  this.setText(hoverTime);
385
290
  // determine which thumbnail applies to the current time
386
- const thumbIndex = this._getThumbIndexForTime(hoverTime);
387
- const thumb = this._thumbs[thumbIndex];
291
+ const thumbIndex = this.getThumbIndexForTime(hoverTime);
292
+ const thumb = this.thumbs[thumbIndex];
388
293
  // update thumbnail
389
- const $spotlight = this._$spotlight;
294
+ const $spotlight = this.$el.find('#thumbnails-spotlight');
390
295
  $spotlight.empty();
391
- $spotlight.append(this._buildImg(thumb, this._getOptions().spotlightHeight));
296
+ $spotlight.append(this.buildThumbImage(thumb, this.spotlightHeight));
392
297
  const elWidth = this.$el.width();
393
298
  const thumbWidth = $spotlight.width();
394
299
  const thumbHeight = $spotlight.height();
395
- let spotlightXPos = elWidth * hoverPosition - thumbWidth / 2;
396
300
  // adjust so the entire thumbnail is always visible
397
- spotlightXPos = Math.max(Math.min(spotlightXPos, elWidth - thumbWidth), 0);
301
+ const spotlightXPos = Math.max(Math.min(elWidth * this.hoverPosition - thumbWidth / 2, elWidth - thumbWidth), 0);
398
302
  $spotlight.css('left', spotlightXPos);
399
- this.$textThumbnail.css('left', spotlightXPos);
400
- this.$textThumbnail.css('width', thumbWidth);
401
- this.$textThumbnail.css('bottom', thumbHeight + 1);
303
+ const $textThumbnail = this.$el.find('#thumbnails-text');
304
+ $textThumbnail.css('left', spotlightXPos);
305
+ $textThumbnail.css('width', thumbWidth);
306
+ $textThumbnail.css('bottom', thumbHeight + 1);
402
307
  }
403
308
  // returns the thumbnail which represents a time in the video
404
309
  // or null if there is no thumbnail that can represent the time
405
- _getThumbIndexForTime(time) {
406
- const thumbs = this._thumbs;
407
- for (let i = thumbs.length - 1; i >= 0; i--) {
408
- const thumb = thumbs[i];
310
+ getThumbIndexForTime(time) {
311
+ for (let i = this.thumbs.length - 1; i >= 0; i--) {
312
+ const thumb = this.thumbs[i];
409
313
  if (thumb.time <= time) {
410
314
  return i;
411
315
  }
@@ -413,36 +317,47 @@ export class Thumbnails extends UICorePlugin {
413
317
  // stretch the first thumbnail back to the start
414
318
  return 0;
415
319
  }
416
- _renderPlugin() {
417
- trace(`${T} _renderPlugin`, {
418
- show: this._show,
419
- thumbsLoaded: this._thumbsLoaded,
420
- thumbs: this._thumbs.length,
421
- });
422
- if (!this._thumbsLoaded) {
320
+ update() {
321
+ if (!this.thumbsLoaded) {
423
322
  return;
424
323
  }
425
- if (this._show && this._thumbs.length > 0) {
324
+ if (this.showing && this.thumbs.length > 0) {
325
+ this.updateCarousel();
326
+ this.updateSpotlightThumb();
426
327
  this.$el.removeClass('hidden');
427
- this._updateCarousel();
428
- this._updateSpotlightThumb();
429
328
  }
430
329
  else {
431
330
  this.$el.addClass('hidden');
432
331
  }
433
332
  }
434
- _createElements() {
435
- trace(`${T} _createElements`);
436
- this.$el.html(Thumbnails.template({
437
- backdropHeight: this._getOptions().backdropHeight,
438
- spotlightHeight: this._getOptions().spotlightHeight,
439
- }));
440
- // cache dom references
441
- this._$spotlight = this.$el.find('.spotlight');
442
- this._$backdrop = this.$el.find('.backdrop');
443
- this._$carousel = this._$backdrop.find('.carousel');
444
- this.$textThumbnail = this.$el.find('.thumbnails-text');
333
+ fixElements() {
334
+ const $spotlight = this.$el.find('#thumbnails-spotlight');
335
+ if (this.spotlightHeight) {
336
+ $spotlight.css('height', this.spotlightHeight);
337
+ }
338
+ else {
339
+ $spotlight.remove();
340
+ }
341
+ const $backdrop = this.$el.find('#thumbnails-backdrop');
342
+ if (this.backdropHeight) {
343
+ $backdrop.css('height', this.backdropHeight);
344
+ }
345
+ else {
346
+ $backdrop.remove();
347
+ }
348
+ this.mount();
349
+ }
350
+ get shouldRender() {
351
+ return (this.options.thumbnails &&
352
+ this.options.thumbnails.sprite &&
353
+ this.options.thumbnails.vtt);
354
+ }
355
+ render() {
356
+ if (!this.shouldRender) {
357
+ return this;
358
+ }
359
+ this.$el.html(Thumbnails.template());
445
360
  this.$el.addClass('hidden');
446
- this._appendElToMediaControl();
361
+ return this;
447
362
  }
448
363
  }
@@ -0,0 +1,5 @@
1
+ export declare function loadImageDimensions(url: string): Promise<{
2
+ width: number;
3
+ height: number;
4
+ }>;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/plugins/thumbnails/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAW3F"}
@@ -0,0 +1,12 @@
1
+ export function loadImageDimensions(url) {
2
+ return new Promise((resolve, reject) => {
3
+ const img = new Image();
4
+ img.src = url;
5
+ img.onload = () => {
6
+ resolve({ width: img.width, height: img.height });
7
+ };
8
+ img.onerror = () => {
9
+ reject(new Error('Failed to load image'));
10
+ };
11
+ });
12
+ }