UNPKG

cloudinary-video-player

Version:
1,641 lines (1,458 loc) 210 kB
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