@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.
- package/assets/media-control/container.scss +2 -3
- package/assets/poster/poster.ejs +3 -1
- package/assets/poster/poster.scss +3 -3
- package/assets/style/main.scss +1 -1
- 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 +533 -532
- package/dist/index.js +273 -377
- package/dist/player.d.ts +63 -33
- package/docs/api/{player.seektime.bindevents.md → player.clapprstats.clearmetrics.md} +3 -3
- package/docs/api/player.clapprstats.md +14 -0
- package/docs/api/player.extendedevents.md +14 -0
- package/docs/api/player.md +13 -2
- package/docs/api/player.seektime.attributes.md +0 -1
- package/docs/api/player.seektime.md +6 -197
- package/docs/api/{player.seektime.render.md → player.seektimesettings.md} +7 -7
- package/docs/api/player.skiptime.md +3 -184
- 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/poster/Poster.d.ts +24 -14
- package/lib/plugins/poster/Poster.d.ts.map +1 -1
- package/lib/plugins/poster/Poster.js +67 -97
- 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 -259
- 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.d.ts +13 -39
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +15 -67
- package/package.json +2 -1
- package/src/plugins/clips/Clips.ts +10 -1
- package/src/plugins/media-control/MediaControl.ts +10 -21
- package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +1 -1
- package/src/plugins/poster/Poster.ts +91 -110
- package/src/plugins/poster/__tests__/Poster.test.ts +119 -0
- package/src/plugins/poster/__tests__/__snapshots__/Poster.test.ts.snap +8 -0
- package/src/plugins/source-controller/__tests__/SourceController.test.ts +1 -2
- package/src/plugins/thumbnails/Thumbnails.ts +228 -330
- 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 +15 -88
- package/temp/player.api.json +295 -829
- package/tsconfig.tsbuildinfo +1 -1
- package/docs/api/player.seektime.durationshown.md +0 -14
- package/docs/api/player.seektime.getseektime.md +0 -20
- package/docs/api/player.seektime.islivestreamwithdvr.md +0 -14
- package/docs/api/player.seektime.mediacontrol.md +0 -14
- package/docs/api/player.seektime.mediacontrolcontainer.md +0 -14
- package/docs/api/player.seektime.shouldbevisible.md +0 -18
- package/docs/api/player.seektime.template.md +0 -14
- package/docs/api/player.seektime.update.md +0 -18
- package/docs/api/player.skiptime.attributes.md +0 -17
- package/docs/api/player.skiptime.bindevents.md +0 -18
- package/docs/api/player.skiptime.events.md +0 -18
- package/docs/api/player.skiptime.handlerewindclicks.md +0 -18
- package/docs/api/player.skiptime.render.md +0 -18
- package/docs/api/player.skiptime.setback.md +0 -18
- package/docs/api/player.skiptime.setforward.md +0 -18
- package/docs/api/player.skiptime.setmidclick.md +0 -18
- package/docs/api/player.skiptime.template.md +0 -14
- package/docs/api/player.skiptime.togglefullscreen.md +0 -18
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.0";
|
|
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');
|
|
@@ -50001,7 +50000,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
50001
50000
|
}
|
|
50002
50001
|
}
|
|
50003
50002
|
|
|
50004
|
-
const posterHTML = "<div class=\"play-wrapper\"
|
|
50003
|
+
const posterHTML = "<div class=\"play-wrapper\" id=\"gplayer-poster\">\n <div class='circle-poster gcore-skin-button-color gcore-skin-border-color' id='poster-play'></div>\n</div>\n";
|
|
50005
50004
|
|
|
50006
50005
|
//Copyright 2014 Globo.com Player authors. All rights reserved.
|
|
50007
50006
|
// Use of this source code is governed by a BSD-style
|
|
@@ -50011,19 +50010,13 @@ const T$8 = 'plugins.poster';
|
|
|
50011
50010
|
* `PLUGIN` that displays a poster image in the background and a big play button on top when playback is stopped
|
|
50012
50011
|
* @beta
|
|
50013
50012
|
* @remarks
|
|
50014
|
-
* When the playback is stopped, media control UI is disabled.
|
|
50013
|
+
* When the playback is stopped or not yet started, the media control UI is disabled and hidden.
|
|
50014
|
+
* Media control gets activated once the metadata is loaded after playback is initiated.
|
|
50015
|
+
* This plugin displays a big play button on top of the poster image to allow user to start playback.
|
|
50015
50016
|
* Note that the poster image, if specified via the player config, will be used to update video element's poster attribute by the
|
|
50016
50017
|
* HTML5-video-based playback module.
|
|
50017
50018
|
*
|
|
50018
|
-
* Configuration options
|
|
50019
|
-
*
|
|
50020
|
-
* - `poster.custom` - custom CSS background
|
|
50021
|
-
*
|
|
50022
|
-
* - `poster.showForNoOp` - whether to show the poster when the playback is not started
|
|
50023
|
-
*
|
|
50024
|
-
* - `poster.url` - the URL of the poster image
|
|
50025
|
-
*
|
|
50026
|
-
* - `poster.showOnVideoEnd` - whether to show the poster when the playback is ended
|
|
50019
|
+
* Configuration options - {@link PosterPluginSettings}
|
|
50027
50020
|
*
|
|
50028
50021
|
* @example
|
|
50029
50022
|
* ```ts
|
|
@@ -50039,10 +50032,9 @@ const T$8 = 'plugins.poster';
|
|
|
50039
50032
|
class Poster extends UIContainerPlugin {
|
|
50040
50033
|
// TODO merge non-poster related functionality into the ClickToPause plugin
|
|
50041
50034
|
hasFatalError = false;
|
|
50042
|
-
|
|
50035
|
+
playing = false;
|
|
50043
50036
|
playRequested = false;
|
|
50044
50037
|
$playButton = null;
|
|
50045
|
-
$playWrapper = null;
|
|
50046
50038
|
/**
|
|
50047
50039
|
* @internal
|
|
50048
50040
|
*/
|
|
@@ -50062,8 +50054,10 @@ class Poster extends UIContainerPlugin {
|
|
|
50062
50054
|
}
|
|
50063
50055
|
const showForNoOp = !!this.options.poster?.showForNoOp;
|
|
50064
50056
|
return (this.container.playback.name !== 'html_img' &&
|
|
50065
|
-
(this.
|
|
50066
|
-
|
|
50057
|
+
(!this.isNoOp || showForNoOp));
|
|
50058
|
+
}
|
|
50059
|
+
get isNoOp() {
|
|
50060
|
+
return this.container.playback.getPlaybackType() === Playback.NO_OP;
|
|
50067
50061
|
}
|
|
50068
50062
|
/**
|
|
50069
50063
|
* @internal
|
|
@@ -50071,7 +50065,6 @@ class Poster extends UIContainerPlugin {
|
|
|
50071
50065
|
get attributes() {
|
|
50072
50066
|
return {
|
|
50073
50067
|
class: 'player-poster',
|
|
50074
|
-
'data-poster': '',
|
|
50075
50068
|
};
|
|
50076
50069
|
}
|
|
50077
50070
|
/**
|
|
@@ -50082,9 +50075,6 @@ class Poster extends UIContainerPlugin {
|
|
|
50082
50075
|
click: 'clicked',
|
|
50083
50076
|
};
|
|
50084
50077
|
}
|
|
50085
|
-
get showOnVideoEnd() {
|
|
50086
|
-
return this.options.poster?.showOnVideoEnd !== false;
|
|
50087
|
-
}
|
|
50088
50078
|
/**
|
|
50089
50079
|
* @internal
|
|
50090
50080
|
*/
|
|
@@ -50093,19 +50083,22 @@ class Poster extends UIContainerPlugin {
|
|
|
50093
50083
|
this.listenTo(this.container, Events$1.CONTAINER_PLAY, this.onPlay);
|
|
50094
50084
|
this.listenTo(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.update);
|
|
50095
50085
|
this.listenTo(this.container, Events$1.CONTAINER_STATE_BUFFERFULL, this.update);
|
|
50096
|
-
this.listenTo(this.container, Events$1.CONTAINER_OPTIONS_CHANGE, this.
|
|
50086
|
+
this.listenTo(this.container, Events$1.CONTAINER_OPTIONS_CHANGE, this.update);
|
|
50097
50087
|
this.listenTo(this.container, Events$1.CONTAINER_ERROR, this.onError);
|
|
50098
|
-
this
|
|
50088
|
+
// TODO check if this event is always accompanied with the CONTAINER_STOP
|
|
50089
|
+
if (this.options.poster?.showOnVideoEnd !== false) {
|
|
50099
50090
|
this.listenTo(this.container, Events$1.CONTAINER_ENDED, this.onStop);
|
|
50091
|
+
}
|
|
50100
50092
|
this.listenTo(this.container, Events$1.CONTAINER_READY, this.render);
|
|
50101
|
-
this.listenTo(this.container, Events$1.PLAYBACK_PLAY_INTENT, this.onPlayIntent);
|
|
50093
|
+
this.listenTo(this.container.playback, Events$1.PLAYBACK_PLAY_INTENT, this.onPlayIntent);
|
|
50102
50094
|
}
|
|
50103
50095
|
/**
|
|
50104
50096
|
* Reenables earlier disabled plugin
|
|
50105
50097
|
*/
|
|
50106
50098
|
enable() {
|
|
50099
|
+
trace(`${T$8} enable`);
|
|
50107
50100
|
super.enable();
|
|
50108
|
-
this.
|
|
50101
|
+
this.playing = this.container.playback.isPlaying();
|
|
50109
50102
|
this.update();
|
|
50110
50103
|
}
|
|
50111
50104
|
/**
|
|
@@ -50113,7 +50106,7 @@ class Poster extends UIContainerPlugin {
|
|
|
50113
50106
|
*/
|
|
50114
50107
|
disable() {
|
|
50115
50108
|
trace(`${T$8} disable`);
|
|
50116
|
-
this.
|
|
50109
|
+
this.playing = false;
|
|
50117
50110
|
this.playRequested = false;
|
|
50118
50111
|
super.disable();
|
|
50119
50112
|
}
|
|
@@ -50122,17 +50115,15 @@ class Poster extends UIContainerPlugin {
|
|
|
50122
50115
|
error,
|
|
50123
50116
|
enabled: this.enabled,
|
|
50124
50117
|
});
|
|
50125
|
-
this.hasFatalError = error.level === PlayerError.Levels.FATAL;
|
|
50126
50118
|
if (this.hasFatalError) {
|
|
50127
|
-
|
|
50128
|
-
if (!this.playRequested) {
|
|
50129
|
-
this.showPlayButton();
|
|
50130
|
-
}
|
|
50119
|
+
return;
|
|
50131
50120
|
}
|
|
50121
|
+
this.hasFatalError = error.level === PlayerError.Levels.FATAL;
|
|
50122
|
+
// this.hasFatalError is reset on container recreate
|
|
50132
50123
|
}
|
|
50133
50124
|
onPlay() {
|
|
50134
50125
|
trace(`${T$8} onPlay`);
|
|
50135
|
-
this.
|
|
50126
|
+
this.playing = true;
|
|
50136
50127
|
this.playRequested = false;
|
|
50137
50128
|
this.update();
|
|
50138
50129
|
}
|
|
@@ -50142,21 +50133,21 @@ class Poster extends UIContainerPlugin {
|
|
|
50142
50133
|
this.update();
|
|
50143
50134
|
}
|
|
50144
50135
|
onStop() {
|
|
50145
|
-
trace(`${T$8} onStop
|
|
50146
|
-
|
|
50147
|
-
});
|
|
50148
|
-
this.hasStartedPlaying = false;
|
|
50136
|
+
trace(`${T$8} onStop`);
|
|
50137
|
+
this.playing = false;
|
|
50149
50138
|
this.playRequested = false;
|
|
50150
50139
|
this.update();
|
|
50151
50140
|
}
|
|
50152
|
-
updatePlayButton(
|
|
50153
|
-
trace(`${T$8} updatePlayButton
|
|
50154
|
-
|
|
50155
|
-
chromeless
|
|
50156
|
-
|
|
50157
|
-
|
|
50158
|
-
|
|
50159
|
-
|
|
50141
|
+
updatePlayButton() {
|
|
50142
|
+
trace(`${T$8} updatePlayButton`);
|
|
50143
|
+
const show = !this.isNoOp &&
|
|
50144
|
+
!(this.options.chromeless && !this.options.allowUserInteraction) &&
|
|
50145
|
+
!this.playRequested &&
|
|
50146
|
+
!this.playing &&
|
|
50147
|
+
!this.container.buffering &&
|
|
50148
|
+
!this.hasFatalError &&
|
|
50149
|
+
!this.options.disableMediaControl;
|
|
50150
|
+
if (show) {
|
|
50160
50151
|
this.showPlayButton();
|
|
50161
50152
|
}
|
|
50162
50153
|
else {
|
|
@@ -50164,59 +50155,42 @@ class Poster extends UIContainerPlugin {
|
|
|
50164
50155
|
}
|
|
50165
50156
|
}
|
|
50166
50157
|
showPlayButton() {
|
|
50167
|
-
|
|
50168
|
-
|
|
50169
|
-
}
|
|
50170
|
-
if (this.hasFatalError && !this.options.disableErrorScreen) {
|
|
50171
|
-
return;
|
|
50172
|
-
}
|
|
50173
|
-
this.$playButton?.show();
|
|
50158
|
+
trace(`${T$8} showPlayButton`);
|
|
50159
|
+
this.$el.find('#poster-play').show();
|
|
50174
50160
|
this.$el.addClass('clickable');
|
|
50175
50161
|
this.container.$el.addClass('container-with-poster-clickable');
|
|
50176
50162
|
}
|
|
50177
50163
|
hidePlayButton() {
|
|
50178
|
-
|
|
50164
|
+
trace(`${T$8} hidePlayButton`);
|
|
50165
|
+
this.$el.find('#poster-play').hide();
|
|
50179
50166
|
this.$el.removeClass('clickable');
|
|
50180
50167
|
}
|
|
50181
|
-
clicked() {
|
|
50182
|
-
trace(`${T$8} clicked
|
|
50183
|
-
|
|
50184
|
-
|
|
50185
|
-
|
|
50186
|
-
|
|
50168
|
+
clicked(e) {
|
|
50169
|
+
trace(`${T$8} clicked`);
|
|
50170
|
+
e.preventDefault();
|
|
50171
|
+
e.stopPropagation();
|
|
50172
|
+
if (this.options.chromeless && !this.options.allowUserInteraction) {
|
|
50173
|
+
return;
|
|
50174
|
+
}
|
|
50187
50175
|
// Let "click_to_pause" plugin handle click event if media has started playing
|
|
50188
|
-
if (!this.
|
|
50189
|
-
|
|
50190
|
-
|
|
50191
|
-
|
|
50192
|
-
this.container.playback.consent();
|
|
50193
|
-
this.container.playback.play();
|
|
50194
|
-
}
|
|
50176
|
+
if (!this.playing) {
|
|
50177
|
+
this.playRequested = true;
|
|
50178
|
+
this.update();
|
|
50179
|
+
this.container.play();
|
|
50195
50180
|
}
|
|
50196
|
-
return false;
|
|
50197
50181
|
}
|
|
50198
50182
|
shouldHideOnPlay() {
|
|
50199
50183
|
// Audio broadcasts should keep the poster up; video should hide poster while playing.
|
|
50200
50184
|
return !this.container.playback.isAudioOnly;
|
|
50201
50185
|
}
|
|
50202
50186
|
update() {
|
|
50203
|
-
trace(`${T$8} update
|
|
50204
|
-
|
|
50205
|
-
});
|
|
50206
|
-
if (!this.shouldRender) {
|
|
50207
|
-
return;
|
|
50208
|
-
}
|
|
50209
|
-
const showPlayButton = !this.playRequested &&
|
|
50210
|
-
!this.hasStartedPlaying &&
|
|
50211
|
-
!this.container.buffering;
|
|
50212
|
-
this.updatePlayButton(showPlayButton);
|
|
50187
|
+
trace(`${T$8} update`);
|
|
50188
|
+
this.updatePlayButton();
|
|
50213
50189
|
this.updatePoster();
|
|
50214
50190
|
}
|
|
50215
50191
|
updatePoster() {
|
|
50216
|
-
trace(`${T$8} updatePoster
|
|
50217
|
-
|
|
50218
|
-
});
|
|
50219
|
-
if (!this.hasStartedPlaying) {
|
|
50192
|
+
trace(`${T$8} updatePoster`);
|
|
50193
|
+
if (!this.playing) {
|
|
50220
50194
|
this.showPoster();
|
|
50221
50195
|
}
|
|
50222
50196
|
else {
|
|
@@ -50228,9 +50202,7 @@ class Poster extends UIContainerPlugin {
|
|
|
50228
50202
|
this.$el.show();
|
|
50229
50203
|
}
|
|
50230
50204
|
hidePoster() {
|
|
50231
|
-
trace(`${T$8} hidePoster
|
|
50232
|
-
shouldHideOnPlay: this.shouldHideOnPlay(),
|
|
50233
|
-
});
|
|
50205
|
+
trace(`${T$8} hidePoster`);
|
|
50234
50206
|
if (!this.options.disableMediaControl) {
|
|
50235
50207
|
this.container.enableMediaControl();
|
|
50236
50208
|
}
|
|
@@ -50246,27 +50218,24 @@ class Poster extends UIContainerPlugin {
|
|
|
50246
50218
|
return this;
|
|
50247
50219
|
}
|
|
50248
50220
|
this.$el.html(Poster.template());
|
|
50249
|
-
const
|
|
50250
|
-
if (
|
|
50251
|
-
const posterUrl = this.options.poster.url || this.options.poster;
|
|
50252
|
-
this.$el.css({ 'background-image': 'url(' + posterUrl + ')' });
|
|
50253
|
-
}
|
|
50254
|
-
else if (this.options.poster) {
|
|
50221
|
+
const isCustomPoster = this.options.poster?.custom !== undefined;
|
|
50222
|
+
if (isCustomPoster) {
|
|
50255
50223
|
this.$el.css({ background: this.options.poster.custom });
|
|
50256
50224
|
}
|
|
50225
|
+
else {
|
|
50226
|
+
const posterUrl = typeof this.options.poster === 'string'
|
|
50227
|
+
? this.options.poster
|
|
50228
|
+
: this.options.poster?.url;
|
|
50229
|
+
if (posterUrl) {
|
|
50230
|
+
this.$el.css({ 'background-image': 'url(' + posterUrl + ')' });
|
|
50231
|
+
}
|
|
50232
|
+
}
|
|
50257
50233
|
this.container.$el.removeClass('container-with-poster-clickable');
|
|
50258
50234
|
this.container.$el.append(this.el);
|
|
50259
|
-
this.$
|
|
50260
|
-
this
|
|
50261
|
-
|
|
50262
|
-
this.$playWrapper.append(this.$playButton);
|
|
50263
|
-
this.$playButton.append(playIcon);
|
|
50264
|
-
if (this.options.autoPlay) {
|
|
50265
|
-
this.$playButton.hide();
|
|
50235
|
+
this.$el.find('#poster-play').append(playIcon);
|
|
50236
|
+
if (this.options.autoPlay || this.isNoOp) {
|
|
50237
|
+
this.$el.find('#poster-play').hide();
|
|
50266
50238
|
}
|
|
50267
|
-
this.$playButton.addClass('poster-icon');
|
|
50268
|
-
this.$playButton.attr('data-poster', '');
|
|
50269
|
-
this.update();
|
|
50270
50239
|
return this;
|
|
50271
50240
|
}
|
|
50272
50241
|
/**
|
|
@@ -51997,12 +51966,31 @@ function requireParseSrt () {
|
|
|
51997
51966
|
var parseSrtExports = requireParseSrt();
|
|
51998
51967
|
const parseSRT = /*@__PURE__*/getDefaultExportFromCjs$1(parseSrtExports);
|
|
51999
51968
|
|
|
52000
|
-
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
|
+
}
|
|
52001
51983
|
|
|
52002
51984
|
const T$1 = 'plugins.thumbnails';
|
|
52003
51985
|
/**
|
|
52004
51986
|
* `PLUGIN` that displays the thumbnails of the video when available.
|
|
52005
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
|
+
*
|
|
52006
51994
|
* @example
|
|
52007
51995
|
* ```ts
|
|
52008
51996
|
* import { Thumbnails } from '@gcorevideo/player'
|
|
@@ -52023,19 +52011,15 @@ const T$1 = 'plugins.thumbnails';
|
|
|
52023
52011
|
* ```
|
|
52024
52012
|
*/
|
|
52025
52013
|
class Thumbnails extends UICorePlugin {
|
|
52026
|
-
|
|
52027
|
-
_$backdrop = null;
|
|
52028
|
-
$container = null;
|
|
52029
|
-
$img = null;
|
|
52030
|
-
_$carousel = null;
|
|
52031
|
-
$textThumbnail = null;
|
|
52032
|
-
_$backdropCarouselImgs = [];
|
|
52014
|
+
$backdropCarouselImgs = [];
|
|
52033
52015
|
spriteSheetHeight = 0;
|
|
52034
|
-
|
|
52035
|
-
|
|
52036
|
-
|
|
52037
|
-
|
|
52038
|
-
|
|
52016
|
+
spriteSheetWidth = 0;
|
|
52017
|
+
hoverPosition = 0;
|
|
52018
|
+
showing = false;
|
|
52019
|
+
thumbsLoaded = false;
|
|
52020
|
+
spotlightHeight = 0;
|
|
52021
|
+
backdropHeight = 0;
|
|
52022
|
+
thumbs = [];
|
|
52039
52023
|
/**
|
|
52040
52024
|
* @internal
|
|
52041
52025
|
*/
|
|
@@ -52053,10 +52037,14 @@ class Thumbnails extends UICorePlugin {
|
|
|
52053
52037
|
*/
|
|
52054
52038
|
get attributes() {
|
|
52055
52039
|
return {
|
|
52056
|
-
class:
|
|
52040
|
+
class: 'scrub-thumbnails',
|
|
52057
52041
|
};
|
|
52058
52042
|
}
|
|
52059
52043
|
static template = tmpl(pluginHtml);
|
|
52044
|
+
constructor(core) {
|
|
52045
|
+
super(core);
|
|
52046
|
+
this.backdropHeight = this.options.thumbnails?.backdropHeight ?? 0;
|
|
52047
|
+
}
|
|
52060
52048
|
/*
|
|
52061
52049
|
* Helper to build the "thumbs" property for a sprite sheet.
|
|
52062
52050
|
*
|
|
@@ -52068,25 +52056,21 @@ class Thumbnails extends UICorePlugin {
|
|
|
52068
52056
|
* timeInterval- The interval (in seconds) between the thumbnails.
|
|
52069
52057
|
* startTime- The time (in seconds) that the first thumbnail represents. (defaults to 0)
|
|
52070
52058
|
*/
|
|
52071
|
-
|
|
52072
|
-
buildSpriteConfig(vtt, spriteSheetUrl) {
|
|
52059
|
+
buildSpriteConfig(vtt, baseUrl) {
|
|
52073
52060
|
const thumbs = [];
|
|
52074
|
-
// let coor: string[] = [];
|
|
52075
52061
|
for (const vt of vtt) {
|
|
52076
52062
|
const el = vt.text;
|
|
52077
|
-
// if (el && el.search(/\d*,\d*,\d*,\d*/g) > -1) {
|
|
52078
|
-
// el = el.match(/\d*,\d*,\d*,\d*/g)[0];
|
|
52079
|
-
// coor = el.split(',');
|
|
52080
|
-
// }
|
|
52081
52063
|
if (el) {
|
|
52082
|
-
const m = el.match(/xywh
|
|
52064
|
+
const m = el.match(/(\w+)#xywh=(\d+,\d+,\d+,\d+)/);
|
|
52083
52065
|
if (m) {
|
|
52084
|
-
const coor = m[
|
|
52066
|
+
const coor = m[2].split(',');
|
|
52085
52067
|
const w = parseInt(coor[2], 10);
|
|
52086
52068
|
const h = parseInt(coor[3], 10);
|
|
52087
52069
|
if (w > 0 && h > 0) {
|
|
52088
52070
|
thumbs.push({
|
|
52089
|
-
|
|
52071
|
+
// TODO handle relative URLs
|
|
52072
|
+
// url: new URL(m[0], baseUrl).toString(),
|
|
52073
|
+
url: baseUrl,
|
|
52090
52074
|
time: vt.start,
|
|
52091
52075
|
w,
|
|
52092
52076
|
h,
|
|
@@ -52099,242 +52083,147 @@ class Thumbnails extends UICorePlugin {
|
|
|
52099
52083
|
}
|
|
52100
52084
|
return thumbs;
|
|
52101
52085
|
}
|
|
52102
|
-
// TODO check if seek enabled
|
|
52103
52086
|
/**
|
|
52104
52087
|
* @internal
|
|
52105
52088
|
*/
|
|
52106
52089
|
bindEvents() {
|
|
52107
|
-
this.listenToOnce(this.core, Events$1.CORE_READY, this.
|
|
52108
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this._onMouseMove);
|
|
52109
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this._onMouseLeave);
|
|
52110
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_RENDERED, this._init);
|
|
52111
|
-
this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, this._onMediaControlContainerChanged);
|
|
52090
|
+
this.listenToOnce(this.core, Events$1.CORE_READY, this.onCoreReady);
|
|
52112
52091
|
}
|
|
52113
|
-
|
|
52114
|
-
|
|
52115
|
-
this.stopListening(this._oldContainer, Events$1.CONTAINER_TIMEUPDATE, this._renderPlugin);
|
|
52116
|
-
}
|
|
52117
|
-
this._oldContainer = this.core.mediaControl.container;
|
|
52118
|
-
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);
|
|
52119
52094
|
}
|
|
52120
|
-
|
|
52121
|
-
|
|
52122
|
-
|
|
52123
|
-
|
|
52124
|
-
|
|
52125
|
-
|
|
52126
|
-
|
|
52127
|
-
|
|
52128
|
-
}
|
|
52129
|
-
catch (error) {
|
|
52130
|
-
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();
|
|
52131
52103
|
return;
|
|
52132
52104
|
}
|
|
52133
|
-
|
|
52134
|
-
|
|
52135
|
-
|
|
52136
|
-
|
|
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`);
|
|
52137
52109
|
this.destroy();
|
|
52138
52110
|
return;
|
|
52139
52111
|
}
|
|
52112
|
+
this.spotlightHeight = this.options.thumbnails?.spotlightHeight ?? 0;
|
|
52140
52113
|
this.loadSpriteSheet(spriteSheet).then(() => {
|
|
52141
|
-
this.
|
|
52142
|
-
this.
|
|
52143
|
-
|
|
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();
|
|
52144
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));
|
|
52145
52124
|
}
|
|
52146
52125
|
async loadSpriteSheet(spriteSheetUrl) {
|
|
52147
|
-
return
|
|
52148
|
-
|
|
52149
|
-
|
|
52150
|
-
this.spriteSheetHeight = img.height;
|
|
52151
|
-
resolve();
|
|
52152
|
-
};
|
|
52153
|
-
img.onerror = reject;
|
|
52154
|
-
img.src = spriteSheetUrl;
|
|
52126
|
+
return loadImageDimensions(spriteSheetUrl).then(({ height, width }) => {
|
|
52127
|
+
this.spriteSheetHeight = height;
|
|
52128
|
+
this.spriteSheetWidth = width;
|
|
52155
52129
|
});
|
|
52156
52130
|
}
|
|
52157
|
-
|
|
52158
|
-
this.
|
|
52131
|
+
onContainerChanged(container) {
|
|
52132
|
+
this.bindContainerEvents(container);
|
|
52159
52133
|
}
|
|
52160
|
-
|
|
52161
|
-
if (!this.
|
|
52162
|
-
//
|
|
52134
|
+
init() {
|
|
52135
|
+
if (!this.thumbsLoaded) {
|
|
52136
|
+
// init() will be called when the thumbs are loaded,
|
|
52163
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)
|
|
52164
52138
|
return;
|
|
52165
52139
|
}
|
|
52166
52140
|
// Init the backdropCarousel as array to keep reference of thumbnail images
|
|
52167
|
-
this
|
|
52168
|
-
|
|
52169
|
-
this.
|
|
52170
|
-
this.
|
|
52171
|
-
|
|
52172
|
-
|
|
52173
|
-
|
|
52174
|
-
|
|
52175
|
-
|
|
52176
|
-
|
|
52177
|
-
|
|
52178
|
-
}
|
|
52179
|
-
|
|
52180
|
-
|
|
52181
|
-
this.
|
|
52182
|
-
this.
|
|
52183
|
-
}
|
|
52184
|
-
|
|
52185
|
-
|
|
52186
|
-
|
|
52187
|
-
|
|
52188
|
-
// t2: typeof arguments[1],
|
|
52189
|
-
// });
|
|
52190
|
-
this._calculateHoverPosition(e);
|
|
52191
|
-
this._show = true;
|
|
52192
|
-
this._renderPlugin();
|
|
52193
|
-
}
|
|
52194
|
-
_onMouseLeave() {
|
|
52195
|
-
this._show = false;
|
|
52196
|
-
this._renderPlugin();
|
|
52197
|
-
}
|
|
52198
|
-
_calculateHoverPosition(e) {
|
|
52199
|
-
const offset = getPageX(e) - this.core.mediaControl.$seekBarContainer.offset().left;
|
|
52200
|
-
// proportion into the seek bar that the mouse is hovered over 0-1
|
|
52201
|
-
this._hoverPosition = Math.min(1, Math.max(offset / this.core.mediaControl.$seekBarContainer.width(), 0));
|
|
52202
|
-
}
|
|
52203
|
-
// private _buildThumbsFromOptions() {
|
|
52204
|
-
// const thumbs = this._thumbs;
|
|
52205
|
-
// const promises = thumbs.map((thumb) => {
|
|
52206
|
-
// return this._addThumbFromSrc(thumb);
|
|
52207
|
-
// });
|
|
52208
|
-
// return Promise.all(promises);
|
|
52209
|
-
// }
|
|
52210
|
-
// private _addThumbFromSrc(thumbSrc) {
|
|
52211
|
-
// return new Promise((resolve, reject) => {
|
|
52212
|
-
// const img = new Image();
|
|
52213
|
-
// img.onload = () => {
|
|
52214
|
-
// resolve(img);
|
|
52215
|
-
// };
|
|
52216
|
-
// img.onerror = reject;
|
|
52217
|
-
// img.src = thumbSrc.url;
|
|
52218
|
-
// }).then((img) => {
|
|
52219
|
-
// const startTime = thumbSrc.time;
|
|
52220
|
-
// // determine the thumb index
|
|
52221
|
-
// let index = null;
|
|
52222
|
-
// this._thumbs.some((thumb, i) => {
|
|
52223
|
-
// if (startTime < thumb.time) {
|
|
52224
|
-
// index = i;
|
|
52225
|
-
// return true;
|
|
52226
|
-
// }
|
|
52227
|
-
// return false;
|
|
52228
|
-
// });
|
|
52229
|
-
// if (index === null) {
|
|
52230
|
-
// index = this._thumbs.length;
|
|
52231
|
-
// }
|
|
52232
|
-
// const next = index < this._thumbs.length ? this._thumbs[index] : null;
|
|
52233
|
-
// const prev = index > 0 ? this._thumbs[index - 1] : null;
|
|
52234
|
-
// if (prev) {
|
|
52235
|
-
// // update the duration of the previous thumbnail
|
|
52236
|
-
// prev.duration = startTime - prev.time;
|
|
52237
|
-
// }
|
|
52238
|
-
// // the duration this thumb lasts for
|
|
52239
|
-
// // if it is the last thumb then duration will be null
|
|
52240
|
-
// const duration = next ? next.time - thumbSrc.time : null;
|
|
52241
|
-
// const imageW = img.width;
|
|
52242
|
-
// const imageH = img.height;
|
|
52243
|
-
// const thumb = {
|
|
52244
|
-
// imageW: imageW, // actual width of image
|
|
52245
|
-
// imageH: imageH, // actual height of image
|
|
52246
|
-
// x: thumbSrc.x || 0, // x coord in image of sprite
|
|
52247
|
-
// y: thumbSrc.y || 0, // y coord in image of sprite
|
|
52248
|
-
// w: thumbSrc.w || imageW, // width of sprite
|
|
52249
|
-
// h: thumbSrc.h || imageH, // height of sprite
|
|
52250
|
-
// url: thumbSrc.url,
|
|
52251
|
-
// time: startTime, // time this thumb represents
|
|
52252
|
-
// duration: duration, // how long (from time) this thumb represents
|
|
52253
|
-
// src: thumbSrc
|
|
52254
|
-
// };
|
|
52255
|
-
// this._thumbs.splice(index, 0, thumb);
|
|
52256
|
-
// return thumb;
|
|
52257
|
-
// });
|
|
52258
|
-
// }
|
|
52141
|
+
this.$backdropCarouselImgs = [];
|
|
52142
|
+
this.fixElements();
|
|
52143
|
+
this.loadBackdrop();
|
|
52144
|
+
this.update();
|
|
52145
|
+
}
|
|
52146
|
+
mount() {
|
|
52147
|
+
// insert after the background TODO figure out why
|
|
52148
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52149
|
+
mediaControl.$el.find('.seek-time').css('bottom', 56); // TODO check
|
|
52150
|
+
// TODO use mediaControl.mount? into the `layer`
|
|
52151
|
+
mediaControl.$el.append(this.$el);
|
|
52152
|
+
}
|
|
52153
|
+
onMouseMoveSeekbar(_, pos) {
|
|
52154
|
+
this.hoverPosition = pos;
|
|
52155
|
+
this.showing = true;
|
|
52156
|
+
this.update();
|
|
52157
|
+
}
|
|
52158
|
+
onMouseLeave() {
|
|
52159
|
+
this.showing = false;
|
|
52160
|
+
this.update();
|
|
52161
|
+
}
|
|
52259
52162
|
// builds a dom element which represents the thumbnail
|
|
52260
|
-
// scaled to the
|
|
52261
|
-
|
|
52163
|
+
// scaled to the given height
|
|
52164
|
+
buildThumbImage(thumb, height) {
|
|
52262
52165
|
const scaleFactor = height / thumb.h;
|
|
52263
|
-
|
|
52264
|
-
|
|
52265
|
-
|
|
52266
|
-
|
|
52267
|
-
|
|
52268
|
-
|
|
52269
|
-
|
|
52270
|
-
}
|
|
52271
|
-
this.$container.css('width', thumb.w * scaleFactor);
|
|
52272
|
-
this.$container.css('height', height);
|
|
52273
|
-
this.$img.css({
|
|
52274
|
-
height: this.spriteSheetHeight * scaleFactor,
|
|
52275
|
-
left: -1 * thumb.x * scaleFactor,
|
|
52276
|
-
top: -1 * thumb.y * scaleFactor,
|
|
52166
|
+
const $container = $('<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`,
|
|
52277
52173
|
});
|
|
52278
|
-
|
|
52279
|
-
this.$container.append(this.$img);
|
|
52280
|
-
}
|
|
52281
|
-
return this.$container;
|
|
52174
|
+
return $container;
|
|
52282
52175
|
}
|
|
52283
|
-
|
|
52284
|
-
if (!this.
|
|
52176
|
+
loadBackdrop() {
|
|
52177
|
+
if (!this.backdropHeight) {
|
|
52285
52178
|
// disabled
|
|
52286
52179
|
return;
|
|
52287
52180
|
}
|
|
52288
52181
|
// append each of the thumbnails to the backdrop carousel
|
|
52289
|
-
const $carousel = this.
|
|
52290
|
-
for (const thumb of this.
|
|
52291
|
-
const $img = this.
|
|
52292
|
-
// Keep reference to thumbnail
|
|
52293
|
-
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);
|
|
52294
52187
|
// Add thumbnail to DOM
|
|
52295
52188
|
$carousel.append($img);
|
|
52296
52189
|
}
|
|
52297
52190
|
}
|
|
52298
52191
|
setText(time) {
|
|
52299
|
-
|
|
52300
|
-
|
|
52301
|
-
|
|
52192
|
+
const clips = this.core.getPlugin('clips');
|
|
52193
|
+
if (clips) {
|
|
52194
|
+
const txt = clips.getText(time);
|
|
52195
|
+
this.$el.find('#thumbnails-text').text(txt ?? '');
|
|
52302
52196
|
}
|
|
52303
52197
|
}
|
|
52304
52198
|
// calculate how far along the carousel should currently be slid
|
|
52305
52199
|
// depending on where the user is hovering on the progress bar
|
|
52306
|
-
|
|
52307
|
-
|
|
52308
|
-
backdropHeight: this._getOptions().backdropHeight,
|
|
52309
|
-
});
|
|
52310
|
-
if (!this._getOptions().backdropHeight) {
|
|
52200
|
+
updateCarousel() {
|
|
52201
|
+
if (!this.backdropHeight) {
|
|
52311
52202
|
// disabled
|
|
52312
52203
|
return;
|
|
52313
52204
|
}
|
|
52314
|
-
const
|
|
52315
|
-
const videoDuration =
|
|
52316
|
-
const startTimeOffset =
|
|
52205
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52206
|
+
const videoDuration = mediaControl.container.getDuration();
|
|
52207
|
+
const startTimeOffset = mediaControl.container.getStartTimeOffset();
|
|
52317
52208
|
// the time into the video at the current hover position
|
|
52318
|
-
const hoverTime = startTimeOffset + videoDuration * hoverPosition;
|
|
52319
|
-
const
|
|
52320
|
-
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');
|
|
52321
52213
|
const carouselWidth = $carousel.width();
|
|
52322
52214
|
// slide the carousel so that the image on the carousel that is above where the person
|
|
52323
52215
|
// is hovering maps to that position in time.
|
|
52324
52216
|
// Thumbnails may not be distributed at even times along the video
|
|
52325
|
-
const thumbs = this._thumbs;
|
|
52326
52217
|
// assuming that each thumbnail has the same width
|
|
52327
|
-
const thumbWidth = carouselWidth / thumbs.length;
|
|
52218
|
+
const thumbWidth = carouselWidth / this.thumbs.length;
|
|
52328
52219
|
// determine which thumbnail applies to the current time
|
|
52329
|
-
const thumbIndex = this.
|
|
52330
|
-
const thumb = thumbs[thumbIndex];
|
|
52331
|
-
|
|
52332
|
-
|
|
52333
|
-
|
|
52334
|
-
|
|
52335
|
-
|
|
52336
|
-
thumbDuration = Math.max(videoDuration + startTimeOffset - thumb.time, 0);
|
|
52337
|
-
}
|
|
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);
|
|
52338
52227
|
// determine how far accross that thumbnail we are
|
|
52339
52228
|
const timeIntoThumb = hoverTime - thumb.time;
|
|
52340
52229
|
const positionInThumb = timeIntoThumb / thumbDuration;
|
|
@@ -52342,12 +52231,12 @@ class Thumbnails extends UICorePlugin {
|
|
|
52342
52231
|
// now calculate the position along carousel that we want to be above the hover position
|
|
52343
52232
|
const xCoordInCarousel = thumbIndex * thumbWidth + xCoordInThumb;
|
|
52344
52233
|
// and finally the position of the carousel when the hover position is taken in to consideration
|
|
52345
|
-
const carouselXCoord = xCoordInCarousel - hoverPosition * backdropWidth;
|
|
52346
|
-
$carousel.css('left', -carouselXCoord);
|
|
52347
|
-
const maxOpacity = this.
|
|
52348
|
-
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;
|
|
52349
52238
|
// now update the transparencies so that they fade in around the active one
|
|
52350
|
-
for (let i = 0; i < thumbs.length; i++) {
|
|
52239
|
+
for (let i = 0; i < this.thumbs.length; i++) {
|
|
52351
52240
|
const thumbXCoord = thumbWidth * i;
|
|
52352
52241
|
let distance = thumbXCoord - xCoordInCarousel;
|
|
52353
52242
|
if (distance < 0) {
|
|
@@ -52359,47 +52248,43 @@ class Thumbnails extends UICorePlugin {
|
|
|
52359
52248
|
}
|
|
52360
52249
|
// fade over the width of 2 thumbnails
|
|
52361
52250
|
const opacity = Math.max(maxOpacity - Math.abs(distance) / (2 * thumbWidth), minOpacity);
|
|
52362
|
-
this
|
|
52251
|
+
this.$backdropCarouselImgs[i].css('opacity', opacity);
|
|
52363
52252
|
}
|
|
52364
52253
|
}
|
|
52365
|
-
|
|
52366
|
-
|
|
52367
|
-
spotlightHeight: this._getOptions().spotlightHeight,
|
|
52368
|
-
});
|
|
52369
|
-
if (!this._getOptions().spotlightHeight) {
|
|
52254
|
+
updateSpotlightThumb() {
|
|
52255
|
+
if (!this.spotlightHeight) {
|
|
52370
52256
|
// disabled
|
|
52371
52257
|
return;
|
|
52372
52258
|
}
|
|
52373
|
-
const
|
|
52374
|
-
const videoDuration =
|
|
52259
|
+
const mediaControl = this.core.getPlugin('media_control');
|
|
52260
|
+
const videoDuration = mediaControl.container.getDuration();
|
|
52375
52261
|
// the time into the video at the current hover position
|
|
52376
|
-
const startTimeOffset =
|
|
52377
|
-
const hoverTime = startTimeOffset + videoDuration * hoverPosition;
|
|
52262
|
+
const startTimeOffset = mediaControl.container.getStartTimeOffset();
|
|
52263
|
+
const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
|
|
52378
52264
|
this.setText(hoverTime);
|
|
52379
52265
|
// determine which thumbnail applies to the current time
|
|
52380
|
-
const thumbIndex = this.
|
|
52381
|
-
const thumb = this.
|
|
52266
|
+
const thumbIndex = this.getThumbIndexForTime(hoverTime);
|
|
52267
|
+
const thumb = this.thumbs[thumbIndex];
|
|
52382
52268
|
// update thumbnail
|
|
52383
|
-
const $spotlight = this.
|
|
52269
|
+
const $spotlight = this.$el.find('#thumbnails-spotlight');
|
|
52384
52270
|
$spotlight.empty();
|
|
52385
|
-
$spotlight.append(this.
|
|
52271
|
+
$spotlight.append(this.buildThumbImage(thumb, this.spotlightHeight));
|
|
52386
52272
|
const elWidth = this.$el.width();
|
|
52387
52273
|
const thumbWidth = $spotlight.width();
|
|
52388
52274
|
const thumbHeight = $spotlight.height();
|
|
52389
|
-
let spotlightXPos = elWidth * hoverPosition - thumbWidth / 2;
|
|
52390
52275
|
// adjust so the entire thumbnail is always visible
|
|
52391
|
-
spotlightXPos = Math.max(Math.min(
|
|
52276
|
+
const spotlightXPos = Math.max(Math.min(elWidth * this.hoverPosition - thumbWidth / 2, elWidth - thumbWidth), 0);
|
|
52392
52277
|
$spotlight.css('left', spotlightXPos);
|
|
52393
|
-
this.$
|
|
52394
|
-
|
|
52395
|
-
|
|
52278
|
+
const $textThumbnail = this.$el.find('#thumbnails-text');
|
|
52279
|
+
$textThumbnail.css('left', spotlightXPos);
|
|
52280
|
+
$textThumbnail.css('width', thumbWidth);
|
|
52281
|
+
$textThumbnail.css('bottom', thumbHeight + 1);
|
|
52396
52282
|
}
|
|
52397
52283
|
// returns the thumbnail which represents a time in the video
|
|
52398
52284
|
// or null if there is no thumbnail that can represent the time
|
|
52399
|
-
|
|
52400
|
-
|
|
52401
|
-
|
|
52402
|
-
const thumb = thumbs[i];
|
|
52285
|
+
getThumbIndexForTime(time) {
|
|
52286
|
+
for (let i = this.thumbs.length - 1; i >= 0; i--) {
|
|
52287
|
+
const thumb = this.thumbs[i];
|
|
52403
52288
|
if (thumb.time <= time) {
|
|
52404
52289
|
return i;
|
|
52405
52290
|
}
|
|
@@ -52407,37 +52292,48 @@ class Thumbnails extends UICorePlugin {
|
|
|
52407
52292
|
// stretch the first thumbnail back to the start
|
|
52408
52293
|
return 0;
|
|
52409
52294
|
}
|
|
52410
|
-
|
|
52411
|
-
|
|
52412
|
-
show: this._show,
|
|
52413
|
-
thumbsLoaded: this._thumbsLoaded,
|
|
52414
|
-
thumbs: this._thumbs.length,
|
|
52415
|
-
});
|
|
52416
|
-
if (!this._thumbsLoaded) {
|
|
52295
|
+
update() {
|
|
52296
|
+
if (!this.thumbsLoaded) {
|
|
52417
52297
|
return;
|
|
52418
52298
|
}
|
|
52419
|
-
if (this.
|
|
52299
|
+
if (this.showing && this.thumbs.length > 0) {
|
|
52300
|
+
this.updateCarousel();
|
|
52301
|
+
this.updateSpotlightThumb();
|
|
52420
52302
|
this.$el.removeClass('hidden');
|
|
52421
|
-
this._updateCarousel();
|
|
52422
|
-
this._updateSpotlightThumb();
|
|
52423
52303
|
}
|
|
52424
52304
|
else {
|
|
52425
52305
|
this.$el.addClass('hidden');
|
|
52426
52306
|
}
|
|
52427
52307
|
}
|
|
52428
|
-
|
|
52429
|
-
|
|
52430
|
-
this
|
|
52431
|
-
|
|
52432
|
-
|
|
52433
|
-
|
|
52434
|
-
|
|
52435
|
-
|
|
52436
|
-
|
|
52437
|
-
|
|
52438
|
-
|
|
52308
|
+
fixElements() {
|
|
52309
|
+
const $spotlight = this.$el.find('#thumbnails-spotlight');
|
|
52310
|
+
if (this.spotlightHeight) {
|
|
52311
|
+
$spotlight.css('height', this.spotlightHeight);
|
|
52312
|
+
}
|
|
52313
|
+
else {
|
|
52314
|
+
$spotlight.remove();
|
|
52315
|
+
}
|
|
52316
|
+
const $backdrop = this.$el.find('#thumbnails-backdrop');
|
|
52317
|
+
if (this.backdropHeight) {
|
|
52318
|
+
$backdrop.css('height', this.backdropHeight);
|
|
52319
|
+
}
|
|
52320
|
+
else {
|
|
52321
|
+
$backdrop.remove();
|
|
52322
|
+
}
|
|
52323
|
+
this.mount();
|
|
52324
|
+
}
|
|
52325
|
+
get shouldRender() {
|
|
52326
|
+
return (this.options.thumbnails &&
|
|
52327
|
+
this.options.thumbnails.sprite &&
|
|
52328
|
+
this.options.thumbnails.vtt);
|
|
52329
|
+
}
|
|
52330
|
+
render() {
|
|
52331
|
+
if (!this.shouldRender) {
|
|
52332
|
+
return this;
|
|
52333
|
+
}
|
|
52334
|
+
this.$el.html(Thumbnails.template());
|
|
52439
52335
|
this.$el.addClass('hidden');
|
|
52440
|
-
this
|
|
52336
|
+
return this;
|
|
52441
52337
|
}
|
|
52442
52338
|
}
|
|
52443
52339
|
|