cloudinary-video-player
Version:
Cloudinary Video Player
1,641 lines (1,458 loc) • 210 kB
JavaScript
import { _ as _vjs } from './_videojs-proxy.js';
import { g as getDefaultExportFromCjs } from './_commonjsHelpers.js';
import { e as require_baseKeys, h as require_getTag, j as requireIsArguments, k as requireIsArrayLike, l as requireIsBuffer, m as requireIsTypedArray, n as require_isPrototype, q as require_assignValue, t as require_castPath, u as require_toKey, v as require_isIndex, w as require_baseGet, x as requireIsLength, y as require_flatRest, P as PLAYER_EVENT, c as componentUtils, s as sliceProperties, z as isElementInViewport, V as VIDEO_CODEC, A as isKeyInTransformation, B as BaseSource, C as normalizeOptions, S as SOURCE_TYPE, I as ImageSource, T as Transformation, D as isSrcEqual, E as castArray, F as mergeTransformations, G as sliceAndUnsetProperties, H as setupCloudinaryMiddleware, J as extendCloudinaryConfig, K as omitVideoOnlyTransformations, i as isPlainObject, L as isElement, M as setPosition, N as getPointerPosition, O as fontFace, p as playerClassPrefix, U as Utils, Q as defaults$6, R as ERROR_CODE, a as get } from './index2.js';
import { c as requireIsArray, a as requireIsObject, i as isObject, d as isString, S as SOURCE_PARAMS, C as CROP_MODE, F as FLOATING_TO, e as FLUID_CLASS_NAME, P as PLAYER_PARAMS, A as AUTO_PLAY_MODE } from './video-player.const.js';
import { i as isFunction, a as isValidPlayerConfig, b as isValidSourceConfig } from './validators-functions.js';
import { a as appendQueryParams, u as utf8ToBase64, i as isRawUrl, g as getCloudinaryConfigFromOptions, C as CLOUDINARY_CONFIG_PARAM } from './cloudinary-config-from-options.js';
import { g as getCloudinaryUrlPrefix } from './cloudinary-url-prefix.js';
const byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 0x100).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return (byteToHex[arr[offset + 0]] +
byteToHex[arr[offset + 1]] +
byteToHex[arr[offset + 2]] +
byteToHex[arr[offset + 3]] +
'-' +
byteToHex[arr[offset + 4]] +
byteToHex[arr[offset + 5]] +
'-' +
byteToHex[arr[offset + 6]] +
byteToHex[arr[offset + 7]] +
'-' +
byteToHex[arr[offset + 8]] +
byteToHex[arr[offset + 9]] +
'-' +
byteToHex[arr[offset + 10]] +
byteToHex[arr[offset + 11]] +
byteToHex[arr[offset + 12]] +
byteToHex[arr[offset + 13]] +
byteToHex[arr[offset + 14]] +
byteToHex[arr[offset + 15]]).toLowerCase();
}
const rnds8 = new Uint8Array(16);
function rng() {
return crypto.getRandomValues(rnds8);
}
function v4(options, buf, offset) {
if (crypto.randomUUID) {
return crypto.randomUUID();
}
return _v4(options);
}
function _v4(options, buf, offset) {
options = options || {};
const rnds = options.random ?? options.rng?.() ?? rng();
if (rnds.length < 16) {
throw new Error('Random bytes length must be >= 16');
}
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
return unsafeStringify(rnds);
}
var isEmpty_1;
var hasRequiredIsEmpty;
function requireIsEmpty () {
if (hasRequiredIsEmpty) return isEmpty_1;
hasRequiredIsEmpty = 1;
var baseKeys = require_baseKeys(),
getTag = require_getTag(),
isArguments = requireIsArguments(),
isArray = requireIsArray(),
isArrayLike = requireIsArrayLike(),
isBuffer = requireIsBuffer(),
isPrototype = require_isPrototype(),
isTypedArray = requireIsTypedArray();
/** `Object#toString` result references. */
var mapTag = '[object Map]',
setTag = '[object Set]';
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Checks if `value` is an empty object, collection, map, or set.
*
* Objects are considered empty if they have no own enumerable string keyed
* properties.
*
* Array-like values such as `arguments` objects, arrays, buffers, strings, or
* jQuery-like collections are considered empty if they have a `length` of `0`.
* Similarly, maps and sets are considered empty if they have a `size` of `0`.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is empty, else `false`.
* @example
*
* _.isEmpty(null);
* // => true
*
* _.isEmpty(true);
* // => true
*
* _.isEmpty(1);
* // => true
*
* _.isEmpty([1, 2, 3]);
* // => false
*
* _.isEmpty({ 'a': 1 });
* // => false
*/
function isEmpty(value) {
if (value == null) {
return true;
}
if (isArrayLike(value) &&
(isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
isBuffer(value) || isTypedArray(value) || isArguments(value))) {
return !value.length;
}
var tag = getTag(value);
if (tag == mapTag || tag == setTag) {
return !value.size;
}
if (isPrototype(value)) {
return !baseKeys(value).length;
}
for (var key in value) {
if (hasOwnProperty.call(value, key)) {
return false;
}
}
return true;
}
isEmpty_1 = isEmpty;
return isEmpty_1;
}
var isEmptyExports = requireIsEmpty();
var isEmpty = /*@__PURE__*/getDefaultExportFromCjs(isEmptyExports);
var _baseSet;
var hasRequired_baseSet;
function require_baseSet () {
if (hasRequired_baseSet) return _baseSet;
hasRequired_baseSet = 1;
var assignValue = require_assignValue(),
castPath = require_castPath(),
isIndex = require_isIndex(),
isObject = requireIsObject(),
toKey = require_toKey();
/**
* The base implementation of `_.set`.
*
* @private
* @param {Object} object The object to modify.
* @param {Array|string} path The path of the property to set.
* @param {*} value The value to set.
* @param {Function} [customizer] The function to customize path creation.
* @returns {Object} Returns `object`.
*/
function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
return object;
}
path = castPath(path, object);
var index = -1,
length = path.length,
lastIndex = length - 1,
nested = object;
while (nested != null && ++index < length) {
var key = toKey(path[index]),
newValue = value;
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
return object;
}
if (index != lastIndex) {
var objValue = nested[key];
newValue = customizer ? customizer(objValue, key, nested) : undefined;
if (newValue === undefined) {
newValue = isObject(objValue)
? objValue
: (isIndex(path[index + 1]) ? [] : {});
}
}
assignValue(nested, key, newValue);
nested = nested[key];
}
return object;
}
_baseSet = baseSet;
return _baseSet;
}
var _basePickBy;
var hasRequired_basePickBy;
function require_basePickBy () {
if (hasRequired_basePickBy) return _basePickBy;
hasRequired_basePickBy = 1;
var baseGet = require_baseGet(),
baseSet = require_baseSet(),
castPath = require_castPath();
/**
* The base implementation of `_.pickBy` without support for iteratee shorthands.
*
* @private
* @param {Object} object The source object.
* @param {string[]} paths The property paths to pick.
* @param {Function} predicate The function invoked per property.
* @returns {Object} Returns the new object.
*/
function basePickBy(object, paths, predicate) {
var index = -1,
length = paths.length,
result = {};
while (++index < length) {
var path = paths[index],
value = baseGet(object, path);
if (predicate(value, path)) {
baseSet(result, castPath(path, object), value);
}
}
return result;
}
_basePickBy = basePickBy;
return _basePickBy;
}
/**
* The base implementation of `_.hasIn` without support for deep paths.
*
* @private
* @param {Object} [object] The object to query.
* @param {Array|string} key The key to check.
* @returns {boolean} Returns `true` if `key` exists, else `false`.
*/
var _baseHasIn;
var hasRequired_baseHasIn;
function require_baseHasIn () {
if (hasRequired_baseHasIn) return _baseHasIn;
hasRequired_baseHasIn = 1;
function baseHasIn(object, key) {
return object != null && key in Object(object);
}
_baseHasIn = baseHasIn;
return _baseHasIn;
}
var _hasPath;
var hasRequired_hasPath;
function require_hasPath () {
if (hasRequired_hasPath) return _hasPath;
hasRequired_hasPath = 1;
var castPath = require_castPath(),
isArguments = requireIsArguments(),
isArray = requireIsArray(),
isIndex = require_isIndex(),
isLength = requireIsLength(),
toKey = require_toKey();
/**
* Checks if `path` exists on `object`.
*
* @private
* @param {Object} object The object to query.
* @param {Array|string} path The path to check.
* @param {Function} hasFunc The function to check properties.
* @returns {boolean} Returns `true` if `path` exists, else `false`.
*/
function hasPath(object, path, hasFunc) {
path = castPath(path, object);
var index = -1,
length = path.length,
result = false;
while (++index < length) {
var key = toKey(path[index]);
if (!(result = object != null && hasFunc(object, key))) {
break;
}
object = object[key];
}
if (result || ++index != length) {
return result;
}
length = object == null ? 0 : object.length;
return !!length && isLength(length) && isIndex(key, length) &&
(isArray(object) || isArguments(object));
}
_hasPath = hasPath;
return _hasPath;
}
var hasIn_1;
var hasRequiredHasIn;
function requireHasIn () {
if (hasRequiredHasIn) return hasIn_1;
hasRequiredHasIn = 1;
var baseHasIn = require_baseHasIn(),
hasPath = require_hasPath();
/**
* Checks if `path` is a direct or inherited property of `object`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Object
* @param {Object} object The object to query.
* @param {Array|string} path The path to check.
* @returns {boolean} Returns `true` if `path` exists, else `false`.
* @example
*
* var object = _.create({ 'a': _.create({ 'b': 2 }) });
*
* _.hasIn(object, 'a');
* // => true
*
* _.hasIn(object, 'a.b');
* // => true
*
* _.hasIn(object, ['a', 'b']);
* // => true
*
* _.hasIn(object, 'b');
* // => false
*/
function hasIn(object, path) {
return object != null && hasPath(object, path, baseHasIn);
}
hasIn_1 = hasIn;
return hasIn_1;
}
var _basePick;
var hasRequired_basePick;
function require_basePick () {
if (hasRequired_basePick) return _basePick;
hasRequired_basePick = 1;
var basePickBy = require_basePickBy(),
hasIn = requireHasIn();
/**
* The base implementation of `_.pick` without support for individual
* property identifiers.
*
* @private
* @param {Object} object The source object.
* @param {string[]} paths The property paths to pick.
* @returns {Object} Returns the new object.
*/
function basePick(object, paths) {
return basePickBy(object, paths, function(value, path) {
return hasIn(object, path);
});
}
_basePick = basePick;
return _basePick;
}
var pick_1;
var hasRequiredPick;
function requirePick () {
if (hasRequiredPick) return pick_1;
hasRequiredPick = 1;
var basePick = require_basePick(),
flatRest = require_flatRest();
/**
* Creates an object composed of the picked `object` properties.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The source object.
* @param {...(string|string[])} [paths] The property paths to pick.
* @returns {Object} Returns the new object.
* @example
*
* var object = { 'a': 1, 'b': '2', 'c': 3 };
*
* _.pick(object, ['a', 'c']);
* // => { 'a': 1, 'c': 3 }
*/
var pick = flatRest(function(object, paths) {
return object == null ? {} : basePick(object, paths);
});
pick_1 = pick;
return pick_1;
}
var pickExports = requirePick();
var pick = /*@__PURE__*/getDefaultExportFromCjs(pickExports);
const ErrorDisplay = _vjs.getComponent('ErrorDisplay');
class CldErrorDisplay extends ErrorDisplay {
content() {
const error = this.player().error();
if (!error) return '';
const wrapper = _vjs.dom.createEl('div', {
className: 'cld-error-display-content'
});
const header = _vjs.dom.createEl('div', {
className: 'cld-error-header'
});
const title = _vjs.dom.createEl('span', {
className: 'cld-error-title'
}, {}, this.localize('Something went wrong'));
const msg = _vjs.dom.createEl('div', {
className: 'cld-error-message'
});
header.appendChild(title);
if (error.isHtml) {
msg.innerHTML = this.localize(error.message);
} else {
msg.textContent = this.localize(error.message);
}
wrapper.appendChild(header);
wrapper.appendChild(msg);
const refreshLink = msg.querySelector('.cld-error-refresh');
if (refreshLink) {
refreshLink.onclick = e => {
e.preventDefault();
this.player().trigger(PLAYER_EVENT.REFRESH);
};
}
return wrapper;
}
}
const ClickableComponent$2 = _vjs.getComponent('ClickableComponent');
class JumpForwardButton extends ClickableComponent$2 {
handleClick(event) {
super.handleClick(event);
this.player().currentTime(this.player().currentTime() + 10);
}
createEl() {
return _vjs.dom.createEl('button', {
className: 'vjs-control vjs-icon-skip-10-plus vjs-icon-forward-10 vjs-button',
ariaLabel: 'Jump forward 10 seconds'
});
}
}
_vjs.registerComponent('JumpForwardButton', JumpForwardButton);
const ClickableComponent$1 = _vjs.getComponent('ClickableComponent');
class JumpBackButton extends ClickableComponent$1 {
handleClick(event) {
super.handleClick(event);
this.player().currentTime(this.player().currentTime() - 10);
}
createEl() {
return _vjs.dom.createEl('button', {
className: 'vjs-control vjs-icon-skip-10-min vjs-icon-replay-10 vjs-button',
ariaLabel: 'Jump back 10 seconds'
});
}
}
_vjs.registerComponent('JumpBackButton', JumpBackButton);
// support VJS5 & VJS6 at the same time
const ClickableComponent = _vjs.getComponent('ClickableComponent');
class LogoButton extends ClickableComponent {
createEl() {
const opts = this.options_.playerOptions;
const display = opts.showLogo ? 'block' : 'none';
const bgImage = opts.logoImageUrl ? `background-image: url(${opts.logoImageUrl})` : '';
return _vjs.dom.createEl('a', {}, {
class: 'vjs-control vjs-cloudinary-button vjs-button',
href: opts.logoOnclickUrl,
target: '_blank',
style: `display: ${display}; ${bgImage}`,
'aria-label': 'Logo link'
});
}
}
_vjs.registerComponent('logoButton', LogoButton);
const Component$1 = _vjs.getComponent('Component');
class ProgressControlEventsBlocker extends Component$1 {
constructor(player) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
super(player, options);
}
createEl() {
return super.createEl('div', {
className: 'vjs-progress-control-events-blocker'
});
}
}
_vjs.registerComponent('progressControlEventsBlocker', ProgressControlEventsBlocker);
// support VJS5 & VJS6 at the same time
const dom = _vjs.dom || _vjs;
const Component = _vjs.getComponent('Component');
class TitleBar extends Component {
constructor(player) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
super(player, options);
this.on(player, 'cldsourcechanged', (_, _ref) => {
let {
source
} = _ref;
return this.setItem(source);
});
}
setItem(source) {
if (!source) {
this.setTitle('');
this.setDescription('');
return;
}
const info = source.info();
this.setTitle(info.title);
this.setDescription(info.subtitle);
// auto-fetch title/description if `true`
const shouldFetchTitle = source.title && source.title() === true;
const shouldFetchDescription = source.description && source.description() === true;
if (shouldFetchTitle || shouldFetchDescription) {
this.fetchAutoMetadata(source, shouldFetchTitle, shouldFetchDescription);
}
}
fetchAutoMetadata(source, fetchTitle, fetchDescription) {
if (source.isRawUrl) return;
const config = source.cloudinaryConfig();
const publicId = source.publicId();
if (!config?.cloud_name || !publicId) return;
const urlPrefix = getCloudinaryUrlPrefix(config);
const deliveryType = source.getInitOptions().type || 'upload';
const metadataUrl = appendQueryParams(`${urlPrefix}/_applet_/video_service/video_metadata/${deliveryType}/${utf8ToBase64(publicId)}.json`, source.queryParams());
fetch(metadataUrl, {
headers: {
'X-Cld-Video-Player-Version': "4.0.3"
}
}).then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}).then(metadata => {
if (fetchTitle && metadata.title) {
this.setTitle(metadata.title);
}
if (fetchDescription && metadata.description) {
this.setDescription(metadata.description);
}
}).catch(error => {
console.warn(`Failed to fetch metadata for ${publicId}:`, error);
});
}
setTitle(text) {
const displayText = typeof text === 'string' ? text : '';
componentUtils.setText(this.titleEl, displayText);
this.refresh();
return displayText;
}
setDescription(text) {
const displayText = typeof text === 'string' ? text : '';
componentUtils.setText(this.descriptionEl, displayText);
this.refresh();
return displayText;
}
refresh() {
const titleValue = () => this.titleEl.innerText;
const descriptionValue = () => this.descriptionEl.innerText;
if (!titleValue() && !descriptionValue()) {
this.hide();
return;
}
this.show();
}
createEl() {
this.titleEl = dom.createEl('div', {
className: 'vjs-title-bar-title'
});
this.descriptionEl = dom.createEl('div', {
className: 'vjs-title-bar-subtitle'
});
const el = super.createEl('div', {
append: this.titleEl,
className: 'vjs-title-bar'
});
el.appendChild(this.titleEl);
el.appendChild(this.descriptionEl);
return el;
}
}
_vjs.registerComponent('titleBar', TitleBar);
const MenuItem$2 = _vjs.getComponent('MenuItem');
class SourceMenuItem extends MenuItem$2 {
constructor(player) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
super(player, {
...options,
selectable: true,
multiSelectable: false,
selected: !!options.selected,
label: options.label
});
this.value = options.value;
this._ssIndex = options.index;
this._onSelect = typeof options.onSelect === 'function' ? options.onSelect : null;
}
handleClick(event) {
super.handleClick(event);
if (this._onSelect) {
this._onSelect({
index: this._ssIndex,
value: this.value,
label: this.options_.label
});
}
}
}
_vjs.registerComponent('SourceMenuItem', SourceMenuItem);
const MenuButton = _vjs.getComponent('MenuButton');
const MenuItem$1 = _vjs.getComponent('MenuItem');
class SourceSwitcherButton extends MenuButton {
constructor(player) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
super(player, options);
this.controlText(options.tooltip || 'Sources');
this._emptyLabel = options.emptyLabel || 'No sources';
this._items = Array.isArray(options.items) ? options.items : [];
this._selectedIndex = Number.isInteger(options.defaultIndex) ? options.defaultIndex : undefined;
this._onSelected = typeof options.onSelected === 'function' ? options.onSelected : null;
this._setEnabled(this._items.length > 0);
const placeholder = this.el().querySelector('.vjs-icon-placeholder');
if (placeholder) {
placeholder.classList.add('vjs-icon-source-switcher');
}
}
buildCSSClass() {
const empty = !Array.isArray(this._items) || this._items.length === 0;
return `vjs-source-switcher-button${empty ? ' vjs-source-switcher-disabled' : ''} ${super.buildCSSClass()}`;
}
createItems() {
if (!Array.isArray(this._items) || this._items.length === 0) {
const empty = new MenuItem$1(this.player_, {
label: this._emptyLabel,
selectable: false
});
empty.addClass('vjs-source-switcher-empty');
empty.disable();
return [empty];
}
return this._items.map((_ref, index) => {
let {
label,
value
} = _ref;
return new SourceMenuItem(this.player_, {
label,
value,
index,
selected: index === this._selectedIndex,
onSelect: payload => this._handleItemSelect(payload)
});
});
}
_handleItemSelect(_ref2) {
let {
index
} = _ref2;
if (this._selectedIndex === index) return;
this.setSelected(index);
}
setItems(items) {
this._items = Array.isArray(items) ? items : [];
this._selectedIndex = this._items.length ? 0 : undefined;
this._setEnabled(this._items.length > 0);
this._rebuildMenu();
}
setSelected(index) {
if (!Array.isArray(this._items) || index == null || index < 0 || index >= this._items.length) return;
this._selectedIndex = index;
// reflect in UI if menu exists
if (this.menu && typeof this.menu.children === 'function') {
this.menu.children().forEach(child => {
if (child instanceof MenuItem$1) {
child.selected(child._ssIndex === index);
}
});
}
const {
value
} = this._items[index];
if (this._onSelected) this._onSelected({
index,
value
}, this.player_);
}
setOnSelected(fn) {
this._onSelected = typeof fn === 'function' ? fn : null;
}
_rebuildMenu() {
if (!this.menu) return;
this.menu.children().slice().forEach(c => this.menu.removeChild(c));
this.createItems().forEach(i => this.menu.addItem(i));
const el = this.el && this.el();
if (el) {
const empty = this._items.length === 0;
el.classList.toggle('vjs-source-switcher-disabled', empty);
el.setAttribute('aria-disabled', String(empty));
}
}
_setEnabled(enabled) {
const el = this.el && this.el();
if (!el) return;
el.classList.toggle('vjs-source-switcher-disabled', !enabled);
el.setAttribute('aria-disabled', String(!enabled));
}
}
_vjs.registerComponent('sourceSwitcherButton', SourceSwitcherButton);
const BigPlayButton = _vjs.getComponent('BigPlayButton');
class BigPauseButton extends BigPlayButton {
constructor(player, options) {
super(player, options);
this.boundUpdate = this.handleUpdate.bind(this);
const playerInstance = this.player();
playerInstance.on('play', this.boundUpdate);
playerInstance.on('pause', this.boundUpdate);
this.handleUpdate();
}
buildCSSClass() {
return `${super.buildCSSClass()} vjs-big-pause-button`;
}
handleClick() {
const player = this.player();
!player.paused() && player.pause();
}
handleUpdate() {
const player = this.player();
if (!player) {
return;
}
const paused = player.paused();
!paused && player.hasStarted() ? this.show() : this.hide();
this[paused ? 'removeClass' : 'addClass']('vjs-playing');
this.controlText('Pause');
}
dispose() {
if (this.boundUpdate) {
const player = this.player();
player.off('play', this.boundUpdate);
player.off('pause', this.boundUpdate);
}
super.dispose();
}
}
_vjs.registerComponent('BigPauseButton', BigPauseButton);
_vjs.registerComponent('ErrorDisplay', CldErrorDisplay);
/*! @name videojs-per-source-behaviors @version 3.0.1 @license Apache-2.0 */
var version = "3.0.1";
const Html5 = _vjs.getTech('Html5'); // Video.js 5/6 cross-compatibility.
const registerPlugin = _vjs.registerPlugin || _vjs.plugin;
/**
* For the most part, these are the events that occur early in the lifecycle
* of a source, but there is considerable variability across browsers and
* devices (not to mention properties like autoplay and preload). As such, we
* listen to a bunch of events for source changes.
*
* @type {Array}
*/
const CHANGE_DETECT_EVENTS = ['abort', 'emptied', 'loadstart', 'play'];
/**
* These events will indicate that the source is "unstable" (i.e. it might be
* about to change).
*
* @type {Array}
*/
const UNSTABLE_EVENTS = ['abort', 'emptied'];
/**
* These are the ad loading and playback states we care about from
* contrib-ads 5.
*
* @type {Array}
*/
const AD_STATES = ['ad-playback', 'ads-ready?', 'postroll?', 'preroll?'];
/**
* Whether or not a string represents an "ad state".
*
* @param {string} s
* The string to check
*
* @return {boolean}
* If the string is an ad state
*/
const isAdState = s => AD_STATES.indexOf(s) > -1;
/**
* Determine whether or not a player is using contrib-ads 6+.
*
* @param {Player} p
* The player
*
* @return {boolean}
* if the player is using contrib-ads 6+
*/
const usingAds6Plus = p => p.usingPlugin('ads') && typeof p.ads.inAdBreak === 'function';
/**
* Whether or not the player should ignore an event due to ad states.
*
* @param {Player} p
* The player
*
* @param {Event} e
* An event object
*
* @return {boolean}
* If the player is in an ad state
*/
const ignoreEvent = (p, e) => {
// No ads, no ad states to ignore!
if (!p.usingPlugin('ads')) {
return false;
} // In contrib-ads 6+ we only care about non-loadstart events because
// loadstart events _may_ happen during ad mode.
if (usingAds6Plus(p)) {
return p.ads.isInAdMode() && e.type !== 'loadstart';
} // contrib-ads 5+
return isAdState(p.ads.state);
};
let psbGuid = 0;
/**
* Applies per-source behaviors to a video.js Player object.
*/
const perSourceBehaviors = function () {
const perSrcListeners = [];
let cachedSrc;
let disabled = false;
let srcChangeTimer;
let srcStable = true;
/**
* Creates an event binder function of a given type.
*
* @param {boolean} isOne
* Rather than delegating to the player's `one()` method, we want to
* retain full control over when the listener is unbound (particularly
* due to the ability for per-source behaviors to be toggled on and
* off at will).
*
* @return {Function}
* the per source binder function
*/
const createPerSrcBinder = isOne => {
return function (first, second) {
// Do not bind new listeners when per-source behaviors are disabled.
if (this.perSourceBehaviors.disabled()) {
return;
}
const isTargetPlayer = arguments.length === 2;
const originalSrc = this.currentSrc(); // This array is the set of arguments to use for `on()` and `off()` methods
// of the player.
const args = [first]; // Make sure we bind here so that a GUID is set on the original listener
// and that it is bound to the proper context.
const passedListener = arguments[arguments.length - 1];
const listenerContext = isTargetPlayer ? this : first;
const originalListener = passedListener.bind(listenerContext); // The wrapped listener `subargs` are the arguments passed to the original
// listener (i.e. the Event object and an additional data hash).
const wrappedListener = (...subargs) => {
const changed = this.currentSrc() !== originalSrc; // Do not evaluate listeners if per-source behaviors are disabled.
if (this.perSourceBehaviors.disabled()) {
return;
}
if (changed || isOne) {
this.off(...args);
}
if (!changed) {
originalListener(...subargs);
}
}; // Make sure the wrapped listener and the original listener share a GUID,
// so that video.js properly removes event bindings when `off()` is passed
// the original listener!
//
// This property is used internally by Video.js for tracking bound
// listeners in its event system. Normally, Video.js creates this `guid`
// through its `bind` method, but that method is deprecated in public
// interfaces.
//
// Basically, this allows us to avoid deprecation warnings from calling
// `videojs.bind` when used with Video.js 8.
wrappedListener.guid = originalListener.guid = passedListener.guid = `psb-${++psbGuid}`; // If we are targeting a different object from the player, we need to include
// the second argument.
if (!isTargetPlayer) {
args.push(second);
}
args.push(wrappedListener);
perSrcListeners.push(args);
return this.on(...args);
};
};
this.perSourceBehaviors = {
/**
* Disable per-source behaviors on this player.
*
* @return {boolean}
*/
disable: function disable() {
this.clearTimeout(srcChangeTimer);
srcChangeTimer = null;
disabled = true;
return disabled;
}.bind(this),
/**
* Whether per-source behaviors are disabled on this player.
*
* @return {boolean}
* if the per-source behaviors are disabled
*/
disabled() {
return disabled;
},
/**
* Enable per-source behaviors on this player.
*
* @return {boolean}
* always returns true
*/
enable() {
disabled = false;
return disabled;
},
/**
* Whether per-source behaviors are disabled on this player.
*
* @return {boolean}
* if the per-source behaviors are enabled
*/
enabled() {
return !disabled;
},
/**
* Whether or not the source is "stable". This will return `true` if the
* plugin feels that we may be about to change sources.
*
* @return {boolean}
* Whether the source is stable or not
*/
isSrcStable() {
return srcStable;
},
VERSION: version
};
/**
* Bind an event listener on a per-source basis.
*
* @function onPerSrc
* @param {String|Array|Component|Element} first
* The event type(s) or target Component or Element.
*
* @param {Function|String|Array} second
* The event listener or event type(s) (when `first` is target).
*
* @param {Function} third
* The event listener (when `second` is event type(s)).
*
* @return {Player}
*/
this.onPerSrc = createPerSrcBinder();
/**
* Bind an event listener on a per-source basis. This listener can only
* be called once.
*
* @function onePerSrc
* @param {String|Array|Component|Element} first
* The event type(s) or target Component or Element.
*
* @param {Function|String|Array} second
* The event listener or event type(s) (when `first` is target).
*
* @param {Function} third
* The event listener (when `second` is event type(s)).
*
* @return {Player}
*/
this.onePerSrc = createPerSrcBinder(true); // Clear out perSrcListeners cache on player dispose.
this.on('dispose', () => {
perSrcListeners.length = 0;
});
this.on(CHANGE_DETECT_EVENTS, e => {
// Bail-out conditions.
if (this.perSourceBehaviors.disabled() || srcChangeTimer || ignoreEvent(this, e)) {
return;
} // If we did not previously detect that we were in an unstable state and
// this was an event that qualifies as unstable, do that now. In the future,
// we may want to restrict the conditions under which this is triggered by
// checking networkState and/or readyState for reasonable values such as
// NETWORK_NO_SOURCE and HAVE_NOTHING.
if (srcStable && UNSTABLE_EVENTS.indexOf(e.type) > -1) {
srcStable = false;
this.trigger('sourceunstable');
} // Track any and all interim events from this one until the next tick
// when we evaluate the timer.
const interimEvents = [];
const addInterimEvent = f => interimEvents.push({
time: Date.now(),
event: f
});
addInterimEvent(e);
this.on(Html5.Events, addInterimEvent);
srcChangeTimer = this.setTimeout(() => {
const currentSrc = this.currentSrc();
srcStable = true;
srcChangeTimer = null;
this.off(Html5.Events, addInterimEvent);
if (currentSrc && currentSrc !== cachedSrc) {
// Remove per-source listeners explicitly when we know the source has
// changed before we trigger the "sourcechanged" listener.
perSrcListeners.forEach(args => this.off(...args));
perSrcListeners.length = 0;
this.trigger('sourcechanged', {
interimEvents,
from: cachedSrc,
to: currentSrc
});
cachedSrc = currentSrc;
}
}, 1);
});
};
perSourceBehaviors.VERSION = version;
registerPlugin('perSourceBehaviors', perSourceBehaviors);
// Default options for the plugin.
let defaults$5 = {};
/**
* Function to invoke when the player is ready.
*
* @function onPlayerReady
* @param {Player} player
* A Video.js player object.
*
* @param {Object} [options={}]
* A plain object containing options for the plugin.
*/
const onPlayerReady$1 = function onPlayerReady(player, options) {
player.addClass('vjs-ai-highlights-graph');
player.aiHighlightsGraph = new HighlightsGraphPlugin(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 aiHighlightsGraph
* @param {Object} [options={}]
* An object of options left to the plugin author to define.
*/
function aiHighlightsGraph(options) {
this.ready(() => {
onPlayerReady$1(this, _vjs.obj.merge(defaults$5, options));
});
}
/**
* HighlightsGraphPlugin class.
*
* This class performs all functions related to displaying the AI highlights graph.
*/
const HighlightsGraphPlugin = function () {
/**
* Plugin class constructor, called by videojs on
* ready event.
*
* @function constructor
* @param {Player} player
* A Video.js player object.
*
* @param {Object} [options={}]
* A plain object containing options for the plugin.
*/
function HighlightsGraphPlugin(player, options) {
this.player = player;
this.options = options;
this.initializeHighlightsGraph();
return this;
}
HighlightsGraphPlugin.prototype.src = function src(source) {
this.resetPlugin();
this.options.src = source;
this.initializeHighlightsGraph();
};
HighlightsGraphPlugin.prototype.detach = function detach() {
this.resetPlugin();
};
HighlightsGraphPlugin.prototype.resetPlugin = function resetPlugin() {
if (this.graphHolder) {
this.graphHolder.parentNode.removeChild(this.graphHolder);
}
delete this.progressBar;
delete this.graphHolder;
delete this.lastStyle;
};
/**
* Bootstrap the plugin.
*/
HighlightsGraphPlugin.prototype.initializeHighlightsGraph = function initializeHighlightsGraph() {
if (!this.options.src) {
return;
}
fetch(this.options.src, {
credentials: this.player.cloudinary.source?.().withCredentials ? 'include' : 'omit'
}).then(res => {
return res.json();
}).then(res => {
this.setupHighlightsGraphElement();
if (this.graphHolder) {
this.createHighlightsGraph(res);
}
});
};
HighlightsGraphPlugin.prototype.setupHighlightsGraphElement = function setupHighlightsGraphElement() {
this.progressBar = this.player.$('.vjs-progress-control');
if (!this.progressBar) {
return;
}
const graphHolder = this.player.$('.vjs-highlights-graph-display') || document.createElement('div');
graphHolder.setAttribute('class', 'vjs-highlights-graph-display');
this.progressBar.appendChild(graphHolder);
this.graphHolder = graphHolder;
};
/**
* Function to create the SVG path element
*/
HighlightsGraphPlugin.prototype.createPath = function createPath(dataArray, containerWidth, containerHeight) {
// Calculate the x and y coordinates for each point
const stepX = containerWidth / (dataArray.length - 1);
const points = dataArray.map((value, index) => ({
x: index * stepX,
y: containerHeight - value * containerHeight
}));
// Create a smooth line path
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('fill', 'lightblue');
// Generate the smooth line path data
let d = `M ${points[0].x},${points[0].y}`;
for (let i = 0; i < points.length - 1; i++) {
const xc = (points[i].x + points[i + 1].x) / 2;
const yc = (points[i].y + points[i + 1].y) / 2;
d += ` Q ${points[i].x},${points[i].y} ${xc},${yc}`;
}
d += ` Q ${points[points.length - 1].x},${points[points.length - 1].y} ${points[points.length - 1].x},${points[points.length - 1].y}`;
// Close the path to fill the region under the line
d += ` L ${points[points.length - 1].x},${containerHeight} L ${points[0].x},${containerHeight} Z`;
path.setAttribute('d', d);
return path;
};
HighlightsGraphPlugin.prototype.createHighlightsGraph = function createHighlightsGraph(info) {
const data = info.data;
const svgWidth = 600;
const svgHeight = 20;
const svg = this.player.$('.vjs-highlights-graph-display > svg') || document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
svg.setAttribute('preserveAspectRatio', 'none');
svg.setAttribute('width', svgWidth);
svg.setAttribute('height', svgHeight);
svg.innerHTML = '';
const path = this.createPath(data, svgWidth, svgHeight);
svg.appendChild(path);
this.graphHolder.appendChild(svg);
};
return HighlightsGraphPlugin;
}();
var events = {exports: {}};
var hasRequiredEvents;
function requireEvents () {
if (hasRequiredEvents) return events.exports;
hasRequiredEvents = 1;
var R = typeof Reflect === 'object' ? Reflect : null;
var ReflectApply = R && typeof R.apply === 'function'
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
};
var ReflectOwnKeys;
if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys;
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
};
function EventEmitter() {
EventEmitter.init.call(this);
}
events.exports = EventEmitter;
events.exports.once = once;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error');
var events = this._events;
if (events !== undefined)
doError = (doError && events.error === undefined);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event
}
// At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
err.context = er;
throw err; // Unhandled 'error' event
}
var handler = events[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === undefined)
return this;
list = events[type];
if (list === undefined)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (events === undefined)
return this;
// not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === undefined)
return [];
var evlistener = events[type];
if (evlistener === undefined)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
retu