From eac4440d3bed7088c3f894956444d9f4c5197030 Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Fri, 1 Nov 2024 10:02:52 +0100 Subject: [PATCH] Fix play logo (datarhei/restreamer#845, datarhei/restreamer#847) --- package.json | 4 +- .../_player/videojs/dist/videojs-overlay.js | 758 +++++++++--------- .../videojs/dist/videojs-overlay.min.css | 8 +- .../videojs/dist/videojs-overlay.min.js | 4 +- public/_player/videojs/player.html | 63 +- src/misc/Player/index.js | 2 +- src/misc/Player/videojs-overlay.css | 1 + src/misc/Player/videojs-overlay.es.js | 411 ++++++++++ src/misc/Player/videojs.js | 6 +- yarn.lock | 8 +- 10 files changed, 860 insertions(+), 405 deletions(-) create mode 100644 src/misc/Player/videojs-overlay.css create mode 100644 src/misc/Player/videojs-overlay.es.js diff --git a/package.json b/package.json index d94cb1d..9f74b05 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "util": "^0.12.5", "uuid": "^10.0.0", "video.js": "^8.17.3", - "videojs-overlay": "^3.1.0" + "videojs-overlay": "^4.0.0" }, "scripts": { "start": "react-scripts start", @@ -95,4 +95,4 @@ "react-error-overlay": "^6.0.11" }, "resolutions": {} -} +} \ No newline at end of file diff --git a/public/_player/videojs/dist/videojs-overlay.js b/public/_player/videojs/dist/videojs-overlay.js index 6305016..26cb39a 100644 --- a/public/_player/videojs/dist/videojs-overlay.js +++ b/public/_player/videojs/dist/videojs-overlay.js @@ -1,386 +1,420 @@ -/*! @name videojs-overlay @version 2.1.5 @license Apache-2.0 */ -'use strict'; +/*! @name videojs-overlay @version 4.0.0 @license Apache-2.0 */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) : + typeof define === 'function' && define.amd ? define(['video.js'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojsOverlay = factory(global.videojs)); +})(this, (function (videojs) { 'use strict'; -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } -var videojs = _interopDefault(require('video.js')); -var window = _interopDefault(require('global/window')); + var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs); -function _inheritsLoose(subClass, superClass) { - subClass.prototype = Object.create(superClass.prototype); - subClass.prototype.constructor = subClass; - subClass.__proto__ = superClass; -} + const initOverlayComponent = videojs => { + const Component = videojs.getComponent('Component'); + const dom = videojs.dom || videojs; -function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } + /** + * Whether the value is a `Number`. + * + * Both `Infinity` and `-Infinity` are accepted, but `NaN` is not. + * + * @param {Number} n + * @return {Boolean} + */ - return self; -} + /* eslint-disable no-self-compare */ + const isNumber = n => typeof n === 'number' && n === n; + /* eslint-enable no-self-compare */ -var version = "2.1.5"; + /** + * Whether a value is a string with no whitespace. + * + * @param {string} s + * @return {boolean} + */ + const hasNoWhitespace = s => typeof s === 'string' && /^\S+$/.test(s); -var defaults = { - align: 'top-left', - class: '', - content: 'This overlay will show up while the video is playing', - debug: false, - showBackground: true, - attachToControlBar: false, - overlays: [{ - start: 'playing', - end: 'paused' - }] -}; -var Component = videojs.getComponent('Component'); -var dom = videojs.dom || videojs; -var registerPlugin = videojs.registerPlugin || videojs.plugin; -/** - * Whether the value is a `Number`. - * - * Both `Infinity` and `-Infinity` are accepted, but `NaN` is not. - * - * @param {Number} n - * @return {Boolean} - */ + /** + * Overlay component. + * + * @class Overlay + * @extends {videojs.Component} + */ + class Overlay extends Component { + constructor(player, options) { + super(player, options); + ['start', 'end'].forEach(key => { + const value = this.options_[key]; + if (isNumber(value)) { + this[key + 'Event_'] = 'timeupdate'; + } else if (hasNoWhitespace(value)) { + this[key + 'Event_'] = value; -/* eslint-disable no-self-compare */ + // An overlay MUST have a start option. Otherwise, it's pointless. + } else if (key === 'start') { + throw new Error('invalid "start" option; expected number or string'); + } + }); -var isNumber = function isNumber(n) { - return typeof n === 'number' && n === n; -}; -/* eslint-enable no-self-compare */ + // video.js does not like components with multiple instances binding + // events to the player because it tracks them at the player level, + // not at the level of the object doing the binding. This could also be + // solved with Function.prototype.bind (but not videojs.bind because of + // its GUID magic), but the anonymous function approach avoids any issues + // caused by crappy libraries clobbering Function.prototype.bind. + // - https://github.com/videojs/video.js/issues/3097 + ['endListener_', 'rewindListener_', 'startListener_'].forEach(name => { + this[name] = e => Overlay.prototype[name].call(this, e); + }); -/** - * Whether a value is a string with no whitespace. - * - * @param {String} s - * @return {Boolean} - */ - - -var hasNoWhitespace = function hasNoWhitespace(s) { - return typeof s === 'string' && /^\S+$/.test(s); -}; -/** - * Overlay component. - * - * @class Overlay - * @extends {videojs.Component} - */ - - -var Overlay = -/*#__PURE__*/ -function (_Component) { - _inheritsLoose(Overlay, _Component); - - function Overlay(player, options) { - var _this; - - _this = _Component.call(this, player, options) || this; - ['start', 'end'].forEach(function (key) { - var value = _this.options_[key]; - - if (isNumber(value)) { - _this[key + 'Event_'] = 'timeupdate'; - } else if (hasNoWhitespace(value)) { - _this[key + 'Event_'] = value; // An overlay MUST have a start option. Otherwise, it's pointless. - } else if (key === 'start') { - throw new Error('invalid "start" option; expected number or string'); - } - }); // video.js does not like components with multiple instances binding - // events to the player because it tracks them at the player level, - // not at the level of the object doing the binding. This could also be - // solved with Function.prototype.bind (but not videojs.bind because of - // its GUID magic), but the anonymous function approach avoids any issues - // caused by crappy libraries clobbering Function.prototype.bind. - // - https://github.com/videojs/video.js/issues/3097 - - ['endListener_', 'rewindListener_', 'startListener_'].forEach(function (name$$1) { - _this[name$$1] = function (e) { - return Overlay.prototype[name$$1].call(_assertThisInitialized(_assertThisInitialized(_this)), e); - }; - }); // If the start event is a timeupdate, we need to watch for rewinds (i.e., - // when the user seeks backward). - - if (_this.startEvent_ === 'timeupdate') { - _this.on(player, 'timeupdate', _this.rewindListener_); - } - - _this.debug("created, listening to \"" + _this.startEvent_ + "\" for \"start\" and \"" + (_this.endEvent_ || 'nothing') + "\" for \"end\""); - - _this.hide(); - - return _this; - } - - var _proto = Overlay.prototype; - - _proto.createEl = function createEl() { - var options = this.options_; - var content = options.content; - var background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background'; - var el = dom.createEl('div', { - className: "\n vjs-overlay\n vjs-overlay-" + options.align + "\n " + options.class + "\n " + background + "\n vjs-hidden\n " - }); - - if (typeof content === 'string') { - el.innerHTML = content; - } else if (content instanceof window.DocumentFragment) { - el.appendChild(content); - } else { - dom.appendContent(el, content); - } - - return el; - }; - /** - * Logs debug errors - * @param {...[type]} args [description] - * @return {[type]} [description] - */ - - - _proto.debug = function debug() { - if (!this.options_.debug) { - return; - } - - var log = videojs.log; - var fn = log; // Support `videojs.log.foo` calls. - - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') { - fn = log[args.shift()]; - } - - fn.apply(void 0, ["overlay#" + this.id() + ": "].concat(args)); - }; - /** - * Overrides the inherited method to perform some event binding - * - * @return {Overlay} - */ - - - _proto.hide = function hide() { - _Component.prototype.hide.call(this); - - this.debug('hidden'); - this.debug("bound `startListener_` to \"" + this.startEvent_ + "\""); // Overlays without an "end" are valid. - - if (this.endEvent_) { - this.debug("unbound `endListener_` from \"" + this.endEvent_ + "\""); - this.off(this.player(), this.endEvent_, this.endListener_); - } - - this.on(this.player(), this.startEvent_, this.startListener_); - return this; - }; - /** - * Determine whether or not the overlay should hide. - * - * @param {Number} time - * The current time reported by the player. - * @param {String} type - * An event type. - * @return {Boolean} - */ - - - _proto.shouldHide_ = function shouldHide_(time, type) { - var end = this.options_.end; - return isNumber(end) ? time >= end : end === type; - }; - /** - * Overrides the inherited method to perform some event binding - * - * @return {Overlay} - */ - - - _proto.show = function show() { - _Component.prototype.show.call(this); - - this.off(this.player(), this.startEvent_, this.startListener_); - this.debug('shown'); - this.debug("unbound `startListener_` from \"" + this.startEvent_ + "\""); // Overlays without an "end" are valid. - - if (this.endEvent_) { - this.debug("bound `endListener_` to \"" + this.endEvent_ + "\""); - this.on(this.player(), this.endEvent_, this.endListener_); - } - - return this; - }; - /** - * Determine whether or not the overlay should show. - * - * @param {Number} time - * The current time reported by the player. - * @param {String} type - * An event type. - * @return {Boolean} - */ - - - _proto.shouldShow_ = function shouldShow_(time, type) { - var start = this.options_.start; - var end = this.options_.end; - - if (isNumber(start)) { - if (isNumber(end)) { - return time >= start && time < end; // In this case, the start is a number and the end is a string. We need - // to check whether or not the overlay has shown since the last seek. - } else if (!this.hasShownSinceSeek_) { - this.hasShownSinceSeek_ = true; - return time >= start; - } // In this case, the start is a number and the end is a string, but - // the overlay has shown since the last seek. This means that we need - // to be sure we aren't re-showing it at a later time than it is - // scheduled to appear. - - - return Math.floor(time) === start; - } - - return start === type; - }; - /** - * Event listener that can trigger the overlay to show. - * - * @param {Event} e - */ - - - _proto.startListener_ = function startListener_(e) { - var time = this.player().currentTime(); - - if (this.shouldShow_(time, e.type)) { - this.show(); - } - }; - /** - * Event listener that can trigger the overlay to show. - * - * @param {Event} e - */ - - - _proto.endListener_ = function endListener_(e) { - var time = this.player().currentTime(); - - if (this.shouldHide_(time, e.type)) { - this.hide(); - } - }; - /** - * Event listener that can looks for rewinds - that is, backward seeks - * and may hide the overlay as needed. - * - * @param {Event} e - */ - - - _proto.rewindListener_ = function rewindListener_(e) { - var time = this.player().currentTime(); - var previous = this.previousTime_; - var start = this.options_.start; - var end = this.options_.end; // Did we seek backward? - - if (time < previous) { - this.debug('rewind detected'); // The overlay remains visible if two conditions are met: the end value - // MUST be an integer and the the current time indicates that the - // overlay should NOT be visible. - - if (isNumber(end) && !this.shouldShow_(time)) { - this.debug("hiding; " + end + " is an integer and overlay should not show at this time"); - this.hasShownSinceSeek_ = false; - this.hide(); // If the end value is an event name, we cannot reliably decide if the - // overlay should still be displayed based solely on time; so, we can - // only queue it up for showing if the seek took us to a point before - // the start time. - } else if (hasNoWhitespace(end) && time < start) { - this.debug("hiding; show point (" + start + ") is before now (" + time + ") and end point (" + end + ") is an event"); - this.hasShownSinceSeek_ = false; + // If the start event is a timeupdate, we need to watch for rewinds (i.e., + // when the user seeks backward). + if (this.startEvent_ === 'timeupdate') { + this.on(player, 'timeupdate', this.rewindListener_); + } + this.debug(`created, listening to "${this.startEvent_}" for "start" and "${this.endEvent_ || 'nothing'}" for "end"`); this.hide(); } - } + createEl() { + const options = this.options_; + const content = options.content; + const background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background'; + const el = dom.createEl('div', { + className: ` + vjs-overlay + vjs-overlay-${options.align} + ${options.class} + ${background} + vjs-hidden + ` + }); + if (typeof content === 'string') { + el.innerHTML = content; + } else if (content instanceof window.DocumentFragment) { + el.appendChild(content); + } else { + dom.appendContent(el, content); + } + return el; + } - this.previousTime_ = time; + /** + * Logs debug errors + * + * @param {...[type]} args [description] + * @return {[type]} [description] + */ + debug(...args) { + if (!this.options_.debug) { + return; + } + const log = videojs.log; + let fn = log; + + // Support `videojs.log.foo` calls. + if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') { + fn = log[args.shift()]; + } + fn(...[`overlay#${this.id()}: `, ...args]); + } + + /** + * Overrides the inherited method to perform some event binding + * + * @return {Overlay} + */ + hide() { + super.hide(); + this.debug('hidden'); + this.debug(`bound \`startListener_\` to "${this.startEvent_}"`); + + // Overlays without an "end" are valid. + if (this.endEvent_) { + this.debug(`unbound \`endListener_\` from "${this.endEvent_}"`); + this.off(this.player(), this.endEvent_, this.endListener_); + } + this.on(this.player(), this.startEvent_, this.startListener_); + return this; + } + + /** + * Determine whether or not the overlay should hide. + * + * @param {number} time + * The current time reported by the player. + * @param {string} type + * An event type. + * @return {boolean} + */ + shouldHide_(time, type) { + const end = this.options_.end; + return isNumber(end) ? time >= end : end === type; + } + + /** + * Overrides the inherited method to perform some event binding + * + * @return {Overlay} + */ + show() { + super.show(); + this.off(this.player(), this.startEvent_, this.startListener_); + this.debug('shown'); + this.debug(`unbound \`startListener_\` from "${this.startEvent_}"`); + + // Overlays without an "end" are valid. + if (this.endEvent_) { + this.debug(`bound \`endListener_\` to "${this.endEvent_}"`); + this.on(this.player(), this.endEvent_, this.endListener_); + } + return this; + } + + /** + * Determine whether or not the overlay should show. + * + * @param {number} time + * The current time reported by the player. + * @param {string} type + * An event type. + * @return {boolean} + */ + shouldShow_(time, type) { + const start = this.options_.start; + const end = this.options_.end; + if (isNumber(start)) { + if (isNumber(end)) { + return time >= start && time < end; + + // In this case, the start is a number and the end is a string. We need + // to check whether or not the overlay has shown since the last seek. + } else if (!this.hasShownSinceSeek_) { + this.hasShownSinceSeek_ = true; + return time >= start; + } + + // In this case, the start is a number and the end is a string, but + // the overlay has shown since the last seek. This means that we need + // to be sure we aren't re-showing it at a later time than it is + // scheduled to appear. + return Math.floor(time) === start; + } + return start === type; + } + + /** + * Event listener that can trigger the overlay to show. + * + * @param {Event} e + */ + startListener_(e) { + const time = this.player().currentTime(); + if (this.shouldShow_(time, e.type)) { + this.show(); + } + } + + /** + * Event listener that can trigger the overlay to show. + * + * @param {Event} e + */ + endListener_(e) { + const time = this.player().currentTime(); + if (this.shouldHide_(time, e.type)) { + this.hide(); + } + } + + /** + * Event listener that can looks for rewinds - that is, backward seeks + * and may hide the overlay as needed. + * + * @param {Event} e + */ + rewindListener_(e) { + const time = this.player().currentTime(); + const previous = this.previousTime_; + const start = this.options_.start; + const end = this.options_.end; + + // Did we seek backward? + if (time < previous) { + this.debug('rewind detected'); + + // The overlay remains visible if two conditions are met: the end value + // MUST be an integer and the the current time indicates that the + // overlay should NOT be visible. + if (isNumber(end) && !this.shouldShow_(time)) { + this.debug(`hiding; ${end} is an integer and overlay should not show at this time`); + this.hasShownSinceSeek_ = false; + this.hide(); + + // If the end value is an event name, we cannot reliably decide if the + // overlay should still be displayed based solely on time; so, we can + // only queue it up for showing if the seek took us to a point before + // the start time. + } else if (hasNoWhitespace(end) && time < start) { + this.debug(`hiding; show point (${start}) is before now (${time}) and end point (${end}) is an event`); + this.hasShownSinceSeek_ = false; + this.hide(); + } + } + this.previousTime_ = time; + } + } + videojs.registerComponent('Overlay', Overlay); + return Overlay; }; - return Overlay; -}(Component); + const Plugin = videojs__default["default"].getPlugin('plugin'); + const defaults = { + align: 'top-left', + class: '', + content: 'This overlay will show up while the video is playing', + debug: false, + showBackground: true, + attachToControlBar: false, + overlays: [{ + start: 'playing', + end: 'paused' + }] + }; -videojs.registerComponent('Overlay', Overlay); -/** - * Initialize the plugin. - * - * @function plugin - * @param {Object} [options={}] - */ + /** + * A plugin for handling overlays in the Brightcove Player. + */ + class OverlayPlugin extends Plugin { + /** + * Create an Overlay Plugin instance. + * + * @param {Player} player + * A Video.js Player instance. + * + * @param {Object} [options] + * An options object. + */ + constructor(player, options) { + super(player); + this.reset(options); + } -var plugin = function plugin(options) { - var _this2 = this; - - var settings = videojs.mergeOptions(defaults, options); // De-initialize the plugin if it already has an array of overlays. - - if (Array.isArray(this.overlays_)) { - this.overlays_.forEach(function (overlay) { - _this2.removeChild(overlay); - - if (_this2.controlBar) { - _this2.controlBar.removeChild(overlay); + /** + * Adds one or more items to the existing list of overlays. + * + * @param {Object|Array} item + * An item (or an array of items) to be added as overlay/s + * + * @return {Array[Overlay]} + * The array of overlay objects that were added + */ + add(item) { + if (!Array.isArray(item)) { + item = [item]; } + const addedOverlays = this.mapOverlays_(item); + this.player.overlays_ = this.player.overlays_.concat(addedOverlays); + return addedOverlays; + } - overlay.dispose(); - }); + /** + * + * @param {Overlay} item + * An item to be removed from the array of overlays + * + * @throws {Error} + * Item to remove must be present in the array of overlays + * + */ + remove(item) { + const index = this.player.overlays_.indexOf(item); + if (index !== -1) { + item.el().parentNode.removeChild(item.el()); + this.player.overlays_.splice(index, 1); + } else { + this.player.log.warn('overlay does not exist and cannot be removed'); + } + } + + /** + * Gets the array of overlays used for the current video + * + * @return The array of overlay objects currently used by the plugin + */ + get() { + return this.player.overlays_; + } + + /** + * Updates the overlay options + * + * @param {Object} [options] + * An options object. + */ + reset(options) { + this.clearOverlays_(); + + // Use merge function based on video.js version. + const merge = videojs__default["default"].obj && videojs__default["default"].obj.merge || videojs__default["default"].mergeOptions; + this.options = merge(defaults, options); + const overlays = this.options.overlays; + + // We don't want to keep the original array of overlay options around + // because it doesn't make sense to pass it to each Overlay component. + delete this.options.overlays; + this.player.overlays_ = this.mapOverlays_(overlays); + } + + /** + * Disposes the plugin + */ + dispose() { + this.clearOverlays_(); + delete this.player.overlays_; + super.dispose(); + } + clearOverlays_() { + // Remove child components + if (Array.isArray(this.player.overlays_)) { + this.player.overlays_.forEach(overlay => { + this.player.removeChild(overlay); + if (this.player.controlBar) { + this.player.controlBar.removeChild(overlay); + } + }); + } + } + mapOverlays_(items) { + return items.map(o => { + const mergeOptions = videojs__default["default"].mergeOptions(this.options, o); + const attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true; + if (!this.player.controls() || !this.player.controlBar) { + return this.player.addChild('overlay', mergeOptions); + } + if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) { + let referenceChild = this.player.controlBar.children()[0]; + if (this.player.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) { + referenceChild = this.player.controlBar.getChild(mergeOptions.attachToControlBar); + } + if (referenceChild) { + const referenceChildIndex = this.player.controlBar.children().indexOf(referenceChild); + const controlBarChild = this.player.controlBar.addChild('overlay', mergeOptions, referenceChildIndex); + return controlBarChild; + } + } + const playerChild = this.player.addChild('overlay', mergeOptions); + this.player.el().insertBefore(playerChild.el(), this.player.controlBar.el()); + return playerChild; + }); + } } - var overlays = settings.overlays; // We don't want to keep the original array of overlay options around - // because it doesn't make sense to pass it to each Overlay component. + var version = "4.0.0"; - delete settings.overlays; - this.overlays_ = overlays.map(function (o) { - var mergeOptions = videojs.mergeOptions(settings, o); - var attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true; + initOverlayComponent(videojs__default["default"]); + OverlayPlugin.VERSION = version; + videojs__default["default"].registerPlugin('overlay', OverlayPlugin); - if (!_this2.controls() || !_this2.controlBar) { - return _this2.addChild('overlay', mergeOptions); - } + return OverlayPlugin; - if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) { - var referenceChild = _this2.controlBar.children()[0]; - - if (_this2.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) { - referenceChild = _this2.controlBar.getChild(mergeOptions.attachToControlBar); - } - - if (referenceChild) { - var referenceChildIndex = _this2.controlBar.children().indexOf(referenceChild); - - var controlBarChild = _this2.controlBar.addChild('overlay', mergeOptions, referenceChildIndex); - - return controlBarChild; - } - } - - var playerChild = _this2.addChild('overlay', mergeOptions); - - _this2.el().insertBefore(playerChild.el(), _this2.controlBar.el()); - - return playerChild; - }); -}; - -plugin.VERSION = version; -registerPlugin('overlay', plugin); - -module.exports = plugin; +})); diff --git a/public/_player/videojs/dist/videojs-overlay.min.css b/public/_player/videojs/dist/videojs-overlay.min.css index 3ba5a57..54e6bd4 100644 --- a/public/_player/videojs/dist/videojs-overlay.min.css +++ b/public/_player/videojs/dist/videojs-overlay.min.css @@ -1 +1,7 @@ -.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:rgba(255,255,255,0.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px} +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/videojs-overlay@4.0.0/dist/videojs-overlay.css + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:hsla(0,0%,100%,.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px}/*# sourceMappingURL=videojs-overlay.css.map */ diff --git a/public/_player/videojs/dist/videojs-overlay.min.js b/public/_player/videojs/dist/videojs-overlay.min.js index 5eb82cd..7351c9d 100644 --- a/public/_player/videojs/dist/videojs-overlay.min.js +++ b/public/_player/videojs/dist/videojs-overlay.min.js @@ -1,2 +1,2 @@ -/*! @name videojs-overlay @version 2.1.5 @license Apache-2.0 */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("video.js"),require("global/window")):"function"==typeof define&&define.amd?define(["video.js","global/window"],e):t.videojsOverlay=e(t.videojs,t.window)}(this,function(t,e){"use strict";function n(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}t=t&&t.hasOwnProperty("default")?t.default:t,e=e&&e.hasOwnProperty("default")?e.default:e;var i={align:"top-left",class:"",content:"This overlay will show up while the video is playing",debug:!1,showBackground:!0,attachToControlBar:!1,overlays:[{start:"playing",end:"paused"}]},r=t.getComponent("Component"),o=t.dom||t,s=t.registerPlugin||t.plugin,a=function(t){return"number"==typeof t&&t==t},h=function(t){return"string"==typeof t&&/^\S+$/.test(t)},d=function(i){var r,s;function d(t,e){var r;return r=i.call(this,t,e)||this,["start","end"].forEach(function(t){var e=r.options_[t];if(a(e))r[t+"Event_"]="timeupdate";else if(h(e))r[t+"Event_"]=e;else if("start"===t)throw new Error('invalid "start" option; expected number or string')}),["endListener_","rewindListener_","startListener_"].forEach(function(t){r[t]=function(e){return d.prototype[t].call(n(n(r)),e)}}),"timeupdate"===r.startEvent_&&r.on(t,"timeupdate",r.rewindListener_),r.debug('created, listening to "'+r.startEvent_+'" for "start" and "'+(r.endEvent_||"nothing")+'" for "end"'),r.hide(),r}s=i,(r=d).prototype=Object.create(s.prototype),r.prototype.constructor=r,r.__proto__=s;var l=d.prototype;return l.createEl=function(){var t=this.options_,n=t.content,i=t.showBackground?"vjs-overlay-background":"vjs-overlay-no-background",r=o.createEl("div",{className:"\n vjs-overlay\n vjs-overlay-"+t.align+"\n "+t.class+"\n "+i+"\n vjs-hidden\n "});return"string"==typeof n?r.innerHTML=n:n instanceof e.DocumentFragment?r.appendChild(n):o.appendContent(r,n),r},l.debug=function(){if(this.options_.debug){for(var e=t.log,n=e,i=arguments.length,r=new Array(i),o=0;o=n:n===e},l.show=function(){return i.prototype.show.call(this),this.off(this.player(),this.startEvent_,this.startListener_),this.debug("shown"),this.debug('unbound `startListener_` from "'+this.startEvent_+'"'),this.endEvent_&&(this.debug('bound `endListener_` to "'+this.endEvent_+'"'),this.on(this.player(),this.endEvent_,this.endListener_)),this},l.shouldShow_=function(t,e){var n=this.options_.start,i=this.options_.end;return a(n)?a(i)?t>=n&&t=n):n===e},l.startListener_=function(t){var e=this.player().currentTime();this.shouldShow_(e,t.type)&&this.show()},l.endListener_=function(t){var e=this.player().currentTime();this.shouldHide_(e,t.type)&&this.hide()},l.rewindListener_=function(t){var e=this.player().currentTime(),n=this.previousTime_,i=this.options_.start,r=this.options_.end;e{this.player.removeChild(e),this.player.controlBar&&this.player.controlBar.removeChild(e)}))}mapOverlays_(e){return e.map((e=>{const t=s.default.mergeOptions(this.options,e),i="string"==typeof t.attachToControlBar||!0===t.attachToControlBar;if(!this.player.controls()||!this.player.controlBar)return this.player.addChild("overlay",t);if(i&&-1!==t.align.indexOf("bottom")){let e=this.player.controlBar.children()[0];if(void 0!==this.player.controlBar.getChild(t.attachToControlBar)&&(e=this.player.controlBar.getChild(t.attachToControlBar)),e){const s=this.player.controlBar.children().indexOf(e);return this.player.controlBar.addChild("overlay",t,s)}}const r=this.player.addChild("overlay",t);return this.player.el().insertBefore(r.el(),this.player.controlBar.el()),r}))}}return(e=>{const t=e.getComponent("Component"),s=e.dom||e,i=e=>"number"==typeof e&&e==e,r=e=>"string"==typeof e&&/^\S+$/.test(e);class n extends t{constructor(e,t){super(e,t),["start","end"].forEach((e=>{const t=this.options_[e];if(i(t))this[e+"Event_"]="timeupdate";else if(r(t))this[e+"Event_"]=t;else if("start"===e)throw new Error('invalid "start" option; expected number or string')})),["endListener_","rewindListener_","startListener_"].forEach((e=>{this[e]=t=>n.prototype[e].call(this,t)})),"timeupdate"===this.startEvent_&&this.on(e,"timeupdate",this.rewindListener_),this.debug(`created, listening to "${this.startEvent_}" for "start" and "${this.endEvent_||"nothing"}" for "end"`),this.hide()}createEl(){const e=this.options_,t=e.content,i=e.showBackground?"vjs-overlay-background":"vjs-overlay-no-background",r=s.createEl("div",{className:`\n vjs-overlay\n vjs-overlay-${e.align}\n ${e.class}\n ${i}\n vjs-hidden\n `});return"string"==typeof t?r.innerHTML=t:t instanceof window.DocumentFragment?r.appendChild(t):s.appendContent(r,t),r}debug(...t){if(!this.options_.debug)return;const s=e.log;let i=s;s.hasOwnProperty(t[0])&&"function"==typeof s[t[0]]&&(i=s[t.shift()]),i(`overlay#${this.id()}: `,...t)}hide(){return super.hide(),this.debug("hidden"),this.debug(`bound \`startListener_\` to "${this.startEvent_}"`),this.endEvent_&&(this.debug(`unbound \`endListener_\` from "${this.endEvent_}"`),this.off(this.player(),this.endEvent_,this.endListener_)),this.on(this.player(),this.startEvent_,this.startListener_),this}shouldHide_(e,t){const s=this.options_.end;return i(s)?e>=s:s===t}show(){return super.show(),this.off(this.player(),this.startEvent_,this.startListener_),this.debug("shown"),this.debug(`unbound \`startListener_\` from "${this.startEvent_}"`),this.endEvent_&&(this.debug(`bound \`endListener_\` to "${this.endEvent_}"`),this.on(this.player(),this.endEvent_,this.endListener_)),this}shouldShow_(e,t){const s=this.options_.start,r=this.options_.end;return i(s)?i(r)?e>=s&&e=s):s===t}startListener_(e){const t=this.player().currentTime();this.shouldShow_(t,e.type)&&this.show()}endListener_(e){const t=this.player().currentTime();this.shouldHide_(t,e.type)&&this.hide()}rewindListener_(e){const t=this.player().currentTime(),s=this.previousTime_,n=this.options_.start,o=this.options_.end;t { + const Component = videojs.getComponent('Component'); + const dom = videojs.dom || videojs; + + /** + * Whether the value is a `Number`. + * + * Both `Infinity` and `-Infinity` are accepted, but `NaN` is not. + * + * @param {Number} n + * @return {Boolean} + */ + + /* eslint-disable no-self-compare */ + const isNumber = n => typeof n === 'number' && n === n; + /* eslint-enable no-self-compare */ + + /** + * Whether a value is a string with no whitespace. + * + * @param {string} s + * @return {boolean} + */ + const hasNoWhitespace = s => typeof s === 'string' && /^\S+$/.test(s); + + /** + * Overlay component. + * + * @class Overlay + * @extends {videojs.Component} + */ + class Overlay extends Component { + constructor(player, options) { + super(player, options); + ['start', 'end'].forEach(key => { + const value = this.options_[key]; + if (isNumber(value)) { + this[key + 'Event_'] = 'timeupdate'; + } else if (hasNoWhitespace(value)) { + this[key + 'Event_'] = value; + + // An overlay MUST have a start option. Otherwise, it's pointless. + } else if (key === 'start') { + throw new Error('invalid "start" option; expected number or string'); + } + }); + + // video.js does not like components with multiple instances binding + // events to the player because it tracks them at the player level, + // not at the level of the object doing the binding. This could also be + // solved with Function.prototype.bind (but not videojs.bind because of + // its GUID magic), but the anonymous function approach avoids any issues + // caused by crappy libraries clobbering Function.prototype.bind. + // - https://github.com/videojs/video.js/issues/3097 + ['endListener_', 'rewindListener_', 'startListener_'].forEach(name => { + this[name] = e => Overlay.prototype[name].call(this, e); + }); + + // If the start event is a timeupdate, we need to watch for rewinds (i.e., + // when the user seeks backward). + if (this.startEvent_ === 'timeupdate') { + this.on(player, 'timeupdate', this.rewindListener_); + } + this.debug(`created, listening to "${this.startEvent_}" for "start" and "${this.endEvent_ || 'nothing'}" for "end"`); + this.hide(); + } + createEl() { + const options = this.options_; + const content = options.content; + const background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background'; + const el = dom.createEl('div', { + className: ` + vjs-overlay + vjs-overlay-${options.align} + ${options.class} + ${background} + vjs-hidden + ` + }); + if (typeof content === 'string') { + el.innerHTML = content; + } else if (content instanceof window.DocumentFragment) { + el.appendChild(content); + } else { + dom.appendContent(el, content); + } + return el; + } + + /** + * Logs debug errors + * + * @param {...[type]} args [description] + * @return {[type]} [description] + */ + debug(...args) { + if (!this.options_.debug) { + return; + } + const log = videojs.log; + let fn = log; + + // Support `videojs.log.foo` calls. + if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') { + fn = log[args.shift()]; + } + fn(...[`overlay#${this.id()}: `, ...args]); + } + + /** + * Overrides the inherited method to perform some event binding + * + * @return {Overlay} + */ + hide() { + super.hide(); + this.debug('hidden'); + this.debug(`bound \`startListener_\` to "${this.startEvent_}"`); + + // Overlays without an "end" are valid. + if (this.endEvent_) { + this.debug(`unbound \`endListener_\` from "${this.endEvent_}"`); + this.off(this.player(), this.endEvent_, this.endListener_); + } + this.on(this.player(), this.startEvent_, this.startListener_); + return this; + } + + /** + * Determine whether or not the overlay should hide. + * + * @param {number} time + * The current time reported by the player. + * @param {string} type + * An event type. + * @return {boolean} + */ + shouldHide_(time, type) { + const end = this.options_.end; + return isNumber(end) ? time >= end : end === type; + } + + /** + * Overrides the inherited method to perform some event binding + * + * @return {Overlay} + */ + show() { + super.show(); + this.off(this.player(), this.startEvent_, this.startListener_); + this.debug('shown'); + this.debug(`unbound \`startListener_\` from "${this.startEvent_}"`); + + // Overlays without an "end" are valid. + if (this.endEvent_) { + this.debug(`bound \`endListener_\` to "${this.endEvent_}"`); + this.on(this.player(), this.endEvent_, this.endListener_); + } + return this; + } + + /** + * Determine whether or not the overlay should show. + * + * @param {number} time + * The current time reported by the player. + * @param {string} type + * An event type. + * @return {boolean} + */ + shouldShow_(time, type) { + const start = this.options_.start; + const end = this.options_.end; + if (isNumber(start)) { + if (isNumber(end)) { + return time >= start && time < end; + + // In this case, the start is a number and the end is a string. We need + // to check whether or not the overlay has shown since the last seek. + } else if (!this.hasShownSinceSeek_) { + this.hasShownSinceSeek_ = true; + return time >= start; + } + + // In this case, the start is a number and the end is a string, but + // the overlay has shown since the last seek. This means that we need + // to be sure we aren't re-showing it at a later time than it is + // scheduled to appear. + return Math.floor(time) === start; + } + return start === type; + } + + /** + * Event listener that can trigger the overlay to show. + * + * @param {Event} e + */ + startListener_(e) { + const time = this.player().currentTime(); + if (this.shouldShow_(time, e.type)) { + this.show(); + } + } + + /** + * Event listener that can trigger the overlay to show. + * + * @param {Event} e + */ + endListener_(e) { + const time = this.player().currentTime(); + if (this.shouldHide_(time, e.type)) { + this.hide(); + } + } + + /** + * Event listener that can looks for rewinds - that is, backward seeks + * and may hide the overlay as needed. + * + * @param {Event} e + */ + rewindListener_(e) { + const time = this.player().currentTime(); + const previous = this.previousTime_; + const start = this.options_.start; + const end = this.options_.end; + + // Did we seek backward? + if (time < previous) { + this.debug('rewind detected'); + + // The overlay remains visible if two conditions are met: the end value + // MUST be an integer and the the current time indicates that the + // overlay should NOT be visible. + if (isNumber(end) && !this.shouldShow_(time)) { + this.debug(`hiding; ${end} is an integer and overlay should not show at this time`); + this.hasShownSinceSeek_ = false; + this.hide(); + + // If the end value is an event name, we cannot reliably decide if the + // overlay should still be displayed based solely on time; so, we can + // only queue it up for showing if the seek took us to a point before + // the start time. + } else if (hasNoWhitespace(end) && time < start) { + this.debug(`hiding; show point (${start}) is before now (${time}) and end point (${end}) is an event`); + this.hasShownSinceSeek_ = false; + this.hide(); + } + } + this.previousTime_ = time; + } + } + videojs.registerComponent('Overlay', Overlay); + return Overlay; +}; + +const Plugin = videojs.getPlugin('plugin'); +const defaults = { + align: 'top-left', + class: '', + content: 'This overlay will show up while the video is playing', + debug: false, + showBackground: true, + attachToControlBar: false, + overlays: [{ + start: 'playing', + end: 'paused' + }] +}; + +/** + * A plugin for handling overlays in the Brightcove Player. + */ +class OverlayPlugin extends Plugin { + /** + * Create an Overlay Plugin instance. + * + * @param {Player} player + * A Video.js Player instance. + * + * @param {Object} [options] + * An options object. + */ + constructor(player, options) { + super(player); + this.reset(options); + } + + /** + * Adds one or more items to the existing list of overlays. + * + * @param {Object|Array} item + * An item (or an array of items) to be added as overlay/s + * + * @return {Array[Overlay]} + * The array of overlay objects that were added + */ + add(item) { + if (!Array.isArray(item)) { + item = [item]; + } + const addedOverlays = this.mapOverlays_(item); + this.player.overlays_ = this.player.overlays_.concat(addedOverlays); + return addedOverlays; + } + + /** + * + * @param {Overlay} item + * An item to be removed from the array of overlays + * + * @throws {Error} + * Item to remove must be present in the array of overlays + * + */ + remove(item) { + const index = this.player.overlays_.indexOf(item); + if (index !== -1) { + item.el().parentNode.removeChild(item.el()); + this.player.overlays_.splice(index, 1); + } else { + this.player.log.warn('overlay does not exist and cannot be removed'); + } + } + + /** + * Gets the array of overlays used for the current video + * + * @return The array of overlay objects currently used by the plugin + */ + get() { + return this.player.overlays_; + } + + /** + * Updates the overlay options + * + * @param {Object} [options] + * An options object. + */ + reset(options) { + this.clearOverlays_(); + + // Use merge function based on video.js version. + const merge = videojs.obj && videojs.obj.merge || videojs.mergeOptions; + this.options = merge(defaults, options); + const overlays = this.options.overlays; + + // We don't want to keep the original array of overlay options around + // because it doesn't make sense to pass it to each Overlay component. + delete this.options.overlays; + this.player.overlays_ = this.mapOverlays_(overlays); + } + + /** + * Disposes the plugin + */ + dispose() { + this.clearOverlays_(); + delete this.player.overlays_; + super.dispose(); + } + clearOverlays_() { + // Remove child components + if (Array.isArray(this.player.overlays_)) { + this.player.overlays_.forEach(overlay => { + this.player.removeChild(overlay); + if (this.player.controlBar) { + this.player.controlBar.removeChild(overlay); + } + }); + } + } + mapOverlays_(items) { + return items.map(o => { + const mergeOptions = videojs.mergeOptions(this.options, o); + const attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true; + if (!this.player.controls() || !this.player.controlBar) { + return this.player.addChild('overlay', mergeOptions); + } + if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) { + let referenceChild = this.player.controlBar.children()[0]; + if (this.player.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) { + referenceChild = this.player.controlBar.getChild(mergeOptions.attachToControlBar); + } + if (referenceChild) { + const referenceChildIndex = this.player.controlBar.children().indexOf(referenceChild); + const controlBarChild = this.player.controlBar.addChild('overlay', mergeOptions, referenceChildIndex); + return controlBarChild; + } + } + const playerChild = this.player.addChild('overlay', mergeOptions); + this.player.el().insertBefore(playerChild.el(), this.player.controlBar.el()); + return playerChild; + }); + } +} + +var version = "4.0.0"; + +initOverlayComponent(videojs); +OverlayPlugin.VERSION = version; +videojs.registerPlugin('overlay', OverlayPlugin); + +export { OverlayPlugin as default }; diff --git a/src/misc/Player/videojs.js b/src/misc/Player/videojs.js index 8d067e5..87bf9c8 100644 --- a/src/misc/Player/videojs.js +++ b/src/misc/Player/videojs.js @@ -3,11 +3,11 @@ import React from 'react'; import Grid from '@mui/material/Grid'; import videojs from 'video.js'; -import 'videojs-overlay'; +import overlay from './videojs-overlay.es.js'; import 'video.js/dist/video-js.min.css'; import './video-js-skin-internal.min.css'; import './video-js-skin-public.min.css'; -import 'videojs-overlay/dist/videojs-overlay.css'; +import './videojs-overlay.css'; export default function VideoJS({ type = 'videojs-internal', options = {}, onReady = null }) { const videoRef = React.useRef(null); @@ -28,6 +28,8 @@ export default function VideoJS({ type = 'videojs-internal', options = {}, onRea const videoElement = videoRef.current; if (!videoElement) return; + videojs.registerPlugin('overlay', overlay); + const player = (playerRef.current = videojs(videoElement, options, () => { onReady && onReady(player); })); diff --git a/yarn.lock b/yarn.lock index b9e8d82..4728084 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11999,10 +11999,10 @@ videojs-font@4.2.0: resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-4.2.0.tgz#fbce803d347c565816e296f527e208dc65c9f235" integrity sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ== -videojs-overlay@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/videojs-overlay/-/videojs-overlay-3.1.0.tgz#d57505d375eca952feeb36e5b33e0a130e3dc9e0" - integrity sha512-P863Z4ghWgf7Z4A4uzmHlqIixRb8v5220JuQ4pfb/uorbWSBCt5D+czrp/eTxXXLtSmrSUKn596QswVYZuMzPg== +videojs-overlay@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/videojs-overlay/-/videojs-overlay-4.0.0.tgz#aa450b3e6e0a86d9bfc61eff66bbdd9f6ed1f07d" + integrity sha512-R+DqPhq8iiyNXUgnXr3ZKWXjLmyiztyvzmND4jr+Fa/MaxN6dMwG7hvqb+la24Qaqh8Jxprnw62vqQ0vc8QoHQ== dependencies: global "^4.3.2" video.js "^6 || ^7 || ^8"