Add pre-roll ads
change ad macro url rework video.js load logic fix autoplay and retry errors yeet player.ended error
This commit is contained in:
parent
0fbf202f2b
commit
a7e3bceb6c
10 changed files with 303 additions and 81 deletions
|
@ -60,6 +60,8 @@
|
|||
"remove-markdown": "^0.3.0",
|
||||
"source-map-explorer": "^2.5.2",
|
||||
"tempy": "^0.6.0",
|
||||
"videojs-contrib-ads": "^6.9.0",
|
||||
"videojs-ima": "^1.11.0",
|
||||
"videojs-logo": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
// Created by xander on 6/21/2021
|
||||
import videojs from 'video.js';
|
||||
import 'videojs-ima';
|
||||
const VERSION = '0.0.1';
|
||||
|
||||
const macroUrl =
|
||||
'https://vast.aniview.com/api/adserver61/vast/?AV_PUBLISHERID=60afcbc58cfdb065440d2426&AV_CHANNELID=60b354389c7adb506d0bd9a4&AV_URL=[URL_MACRO]&cb=[TIMESTAMP_MACRO]&AV_WIDTH=[WIDTH_MACRO]&AV_HEIGHT=[HEIGHT_MACRO]&AV_SCHAIN=[SCHAIN_MACRO]&AV_CCPA=[CCPA_MACRO]&AV_GDPR=[GDPR_MACRO]&AV_CONSENT=[CONSENT_MACRO]&skip=true&skiptimer=5&usevslot=true&hidecontrols=false';
|
||||
|
||||
const defaults = {
|
||||
adTagUrl: macroUrl,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
const Component = videojs.getComponent('Component');
|
||||
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
|
||||
class AniviewPlugin extends Component {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
// Plugin started
|
||||
if (options.debug) {
|
||||
this.log(`Created aniview plugin.`);
|
||||
}
|
||||
|
||||
// To help with debugging, we'll add a global vjs object with the video js player
|
||||
window.aniview = player;
|
||||
|
||||
this.player = player;
|
||||
|
||||
player.ima({
|
||||
// adTagUrl: macroUrl,
|
||||
id: 'ad_content_video',
|
||||
vpaidMode: google.ima.ImaSdkSettings.VpaidMode.INSECURE,
|
||||
adTagUrl:
|
||||
'https://vast.aniview.com/api/adserver61/vast/?AV_PUBLISHERID=60afcbc58cfdb065440d2426&AV_CHANNELID=60b354389c7adb506d0bd9a4',
|
||||
});
|
||||
|
||||
// this.player.ads();
|
||||
// const serverUrl = this.player.ads.adMacroReplacement(macroUrl);
|
||||
// this.log(serverUrl);
|
||||
|
||||
// request ads whenever there's new video content
|
||||
player.on('contentchanged', () => {
|
||||
// in a real plugin, you might fetch your ad inventory here
|
||||
player.trigger('adsready');
|
||||
});
|
||||
|
||||
// Plugin event listeners
|
||||
player.on('readyforpreroll', (event) => this.onReadyForPreroll(event));
|
||||
}
|
||||
|
||||
onReadyForPreroll(event) {
|
||||
this.player.ads.startLinearAdMode();
|
||||
|
||||
// play your linear ad content
|
||||
// in this example, we use a static mp4
|
||||
this.player.src('kitteh.mp4');
|
||||
|
||||
// send event when ad is playing to remove loading spinner
|
||||
this.player.one('adplaying', () => {
|
||||
this.player.trigger('ads-ad-started');
|
||||
});
|
||||
|
||||
// resume content when all your linear ads have finished
|
||||
this.player.one('adended', () => {
|
||||
this.player.ads.endLinearAdMode();
|
||||
});
|
||||
}
|
||||
|
||||
log(...args) {
|
||||
if (this.options_.debug) {
|
||||
console.log(`Aniview Debug:`, JSON.stringify(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
videojs.registerComponent('recsys', AniviewPlugin);
|
||||
|
||||
const onPlayerReady = (player, options) => {
|
||||
player.aniview = new AniviewPlugin(player, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the plugin.
|
||||
*
|
||||
* @function plugin
|
||||
* @param {Object} [options={}]
|
||||
*/
|
||||
const plugin = function (options) {
|
||||
this.ready(() => {
|
||||
onPlayerReady(this, videojs.mergeOptions(defaults, options));
|
||||
});
|
||||
};
|
||||
|
||||
plugin.VERSION = VERSION;
|
||||
|
||||
registerPlugin('aniview', plugin);
|
||||
|
||||
export default plugin;
|
|
@ -1,4 +1,4 @@
|
|||
import videojs from 'video.js/dist/video.min.js';
|
||||
import videojs from 'video.js';
|
||||
import './touchOverlay.js';
|
||||
import window from 'global/window';
|
||||
import './plugin.scss';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Touch UI component
|
||||
*/
|
||||
|
||||
import videojs from 'video.js/dist/video.min.js';
|
||||
import videojs from 'video.js';
|
||||
import window from 'global/window';
|
||||
|
||||
const Component = videojs.getComponent('Component');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import videojs from 'video.js/dist/video.min.js';
|
||||
import videojs from 'video.js';
|
||||
import window from 'global/window';
|
||||
const VERSION = '2.1.4';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Created by xander on 6/21/2021
|
||||
import videojs from 'video.js/dist/video.min.js';
|
||||
import videojs from 'video.js';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
const VERSION = '0.0.1';
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|||
import Button from 'component/button';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import classnames from 'classnames';
|
||||
import videojs from 'video.js/dist/video.min.js';
|
||||
import videojs from 'video.js';
|
||||
// import 'video.js/dist/alt/video-js-cdn.min.css'; --> 'scss/third-party.scss'
|
||||
import eventTracking from 'videojs-event-tracking';
|
||||
import * as OVERLAY from './overlays';
|
||||
|
@ -12,8 +12,13 @@ import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
|
|||
import recsys from './plugins/videojs-recsys/plugin';
|
||||
import qualityLevels from 'videojs-contrib-quality-levels';
|
||||
import isUserTyping from 'util/detect-typing';
|
||||
import 'videojs-contrib-ads';
|
||||
import 'videojs-ima';
|
||||
import aniview from './plugins/videojs-aniview/plugin';
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
const macroUrl =
|
||||
'https://vast.aniview.com/api/adserver61/vast/?AV_PUBLISHERID=60afcbc58cfdb065440d2426&AV_CHANNELID=60b354389c7adb506d0bd9a4&AV_URL=[URL_MACRO]&cb=[TIMESTAMP_MACRO]&AV_WIDTH=[WIDTH_MACRO]&AV_HEIGHT=[HEIGHT_MACRO]&AV_SCHAIN=[SCHAIN_MACRO]&AV_CCPA=[CCPA_MACRO]&AV_GDPR=[GDPR_MACRO]&AV_CONSENT=[CONSENT_MACRO]&skip=true&skiptimer=5&usevslot=true&hidecontrols=false';
|
||||
|
||||
export type Player = {
|
||||
on: (string, (any) => void) => void,
|
||||
|
@ -77,7 +82,7 @@ const VIDEO_JS_OPTIONS = {
|
|||
responsive: true,
|
||||
controls: true,
|
||||
html5: {
|
||||
hls: {
|
||||
vhs: {
|
||||
overrideNative: !videojs.browser.IS_ANY_SAFARI,
|
||||
},
|
||||
},
|
||||
|
@ -336,6 +341,8 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
// The css starts as "hidden". We make it visible here without
|
||||
// re-rendering the whole thing.
|
||||
showTapButton(TAP.UNMUTE);
|
||||
} else {
|
||||
showTapButton(TAP.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +357,9 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
const player = playerRef.current;
|
||||
showTapButton(TAP.RETRY);
|
||||
|
||||
// reattach initial play listener in case we recover from error successfully
|
||||
player.one('play', onInitialPlay);
|
||||
|
||||
if (player && player.loadingSpinner) {
|
||||
player.loadingSpinner.hide();
|
||||
}
|
||||
|
@ -472,9 +482,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
|
||||
// Create the video DOM element and wrapper
|
||||
function createVideoPlayerDOM(container) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
if (!container) return;
|
||||
|
||||
// This seems like a poor way to generate the DOM for video.js
|
||||
const wrapper = document.createElement('div');
|
||||
|
@ -488,19 +496,51 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
return el;
|
||||
}
|
||||
|
||||
function detectFileType() {
|
||||
console.log(`Detecting file type via pre-fetch...`);
|
||||
return new Promise(async (res, rej) => {
|
||||
try {
|
||||
const response = await fetch(source, { method: 'HEAD', cache: 'no-store' });
|
||||
|
||||
// Temp variables to hold results
|
||||
let finalType = sourceType;
|
||||
let finalSource = source;
|
||||
|
||||
// override type if we receive an .m3u8 (transcoded mp4)
|
||||
// do we need to check if explicitly redirected
|
||||
// or is checking extension only a safer method
|
||||
if (response && response.redirected && response.url && response.url.endsWith('m3u8')) {
|
||||
finalType = 'application/x-mpegURL';
|
||||
finalSource = response.url;
|
||||
}
|
||||
|
||||
console.log(`File type is: ${finalType}`);
|
||||
|
||||
// Modify video source in options
|
||||
videoJsOptions.sources = [
|
||||
{
|
||||
src: finalSource,
|
||||
type: finalType,
|
||||
},
|
||||
];
|
||||
|
||||
return res(videoJsOptions);
|
||||
} catch (error) {
|
||||
console.error(`Failed to pre-fetch video!`);
|
||||
return rej(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize video.js
|
||||
function initializeVideoPlayer(el) {
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
if (!el) return;
|
||||
|
||||
const vjs = videojs(el, videoJsOptions, () => {
|
||||
const player = playerRef.current;
|
||||
|
||||
// this seems like a weird thing to have to check for here
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
if (!player) return;
|
||||
|
||||
// Add various event listeners to player
|
||||
player.one('play', onInitialPlay);
|
||||
|
@ -516,11 +556,38 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
// Replace volume bar with custom LBRY volume bar
|
||||
LbryVolumeBarClass.replaceExisting(player);
|
||||
|
||||
// Add reloadSourceOnError plugin
|
||||
player.reloadSourceOnError({ errorInterval: 10 });
|
||||
|
||||
// initialize mobile UI
|
||||
player.mobileUi(); // Inits mobile version. No-op if Desktop.
|
||||
|
||||
// I think this is a callback function
|
||||
onPlayerReady(player);
|
||||
|
||||
// Add quality selector to player
|
||||
player.hlsQualitySelector({
|
||||
displayCurrentQuality: true,
|
||||
});
|
||||
|
||||
// Add recsys plugin
|
||||
// TODO: Add an if(odysee.com) around this function to only use recsys on odysee
|
||||
player.recsys({
|
||||
videoId: claimId,
|
||||
userId: userId,
|
||||
});
|
||||
|
||||
// player.aniview();
|
||||
|
||||
player.ima({
|
||||
// id: 'ad_content_video',
|
||||
vpaidMode: google.ima.ImaSdkSettings.VpaidMode.INSECURE,
|
||||
adTagUrl: macroUrl,
|
||||
});
|
||||
|
||||
// set playsinline for mobile
|
||||
// TODO: make this better
|
||||
player.children_[0].setAttribute('playsinline', '');
|
||||
});
|
||||
|
||||
// fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498)
|
||||
|
@ -533,6 +600,10 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
// This lifecycle hook is only called once (on mount), or when `isAudio` changes.
|
||||
useEffect(() => {
|
||||
const vjsElement = createVideoPlayerDOM(containerRef.current);
|
||||
|
||||
// Detect source file type via pre-fetch (async)
|
||||
detectFileType().then(() => {
|
||||
// Initialize Video.js
|
||||
const vjsPlayer = initializeVideoPlayer(vjsElement);
|
||||
|
||||
// Add reference to player to global scope
|
||||
|
@ -543,6 +614,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
|
||||
// Add event listener for keyboard shortcuts
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
|
@ -560,50 +632,28 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
useEffect(() => {
|
||||
// For some reason the video player is responsible for detecting content type this way
|
||||
fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => {
|
||||
const player = playerRef.current;
|
||||
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
let type = sourceType;
|
||||
let finalType = sourceType;
|
||||
let finalSource = source;
|
||||
|
||||
// override type if we receive an .m3u8 (transcoded mp4)
|
||||
// do we need to check if explicitly redirected
|
||||
// or is checking extension only a safer method
|
||||
if (response && response.redirected && response.url && response.url.endsWith('m3u8')) {
|
||||
type = 'application/x-mpegURL';
|
||||
finalType = 'application/x-mpegURL';
|
||||
finalSource = response.url;
|
||||
}
|
||||
|
||||
// Update player poster
|
||||
// note: the poster prop seems to return null usually.
|
||||
if (poster) player.poster(poster);
|
||||
// Modify video source in options
|
||||
videoJsOptions.sources = [
|
||||
{
|
||||
src: finalSource,
|
||||
type: finalType,
|
||||
},
|
||||
];
|
||||
|
||||
// Update player source
|
||||
player.src({
|
||||
src: finalSource,
|
||||
type: type,
|
||||
});
|
||||
|
||||
// set playsinline for mobile
|
||||
player.children_[0].setAttribute('playsinline', '');
|
||||
|
||||
// Add quality selector to player
|
||||
player.hlsQualitySelector({
|
||||
displayCurrentQuality: true,
|
||||
});
|
||||
|
||||
// Add recsys plugin
|
||||
// TODO: Add an if(odysee.com) around this function to only use recsys on odysee
|
||||
player.recsys({
|
||||
videoId: claimId,
|
||||
userId: userId,
|
||||
});
|
||||
|
||||
// Update player source
|
||||
player.src({
|
||||
src: finalSource,
|
||||
type: type,
|
||||
});
|
||||
const player = playerRef.current;
|
||||
if (!player) return;
|
||||
|
||||
// PR #5570: Temp workaround to avoid double Play button until the next re-architecture.
|
||||
if (!player.paused()) {
|
||||
|
@ -612,6 +662,20 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
});
|
||||
}, [source, reload]);
|
||||
|
||||
// Load IMA3 SDK for aniview
|
||||
useEffect(() => {
|
||||
const script = document.createElement('script');
|
||||
script.src = `https://imasdk.googleapis.com/js/sdkloader/ima3.js`;
|
||||
script.async = true;
|
||||
// $FlowFixMe
|
||||
document.body.appendChild(script);
|
||||
|
||||
return () => {
|
||||
// $FlowFixMe
|
||||
document.body.removeChild(script);
|
||||
};
|
||||
}, [source, reload]);
|
||||
|
||||
return (
|
||||
// $FlowFixMe
|
||||
<div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef}>
|
||||
|
|
|
@ -148,7 +148,10 @@ function VideoViewer(props: Props) {
|
|||
}, [embedded, videoPlaybackRate]);
|
||||
|
||||
function doTrackingBuffered(e: Event, data: any) {
|
||||
console.log(`tracking buffered`, e);
|
||||
console.log(`Source (tracking buffered): ${source}`);
|
||||
fetch(source, { method: 'HEAD' }).then((response) => {
|
||||
console.log(`Source Response (tracking buffered)`, response);
|
||||
data.playerPoweredBy = response.headers.get('x-powered-by');
|
||||
doAnalyticsBuffer(uri, data);
|
||||
});
|
||||
|
@ -179,7 +182,9 @@ function VideoViewer(props: Props) {
|
|||
} else if (autoplaySetting) {
|
||||
setShowAutoplayCountdown(true);
|
||||
}
|
||||
}, [embedded, setIsEndededEmbed, autoplaySetting, setShowAutoplayCountdown, adUrl, setAdUrl]);
|
||||
|
||||
clearPosition(uri);
|
||||
}, [embedded, setIsEndededEmbed, autoplaySetting, setShowAutoplayCountdown, adUrl, setAdUrl, clearPosition, uri]);
|
||||
|
||||
function onPlay() {
|
||||
setIsLoading(false);
|
||||
|
@ -198,12 +203,8 @@ function VideoViewer(props: Props) {
|
|||
}
|
||||
|
||||
function handlePosition(player) {
|
||||
if (player.ended()) {
|
||||
clearPosition(uri);
|
||||
} else {
|
||||
savePosition(uri, player.currentTime());
|
||||
}
|
||||
}
|
||||
|
||||
function restorePlaybackRate(player) {
|
||||
if (!vjsCallbackDataRef.current.embedded) {
|
||||
|
@ -235,22 +236,13 @@ function VideoViewer(props: Props) {
|
|||
Promise.race([playPromise, timeoutPromise]).catch((error) => {
|
||||
if (typeof error === 'object' && error.name && error.name === 'NotAllowedError') {
|
||||
if (player.autoplay() && !player.muted()) {
|
||||
player.muted(true);
|
||||
// player.muted(true);
|
||||
// another version had player.play()
|
||||
}
|
||||
}
|
||||
|
||||
if (PLAY_TIMEOUT_ERROR) {
|
||||
const retryPlayPromise = player.play();
|
||||
Promise.race([retryPlayPromise, timeoutPromise]).catch((error) => {
|
||||
setIsLoading(false);
|
||||
setIsPlaying(false);
|
||||
});
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsPlaying(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setIsLoading(shouldPlay); // if we are here outside of an embed, we're playing
|
||||
|
|
|
@ -589,9 +589,9 @@ video::-internal-media-controls-overlay-cast-button {
|
|||
|
||||
.file-render {
|
||||
.video-js {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/*display: flex;*/
|
||||
/*align-items: center;*/
|
||||
/*justify-content: center;*/
|
||||
}
|
||||
|
||||
.vjs-big-play-button {
|
||||
|
|
68
yarn.lock
68
yarn.lock
|
@ -1266,6 +1266,25 @@
|
|||
tough-cookie "^2.2.2"
|
||||
tough-cookie-web-storage-store "^1.0.0"
|
||||
|
||||
"@hapi/boom@9.x.x":
|
||||
version "9.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.2.tgz#48bd41d67437164a2d636e3b5bc954f8c8dc5e38"
|
||||
integrity sha512-uJEJtiNHzKw80JpngDGBCGAmWjBtzxDCz17A9NO2zCi8LLBlb5Frpq4pXwyN+2JQMod4pKz5BALwyneCgDg89Q==
|
||||
dependencies:
|
||||
"@hapi/hoek" "9.x.x"
|
||||
|
||||
"@hapi/cryptiles@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/cryptiles/-/cryptiles-5.1.0.tgz#655de4cbbc052c947f696148c83b187fc2be8f43"
|
||||
integrity sha512-fo9+d1Ba5/FIoMySfMqPBR/7Pa29J2RsiPrl7bkwo5W5o+AN1dAYQRi4SPrPwwVxVGKjgLOEWrsvt1BonJSfLA==
|
||||
dependencies:
|
||||
"@hapi/boom" "9.x.x"
|
||||
|
||||
"@hapi/hoek@9.x.x":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
|
||||
integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
|
||||
|
||||
"@hot-loader/react-dom@^16.13":
|
||||
version "16.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.13.0.tgz#de245b42358110baf80aaf47a0592153d4047997"
|
||||
|
@ -2981,6 +3000,11 @@ camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1:
|
|||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
|
||||
can-autoplay@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/can-autoplay/-/can-autoplay-3.0.0.tgz#fc2de8f1d41b36f6d860d9336b66841d30f8b62d"
|
||||
integrity sha512-qQXGGYPWgF8nPjEt305o3TJ/BkN15l6/wG+VU4N93YYXD3OtYkBBx+l5un7ihIk2UU1OLytAVJjW7ZR39j/CAQ==
|
||||
|
||||
caniuse-api@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
|
||||
|
@ -5124,7 +5148,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
|
|||
assign-symbols "^1.0.0"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
extend@^3.0.0, extend@~3.0.1:
|
||||
extend@>=3.0.2, extend@^3.0.0, extend@~3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
|
||||
|
@ -7421,6 +7445,11 @@ lodash-es@^4.17.14, lodash-es@^4.2.1:
|
|||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
||||
|
||||
lodash._reinterpolate@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
||||
|
||||
lodash.camelcase@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||
|
@ -7470,6 +7499,21 @@ lodash.snakecase@^4.1.1:
|
|||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
|
||||
|
||||
lodash.template@>=4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
|
||||
integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
|
||||
dependencies:
|
||||
lodash._reinterpolate "^3.0.0"
|
||||
lodash.templatesettings "^4.0.0"
|
||||
|
||||
lodash.templatesettings@^4.0.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
|
||||
integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
|
||||
dependencies:
|
||||
lodash._reinterpolate "^3.0.0"
|
||||
|
||||
lodash.toarray@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
|
||||
|
@ -7482,7 +7526,7 @@ lodash.unset@^4.5.2:
|
|||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed"
|
||||
|
||||
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.6.1:
|
||||
lodash@>=4.17.19, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.6.1:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -12064,6 +12108,14 @@ vfile@^2.0.0:
|
|||
videojs-font "3.2.0"
|
||||
videojs-vtt.js "^0.15.2"
|
||||
|
||||
videojs-contrib-ads@^6.6.5, videojs-contrib-ads@^6.9.0:
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/videojs-contrib-ads/-/videojs-contrib-ads-6.9.0.tgz#c792d6fda77254b277545cc3222352fc653b5833"
|
||||
integrity sha512-nzKz+jhCGMTYffSNVYrmp9p70s05v6jUMOY3Z7DpVk3iFrWK4Zi/BIkokDWrMoHpKjdmCdKzfJVBT+CrUj6Spw==
|
||||
dependencies:
|
||||
global "^4.3.2"
|
||||
video.js "^6 || ^7"
|
||||
|
||||
videojs-contrib-quality-levels@^2.0.9:
|
||||
version "2.0.9"
|
||||
resolved "https://registry.yarnpkg.com/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-2.0.9.tgz#b5d533d5092a6fc7d29eae1b43e4597d89bd527b"
|
||||
|
@ -12084,6 +12136,18 @@ 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-ima@^1.11.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/videojs-ima/-/videojs-ima-1.11.0.tgz#26ad385e388c3da72372298d7d755b001d05a91d"
|
||||
integrity sha512-ZRoWuGyJ75zamwZgpr0i/gZ6q7Evda/Q6R46gpW88WN7u0ORU7apw/lM1MSG4c3YDXW8LDENgzMAvMZUdifWhg==
|
||||
dependencies:
|
||||
"@hapi/cryptiles" "^5.1.0"
|
||||
can-autoplay "^3.0.0"
|
||||
extend ">=3.0.2"
|
||||
lodash ">=4.17.19"
|
||||
lodash.template ">=4.5.0"
|
||||
videojs-contrib-ads "^6.6.5"
|
||||
|
||||
videojs-logo@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/videojs-logo/-/videojs-logo-2.1.4.tgz#56675b3f95949910bad3c217835ea57aa6fdf212"
|
||||
|
|
Loading…
Reference in a new issue