diff --git a/config.js b/config.js
index f042ad2e0..69ea0e060 100644
--- a/config.js
+++ b/config.js
@@ -41,6 +41,8 @@ const config = {
PINNED_URI_2: process.env.PINNED_URI_2,
PINNED_LABEL_2: process.env.PINNED_LABEL_2,
KNOWN_APP_DOMAINS: process.env.KNOWN_APP_DOMAINS ? process.env.KNOWN_APP_DOMAINS.split(',') : [],
+ CHANNEL_THUMBNAIL_FALLBACK: process.env.CHANNEL_THUMBNAIL_FALLBACK,
+ STREAM_THUMBNAIL_FALLBACK: process.env.STREAM_THUMBNAIL_FALLBACK,
};
config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`;
diff --git a/package.json b/package.json
index e7414cc5d..acd3d2e3f 100644
--- a/package.json
+++ b/package.json
@@ -53,8 +53,9 @@
"electron-updater": "^4.2.4",
"express": "^4.17.1",
"if-env": "^1.0.4",
- "remark-breaks": "^2.0.1",
- "remove-markdown": "^0.3.0",
+ "remove-markdown": "^0.3.0",
+ "react-image": "^4.0.3",
+ "remark-breaks": "^2.0.1",
"tempy": "^0.6.0",
"videojs-logo": "^2.1.4"
},
@@ -205,6 +206,7 @@
"video.js": "^7.10.1",
"videojs-contrib-quality-levels": "^2.0.9",
"videojs-event-tracking": "^1.0.1",
+ "videojs-hls-quality-selector": "^1.1.4",
"villain-react": "^1.0.9",
"wavesurfer.js": "^2.2.1",
"webpack": "^4.28.4",
diff --git a/ui/component/channelStakedIndicator/view.jsx b/ui/component/channelStakedIndicator/view.jsx
index fb548728f..6f0187f7b 100644
--- a/ui/component/channelStakedIndicator/view.jsx
+++ b/ui/component/channelStakedIndicator/view.jsx
@@ -23,10 +23,10 @@ function getChannelLevel(amount: number): number {
case amount >= 50 && amount < 250:
level = 3;
break;
- case amount >= 250 && amount < 1000:
+ case amount >= 100 && amount < 1000:
level = 4;
break;
- case amount >= 1000:
+ case amount > 1000:
level = 5;
break;
}
diff --git a/ui/component/channelThumbnail/view.jsx b/ui/component/channelThumbnail/view.jsx
index 129565416..d3af5c508 100644
--- a/ui/component/channelThumbnail/view.jsx
+++ b/ui/component/channelThumbnail/view.jsx
@@ -5,7 +5,6 @@ import classnames from 'classnames';
import Gerbil from './gerbil.png';
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
import ChannelStakedIndicator from 'component/channelStakedIndicator';
-import { getThumbnailCdnUrl } from 'util/thumbnail';
type Props = {
thumbnail: ?string,
@@ -68,14 +67,6 @@ function ChannelThumbnail(props: Props) {
);
}
- let url = channelThumbnail;
- // @if TARGET='web'
- // Pass image urls through a compression proxy
- if (thumbnail) {
- url = getThumbnailCdnUrl({ thumbnail });
- }
- // @endif
-
return (
setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
/>
)}
@@ -100,7 +91,7 @@ function ChannelThumbnail(props: Props) {
setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
/>
)}
diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx
index cd6b6d8d9..00e0173bb 100644
--- a/ui/component/claimPreview/view.jsx
+++ b/ui/component/claimPreview/view.jsx
@@ -2,8 +2,9 @@
import type { Node } from 'react';
import React, { useEffect, forwardRef } from 'react';
import { NavLink, withRouter } from 'react-router-dom';
+import { useImage } from 'react-image';
import classnames from 'classnames';
-import { SIMPLE_SITE } from 'config';
+import { CHANNEL_THUMBNAIL_FALLBACK, STREAM_THUMBNAIL_FALLBACK, SIMPLE_SITE } from 'config';
import { parseURI, convertToShareLink } from 'lbry-redux';
import { openCopyLinkMenu } from 'util/context-menu';
import { formatLbryUrlForWeb } from 'util/url';
@@ -188,6 +189,11 @@ const ClaimPreview = forwardRef
((props: Props, ref: any) => {
// Weird placement warning
// Make sure this happens after we figure out if this claim needs to be hidden
const thumbnailUrl = useGetThumbnail(contentUri, claim, streamingUrl, getFile, shouldHide);
+ const isStream = claim && claim.value_type === 'stream';
+ const { src: thumbnailUrlWithFallback } = useImage({
+ srcList: [thumbnailUrl, isStream ? STREAM_THUMBNAIL_FALLBACK : CHANNEL_THUMBNAIL_FALLBACK],
+ useSuspense: false,
+ });
function handleContextMenu(e) {
// @if TARGET='app'
@@ -282,7 +288,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => {
<>
{!pending ? (
-
+
{/* @if TARGET='app' */}
{claim && (
@@ -298,7 +304,7 @@ const ClaimPreview = forwardRef
((props: Props, ref: any) => {
) : (
-
+
)}
>
)}
diff --git a/ui/component/common/icon-custom.jsx b/ui/component/common/icon-custom.jsx
index 049e80325..5f9c9c8d1 100644
--- a/ui/component/common/icon-custom.jsx
+++ b/ui/component/common/icon-custom.jsx
@@ -1100,4 +1100,29 @@ export const icons = {
),
+ [ICONS.MOVIES]: (props: CustomProps) => (
+
+ ),
};
diff --git a/ui/component/fileThumbnail/view.jsx b/ui/component/fileThumbnail/view.jsx
index ad818c19c..8ee1d4c5e 100644
--- a/ui/component/fileThumbnail/view.jsx
+++ b/ui/component/fileThumbnail/view.jsx
@@ -46,9 +46,11 @@ function FileThumbnail(props: Props) {
}
// @endif
+ const cleanUrl = url ? url.replace(/'/g, "\\'") : '';
+
return (
{
- this.updatePlugin();
- });
- }
-
- updatePlugin() {
- // If there is the HLS tech
- if (this.getHls()) {
- // Show quality selector
- this._qualityButton.show();
- } else {
- // Hide quality selector
- this._qualityButton.hide();
- }
- }
-
- /**
- * Returns HLS Plugin
- *
- * @return {*} - videojs-hls-contrib plugin.
- */
- getHls() {
- return this.player.tech({ IWillNotUseThisInPlugins: true }).hls;
- }
-
- /**
- * Binds listener for quality level changes.
- */
- bindPlayerEvents() {
- this.player.qualityLevels().on('addqualitylevel', this.onAddQualityLevel.bind(this));
- }
-
- /**
- * Adds the quality menu button to the player control bar.
- */
- createQualityButton() {
- const player = this.player;
-
- this._qualityButton = new ConcreteButton(player);
-
- const placementIndex = player.controlBar.children().length - 2;
- const concreteButtonInstance = player.controlBar.addChild(
- this._qualityButton,
- { componentClass: 'qualitySelector' },
- this.config.placementIndex || placementIndex
- );
-
- concreteButtonInstance.addClass('vjs-quality-selector');
- if (!this.config.displayCurrentQuality) {
- const icon = ` ${this.config.vjsIconClass || 'vjs-icon-hd'}`;
-
- concreteButtonInstance.menuButton_.$('.vjs-icon-placeholder').className += icon;
- } else {
- this.setButtonInnerText('auto');
- }
- concreteButtonInstance.removeClass('vjs-hidden');
- }
-
- /**
- *Set inner button text.
- *
- * @param {string} text - the text to display in the button.
- */
- setButtonInnerText(text) {
- this._qualityButton.menuButton_.$('.vjs-icon-placeholder').innerHTML = text;
- }
-
- /**
- * Builds individual quality menu items.
- *
- * @param {Object} item - Individual quality menu item.
- * @return {ConcreteMenuItem} - Menu item
- */
- getQualityMenuItem(item) {
- const player = this.player;
-
- return new ConcreteMenuItem(player, item, this._qualityButton, this);
- }
-
- /**
- * Executed when a quality level is added from HLS playlist.
- */
- onAddQualityLevel() {
- const player = this.player;
- const qualityList = player.qualityLevels();
- const levels = qualityList.levels_ || [];
- const levelItems = [];
-
- for (let i = 0; i < levels.length; ++i) {
- if (
- !levelItems.filter((_existingItem) => {
- return _existingItem.item && _existingItem.item.value === levels[i].height;
- }).length
- ) {
- const levelItem = this.getQualityMenuItem.call(this, {
- label: levels[i].height + 'p',
- value: levels[i].height,
- });
-
- levelItems.push(levelItem);
- }
- }
-
- levelItems.sort((current, next) => {
- if (typeof current !== 'object' || typeof next !== 'object') {
- return -1;
- }
- if (current.item.value < next.item.value) {
- return -1;
- }
- if (current.item.value > next.item.value) {
- return 1;
- }
- return 0;
- });
-
- levelItems.push(
- this.getQualityMenuItem.call(this, {
- label: player.localize('Auto'),
- value: 'auto',
- selected: true,
- })
- );
-
- if (this._qualityButton) {
- this._qualityButton.createItems = function () {
- return levelItems;
- };
- this._qualityButton.update();
- }
- }
-
- /**
- * Sets quality (based on media height)
- *
- * @param {number} height - A number representing HLS playlist.
- */
- setQuality(height) {
- const qualityList = this.player.qualityLevels();
-
- // Set quality on plugin
- this._currentQuality = height;
-
- if (this.config.displayCurrentQuality) {
- this.setButtonInnerText(height === 'auto' ? height : `${height}p`);
- }
-
- for (let i = 0; i < qualityList.length; ++i) {
- const quality = qualityList[i];
-
- quality.enabled = quality.height === height || height === 'auto';
- }
- this._qualityButton.unpressButton();
- }
-
- /**
- * Return the current set quality or 'auto'
- *
- * @return {string} the currently set quality
- */
- getCurrentQuality() {
- return this._currentQuality || 'auto';
- }
-}
-
-/**
- * Function to invoke when the player is ready.
- *
- * This is a great place for your plugin to initialize itself. When this
- * function is called, the player will have its DOM and child components
- * in place.
- *
- * @function onPlayerReady
- * @param {Player} player
- * A Video.js player object.
- *
- * @param {Object} [options={}]
- * A plain object containing options for the plugin.
- */
-const onPlayerReady = (player, options) => {
- player.addClass('vjs-hls-quality-selector');
- player.hlsQualitySelector = new HlsQualitySelectorPlugin(player, options);
-};
-
-/**
- * A video.js plugin.
- *
- * In the plugin function, the value of `this` is a video.js `Player`
- * instance. You cannot rely on the player being in a "ready" state here,
- * depending on how the plugin is invoked. This may or may not be important
- * to you; if not, remove the wait for "ready"!
- *
- * @function hlsQualitySelector
- * @param {Object} [options={}]
- * An object of options left to the plugin author to define.
- */
-const hlsQualitySelector = function (options) {
- this.ready(() => {
- onPlayerReady(this, videojs.mergeOptions(defaults, options));
- });
-};
-
-// Register the plugin with video.js.
-registerPlugin('hlsQualitySelector', hlsQualitySelector);
-
-// Include the version number.
-hlsQualitySelector.VERSION = VERSION;
-
-export default hlsQualitySelector;
-/* eslint-enable */
diff --git a/ui/component/viewers/videoViewer/internal/plugins/videojs-hls-quality-selector/plugin.scss b/ui/component/viewers/videoViewer/internal/plugins/videojs-hls-quality-selector/plugin.scss
deleted file mode 100644
index f0e5c7c39..000000000
--- a/ui/component/viewers/videoViewer/internal/plugins/videojs-hls-quality-selector/plugin.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-// Sass for videojs-hls-quality-selector
-
-.video-js {
-
- // This class is added to the video.js element by the plugin by default.
- &.vjs-hls-quality-selector {
- display: block;
- }
-}
diff --git a/ui/component/viewers/videoViewer/internal/videojs.jsx b/ui/component/viewers/videoViewer/internal/videojs.jsx
index 796b241ec..4f2ef44ef 100644
--- a/ui/component/viewers/videoViewer/internal/videojs.jsx
+++ b/ui/component/viewers/videoViewer/internal/videojs.jsx
@@ -8,7 +8,7 @@ import 'video.js/dist/alt/video-js-cdn.min.css';
import eventTracking from 'videojs-event-tracking';
import * as OVERLAY from './overlays';
import './plugins/videojs-mobile-ui/plugin';
-import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
+import qualitySelector from 'videojs-hls-quality-selector';
import qualityLevels from 'videojs-contrib-quality-levels';
import isUserTyping from 'util/detect-typing';
@@ -102,7 +102,7 @@ if (!Object.keys(videojs.getPlugins()).includes('eventTracking')) {
}
if (!Object.keys(videojs.getPlugins()).includes('hlsQualitySelector')) {
- videojs.registerPlugin('hlsQualitySelector', hlsQualitySelector);
+ videojs.registerPlugin('hlsQualitySelector', qualitySelector);
}
if (!Object.keys(videojs.getPlugins()).includes('qualityLevels')) {
@@ -379,6 +379,11 @@ export default React.memo
(function VideoJs(props: Props) {
// initialize mobile UI
player.mobileUi(); // Inits mobile version. No-op if Desktop.
+ // Add quality selector to player
+ player.hlsQualitySelector({
+ displayCurrentQuality: true,
+ });
+
// I think this is a callback function
onPlayerReady(player);
});
@@ -442,17 +447,6 @@ export default React.memo(function VideoJs(props: Props) {
src: source,
type: type,
});
-
- // Add quality selector to player
- player.hlsQualitySelector({
- displayCurrentQuality: true,
- });
-
- // Update player source
- player.src({
- src: source,
- type: type,
- });
});
}, [source, reload]);
diff --git a/ui/constants/icons.js b/ui/constants/icons.js
index df733f879..1b5de2e32 100644
--- a/ui/constants/icons.js
+++ b/ui/constants/icons.js
@@ -136,3 +136,4 @@ export const CHANNEL_LEVEL_2 = 'ChannelLevel2';
export const CHANNEL_LEVEL_3 = 'ChannelLevel3';
export const CHANNEL_LEVEL_4 = 'ChannelLevel4';
export const CHANNEL_LEVEL_5 = 'ChannelLevel5';
+export const MOVIES = 'Movies';
diff --git a/ui/scss/component/_channel.scss b/ui/scss/component/_channel.scss
index 4e4ac9e97..f802fce6d 100644
--- a/ui/scss/component/_channel.scss
+++ b/ui/scss/component/_channel.scss
@@ -343,7 +343,6 @@ $metadata-z-index: 1;
.channel__list-text {
font-weight: var(--font-weight-bold);
- color: var(--color-text);
}
.channel__selector {
diff --git a/web/index.js b/web/index.js
index 46362ccdc..487c09d80 100644
--- a/web/index.js
+++ b/web/index.js
@@ -6,7 +6,6 @@ const logger = require('koa-logger');
const router = require('./src/routes');
const redirectMiddleware = require('./middleware/redirect');
const cacheControlMiddleware = require('./middleware/cache-control');
-const iframeDestroyerMiddleware = require('./middleware/iframe-destroyer');
const app = new Koa();
const DIST_ROOT = path.resolve(__dirname, 'dist');
@@ -26,7 +25,6 @@ app.use(async (ctx, next) => {
app.use(logger());
app.use(cacheControlMiddleware);
app.use(redirectMiddleware);
-app.use(iframeDestroyerMiddleware);
app.use(serve(DIST_ROOT)); // Check if the request url matches any assets inside of /dist
app.use(router.routes());
diff --git a/web/middleware/iframe-destroyer.js b/web/middleware/iframe-destroyer.js
deleted file mode 100644
index 1737480f3..000000000
--- a/web/middleware/iframe-destroyer.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const PAGES = require('../../ui/constants/pages');
-
-async function iframeDestroyerMiddleware(ctx, next) {
- const {
- request: { path },
- } = ctx;
-
- if (!path.startsWith(`/$/${PAGES.EMBED}`)) {
- ctx.set('X-Frame-Options', 'DENY');
- }
-
- return next();
-}
-
-module.exports = iframeDestroyerMiddleware;
diff --git a/yarn.lock b/yarn.lock
index 6d5569fa4..681ae0a2c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2114,6 +2114,11 @@ any-observable@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
+any-promise@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+ integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
+
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -5306,6 +5311,17 @@ fs-copy-file-sync@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz#11bf32c096c10d126e5f6b36d06eece776062918"
+fs-extra@^0.26.5:
+ version "0.26.7"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
+ integrity sha1-muH92UiXeY7at20JGM9C0MMYT6k=
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^2.1.0"
+ klaw "^1.0.0"
+ path-is-absolute "^1.0.0"
+ rimraf "^2.2.8"
+
fs-extra@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
@@ -5339,6 +5355,16 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
+fs-promise@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-0.5.0.tgz#4347d6bf624655a7061a4319213c393276ad3ef3"
+ integrity sha1-Q0fWv2JGVacGGkMZITw5MnatPvM=
+ dependencies:
+ any-promise "^1.0.0"
+ fs-extra "^0.26.5"
+ mz "^2.3.1"
+ thenify-all "^1.6.0"
+
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -5631,7 +5657,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
+graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
@@ -6789,7 +6815,7 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
-jsonfile@^2.2.3:
+jsonfile@^2.1.0, jsonfile@^2.2.3:
version "2.4.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
optionalDependencies:
@@ -6833,6 +6859,14 @@ jszip@~2.5.0:
dependencies:
pako "~0.2.5"
+karma-safaritechpreview-launcher@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/karma-safaritechpreview-launcher/-/karma-safaritechpreview-launcher-0.0.6.tgz#7a841105aeb7053940e33df850edcf220eed906a"
+ integrity sha512-2QMxAGXPQ37H3KoR9SCdh0OoktQZ5MyrxkvBiZ+VVOQfYVrcyOQXGrPea0/DKvf8qoQvrvP2FHcP/BxsuxuyHw==
+ dependencies:
+ fs-promise "^0.5.0"
+ marcosc-async "^3.0.4"
+
keycode@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
@@ -6874,6 +6908,13 @@ kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+klaw@^1.0.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
+ integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk=
+ optionalDependencies:
+ graceful-fs "^4.1.9"
+
latest-version@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
@@ -7329,6 +7370,11 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
+marcosc-async@^3.0.4:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/marcosc-async/-/marcosc-async-3.0.5.tgz#41e6d56c656c811859d34b97a0a26093f71dc360"
+ integrity sha1-QebVbGVsgRhZ00uXoKJgk/cdw2A=
+
markdown-escapes@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
@@ -7707,6 +7753,15 @@ mux.js@5.6.7:
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.6.7.tgz#d39fc85cded5a1257de9f6eeb5cf1578c4a63eb8"
integrity sha512-YSr6B8MUgE4S18MptbY2XM+JKGbw9JDkgs7YkuE/T2fpDKjOhZfb/nD6vmsVxvLYOExWNaQn1UGBp6PGsnTtew==
+mz@^2.3.1:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
+ integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
+ dependencies:
+ any-promise "^1.0.0"
+ object-assign "^4.0.1"
+ thenify-all "^1.0.0"
+
nan@^2.12.1:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
@@ -9201,6 +9256,11 @@ react-hot-loader@^4.11.1:
shallowequal "^1.1.0"
source-map "^0.7.3"
+react-image@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/react-image/-/react-image-4.0.3.tgz#6fa722877660b67295298a914bff1ed87ad2cf83"
+ integrity sha512-19MUK9u1qaw9xys8XEsVkSpVhHctEBUeYFvrLTe1PN+4w5Co13AN2WA7xtBshPM6SthsOj77SlDrEAeOaJpf7g==
+
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2:
version "16.13.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527"
@@ -10780,6 +10840,20 @@ text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+thenify-all@^1.0.0, thenify-all@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
+ integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
+ dependencies:
+ thenify ">= 3.1.0 < 4"
+
+"thenify@>= 3.1.0 < 4":
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
+ integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
+ dependencies:
+ any-promise "^1.0.0"
+
three-full@^17.1.0:
version "17.2.0"
resolved "https://registry.yarnpkg.com/three-full/-/three-full-17.2.0.tgz#b6086360a9429e733e080093c25d02a3171ba1cc"
@@ -11461,7 +11535,7 @@ vfile@^2.0.0:
unist-util-stringify-position "^1.0.0"
vfile-message "^1.0.0"
-"video.js@^6 || ^7", video.js@^7.0.0, video.js@^7.10.1:
+"video.js@^6 || ^7", video.js@^7.0.0, video.js@^7.10.1, video.js@^7.5.5:
version "7.10.2"
resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.10.2.tgz#5156aabad7820e726d72ea6c32324059c68885a4"
integrity sha512-kJTTrqcQn2MhPzWR8zQs6W3HPJWpowO/ZGZcKt2dcJeJdJT0dEDLYtiFdjV37SylCmu66V0flRnV8cipbthveQ==
@@ -11495,6 +11569,16 @@ videojs-font@3.2.0:
resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-3.2.0.tgz#212c9d3f4e4ec3fa7345167d64316add35e92232"
integrity sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==
+videojs-hls-quality-selector@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/videojs-hls-quality-selector/-/videojs-hls-quality-selector-1.1.4.tgz#281b78c6653137b08c544e806aa9c91b71d16989"
+ integrity sha512-wWAjlLQui02gp//t9KHGd3XnbYO7wdOptskh3ZYCrbl/5Lbkveqb9yBVjH4e0zIQBPvGdWPMcOeDukf8iuYeBw==
+ dependencies:
+ global "^4.3.2"
+ karma-safaritechpreview-launcher "0.0.6"
+ video.js "^7.5.5"
+ videojs-contrib-quality-levels "^2.0.9"
+
videojs-logo@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/videojs-logo/-/videojs-logo-2.1.4.tgz#56675b3f95949910bad3c217835ea57aa6fdf212"