@cassette/core
Version:
A simple, clean, and responsive visual wrapper for the HTML audio tag, built with React.
1,261 lines (1,025 loc) • 103 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("prop-types"), require("react"));
else if(typeof define === 'function' && define.amd)
define(["prop-types", "react"], factory);
else if(typeof exports === 'object')
exports["cassetteCore"] = factory(require("prop-types"), require("react"));
else
root["cassetteCore"] = factory(root["PropTypes"], root["React"]);
})((typeof self !== "undefined" ? self : this), function(__WEBPACK_EXTERNAL_MODULE__0__, __WEBPACK_EXTERNAL_MODULE__1__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/dist";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 7);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE__0__;
/***/ }),
/* 1 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE__1__;
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return logError; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return logWarning; });
/* eslint-disable no-console */
const log = console.log.bind(console);
const logError = console.error ? console.error.bind(console) : log;
const logWarning = console.warn ? console.warn.bind(console) : log;
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _console__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
const packageVersion = __webpack_require__(6).version;
const _global = typeof window === 'undefined' ? global : window;
_global.__cassette_contexts__ = _global.__cassette_contexts__ || {};
function createSingleGlobalContext(_ref) {
let displayName = _ref.displayName,
_ref$defaultValue = _ref.defaultValue,
defaultValue = _ref$defaultValue === void 0 ? null : _ref$defaultValue,
keysWillUpdate = _ref.keysWillUpdate;
const ExistingContext = _global.__cassette_contexts__[displayName];
if (ExistingContext) {
if (ExistingContext.packageVersion !== packageVersion) {
Object(_console__WEBPACK_IMPORTED_MODULE_1__[/* logWarning */ "b"])(`Warning: multiple versions of ${displayName} from the @cassette/core` + ` package have been loaded. v${packageVersion} will be ignored and` + ` v${ExistingContext.packageVersion} will be used instead.`);
}
return ExistingContext;
} // inspired by:
// https://github.com/philosaf/observed-bits/blob/master/src/index.js
const flags = {};
let i = 0;
for (const key of keysWillUpdate) {
flags[key] = 1 << i++;
}
const Context = Object(react__WEBPACK_IMPORTED_MODULE_0__["createContext"])(defaultValue, function getChangedBits(prev, next) {
let mask = 0;
for (const key of keysWillUpdate) {
if (prev[key] !== next[key]) {
mask |= flags[key];
}
}
return mask;
});
Context.__cassetteGetObservedBits = keys => {
let observedBits = 0;
for (const key of keys) {
observedBits |= flags[key];
}
return observedBits;
};
Context.displayName = displayName;
Context.packageVersion = packageVersion;
_global.__cassette_contexts__[displayName] = Context;
return Context;
}
/* harmony default export */ __webpack_exports__["a"] = (createSingleGlobalContext);
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(5)))
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function (arr, predicate, ctx) {
if (typeof Array.prototype.findIndex === 'function') {
return arr.findIndex(predicate, ctx);
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(arr);
var len = list.length;
if (len === 0) {
return -1;
}
for (var i = 0; i < len; i++) {
if (predicate.call(ctx, list[i], i, list)) {
return i;
}
}
return -1;
};
/***/ }),
/* 5 */
/***/ (function(module, exports) {
var g;
// This works in non-strict mode
g = (function() {
return this;
})();
try {
// This works if eval is allowed (see CSP)
g = g || Function("return this")() || (1, eval)("this");
} catch (e) {
// This works if the window reference is available
if (typeof window === "object") g = window;
}
// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}
module.exports = g;
/***/ }),
/* 6 */
/***/ (function(module) {
module.exports = {"name":"@cassette/core","version":"2.0.0-beta.4","description":"A simple, clean, and responsive visual wrapper for the HTML audio tag, built with React.","main":"dist/es5/cassette-core.js","scripts":{"build:clean":"rimraf dist","build:webpack":"BUILD_MODE=all webpack --progress","build":"npm run build:clean && npm run build:webpack","prepare":"npm run build","test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"https://github.com/benwiley4000/cassette.git"},"engines":{"node":">=6.0.0","npm":">=5.0.0"},"keywords":["audio","video","media","ui","react","reactjs","responsive","music","player","html5","component","components"],"author":{"name":"Ben Wiley","email":"therealbenwiley@gmail.com","url":"http://benwiley.org/"},"license":"MIT","peerDependencies":{"react":"^16.3.0"},"devDependencies":{"array-find-index":"^1.0.2","rimraf":"^2.5.4","webpack":"^4.17.1"},"dependencies":{"prop-types":"^15.5.10"},"publishConfig":{"access":"public"},"gitHead":"0b53bfc554d37befcf6340dda3e94019f657e7d0"};
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var PlayerPropTypes_namespaceObject = {};
__webpack_require__.r(PlayerPropTypes_namespaceObject);
__webpack_require__.d(PlayerPropTypes_namespaceObject, "controlKeyword", function() { return controlKeyword; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "control", function() { return control; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "crossOriginAttribute", function() { return crossOriginAttribute; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "repeatStrategy", function() { return PlayerPropTypes_repeatStrategy; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "mediaSource", function() { return mediaSource; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "mediaSessionAction", function() { return mediaSessionAction; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "mediaSessionArtwork", function() { return mediaSessionArtwork; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "track", function() { return PlayerPropTypes_track; });
__webpack_require__.d(PlayerPropTypes_namespaceObject, "seekMode", function() { return seekMode; });
// EXTERNAL MODULE: external {"root":"React","commonjs":"react","commonjs2":"react","amd":"react"}
var external_root_React_commonjs_react_commonjs2_react_amd_react_ = __webpack_require__(1);
var external_root_React_commonjs_react_commonjs2_react_amd_react_default = /*#__PURE__*/__webpack_require__.n(external_root_React_commonjs_react_commonjs2_react_amd_react_);
// EXTERNAL MODULE: external {"root":"PropTypes","commonjs":"prop-types","commonjs2":"prop-types","amd":"prop-types"}
var external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_ = __webpack_require__(0);
var external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default = /*#__PURE__*/__webpack_require__.n(external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_);
// EXTERNAL MODULE: ./node_modules/array-find-index/index.js
var array_find_index = __webpack_require__(4);
var array_find_index_default = /*#__PURE__*/__webpack_require__.n(array_find_index);
// EXTERNAL MODULE: ./src/utils/createSingleGlobalContext.js
var createSingleGlobalContext = __webpack_require__(3);
// CONCATENATED MODULE: ./src/PlayerContext.js
// TODO: test to make sure context contents stay in sync with
// enumerated list here
/* harmony default export */ var PlayerContext = (Object(createSingleGlobalContext["a" /* default */])({
displayName: 'PlayerContext',
keysWillUpdate: ['playlist', 'activeTrackIndex', 'trackLoading', 'paused', 'currentTime', 'seekPreviewTime', 'seekInProgress', 'awaitingPlayResume', 'duration', 'bufferedRanges', 'playedRanges', 'seekableRanges', 'volume', 'muted', 'shuffle', 'stalled', 'playbackRate', 'setVolumeInProgress', 'repeatStrategy', 'mediaCannotPlay']
}));
// CONCATENATED MODULE: ./src/GroupContext.js
/* harmony default export */ var GroupContext = (Object(createSingleGlobalContext["a" /* default */])({
displayName: 'GroupContext',
keysWillUpdate: ['groupProps']
}));
// CONCATENATED MODULE: ./src/constants.js
const repeatStrategyOptions = ['none', 'playlist', 'track'];
const MEDIA_ERR_NETWORK = 2;
// EXTERNAL MODULE: ./src/utils/console.js
var console = __webpack_require__(2);
// CONCATENATED MODULE: ./src/PlayerPropTypes.js
function requiredOnlyUnlessHasProp(propType, altPropName) {
let warnedAboutDefiningBoth = false;
function validate(props, propName, componentName) {
if (propName in props) {
if (!warnedAboutDefiningBoth && altPropName in props) {
Object(console["b" /* logWarning */])(`Do not define both the '${propName}' and '${altPropName}' props.`);
warnedAboutDefiningBoth = true;
}
for (var _len = arguments.length, rest = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
rest[_key - 3] = arguments[_key];
}
return propType.isRequired(props, propName, componentName, ...rest);
}
if (!(altPropName in props)) {
return new Error(`If the '${altPropName}' prop is not defined, '${propName}' must be.`);
}
}
return validate;
}
const controlKeyword = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOf(['playpause', 'backskip', 'forwardskip', 'volume', 'mute', 'repeat', 'shuffle', 'progress', 'progressdisplay', 'fullscreen', 'spacer']);
const control = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOfType([external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.func, controlKeyword]);
const crossOriginAttribute = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOf(['anonymous', 'use-credentials']);
const PlayerPropTypes_repeatStrategy = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOf(repeatStrategyOptions);
const mediaSource = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.shape({
src: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string.isRequired,
type: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string.isRequired
});
const mediaSessionAction = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOf(['play', 'pause', 'previoustrack', 'nexttrack', 'seekbackward', 'seekforward']);
const mediaSessionArtwork = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.shape({
src: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string.isRequired,
sizes: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string,
type: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string
});
const PlayerPropTypes_track = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.shape({
url: requiredOnlyUnlessHasProp(external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string, 'sources'),
sources: requiredOnlyUnlessHasProp(external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.arrayOf(mediaSource.isRequired), 'url'),
title: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string.isRequired,
artist: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string,
album: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string,
artwork: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.arrayOf(mediaSessionArtwork.isRequired),
duration: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOfType([external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.string, external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.number]),
startingTime: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.number,
isUnboundedStream: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.bool,
meta: external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.object
});
const seekMode = external_root_PropTypes_commonjs_prop_types_commonjs2_prop_types_amd_prop_types_default.a.oneOf(['paused', 'immediate', 'onrelease']);
// CONCATENATED MODULE: ./src/factories/createCustomMediaElement.js
const loopchange = 'loopchange';
const srcrequest = 'srcrequest';
function createCustomMediaElement(media) {
new MutationObserver(() => {
media.dispatchEvent(new Event(loopchange));
}).observe(media, {
attributes: true,
attributeFilter: ['loop']
}); // Don't let the media src property get modified directly.
// Instead, when it does get set, dispatch an event to be
// handled in a way that doesn't conflict with the loaded
// playlist.
Object.defineProperty(media, 'src', {
get: () => media.currentSrc,
set: src => {
const e = new Event(srcrequest);
e.srcRequested = src;
media.dispatchEvent(e);
}
});
return media;
}
/* harmony default export */ var factories_createCustomMediaElement = (createCustomMediaElement);
// CONCATENATED MODULE: ./src/utils/ShuffleManager.js
/* ShuffleManager
*
* Manages navigation throughout a list which is:
* - Sourced from another provided list
* - In random order (except to avoid consecutive duplicates)
* - Extended endlessly on-the-fly, as needed
* - Able to have future history overwritten by non-random choices
* - Able to swap source lists and maintain shuffle order for common members
*/
class ShuffleManager {
constructor(list, options) {
if (options === void 0) {
options = {};
}
this._list = list;
this._forwardStack = [];
this._backStack = [];
this._currentItem = undefined;
this._allowBackShuffle = Boolean(options.allowBackShuffle);
}
findNextItem(currentIndex) {
if (currentIndex !== undefined) {
this.setCurrentIndex(currentIndex);
}
this._currentItem = _findNextItem(this._list, this._forwardStack, this._backStack, this._currentItem, true);
return this._currentItem;
}
findPreviousItem(currentIndex) {
if (currentIndex !== undefined) {
this.setCurrentIndex(currentIndex);
}
this._currentItem = _findNextItem(this._list, this._backStack, this._forwardStack, this._currentItem, this._allowBackShuffle);
return this._currentItem;
}
pickNextItem(index, currentIndex) {
if (currentIndex !== undefined) {
this.setCurrentIndex(currentIndex);
}
if (this._list[index] === undefined) {
return undefined;
}
if (this._currentItem !== undefined) {
this._backStack.push(this._currentItem);
}
this._forwardStack.length = 0;
this._currentItem = this._list[index];
return this._currentItem;
}
setList(list) {
this._list = list;
}
setOptions(options) {
for (const o of Object.keys(options)) {
switch (o) {
case 'allowBackShuffle':
this[`_${o}`] = Boolean(options[o]);
break;
default:
break;
}
}
}
setCurrentIndex(currentIndex) {
const item = this._list[currentIndex];
if (this._currentItem !== item) {
this.clear();
this._currentItem = item;
}
}
clear() {
this._forwardStack.length = 0;
this._backStack.length = 0;
this._currentItem = undefined;
}
}
function _goForward(n, forwardStack, backStack, currentItem) {
let item = currentItem;
for (let i = 0; i < n; i++) {
if (!forwardStack.length) {
// rollback before erroring (note stack reversal)
_goForward(i, backStack, forwardStack, item);
throw `Moving ${n} places was not possible!`;
}
backStack.push(item);
item = forwardStack.pop();
}
return item;
}
function _allItemsMatch(list, item) {
if (!list.length) {
return false;
}
for (let i = 0; i < list.length; i++) {
if (item !== list[i]) {
return false;
}
}
return true;
}
function _findNextItem(list, forwardStack, backStack, currentItem, allowMore) {
let item = currentItem;
if (!list.length) {
return undefined;
}
for (let i = 1; i <= forwardStack.length; i++) {
if (list.indexOf(forwardStack[forwardStack.length - i]) !== -1) {
return _goForward(i, forwardStack, backStack, item);
}
}
if (!allowMore) {
return undefined;
}
if (_allItemsMatch(list, item)) {
// we can serve this as our "next" item but we
// won't modify our history since it's the same.
return item;
}
let nextItem;
do {
nextItem = list[Math.floor(Math.random() * list.length)];
} while (item === nextItem || nextItem === undefined); // if we're skipping items that aren't in our current list we may
// have some items in our forwardStack - make sure we move to the front.
item = _goForward(forwardStack.length, forwardStack, backStack, item);
if (item !== undefined) {
backStack.push(item);
}
return nextItem;
}
/* harmony default export */ var utils_ShuffleManager = (ShuffleManager);
// CONCATENATED MODULE: ./src/utils/isPlaylistValid.js
function isPlaylistValid(playlist) {
return Boolean(playlist && playlist.length);
}
/* harmony default export */ var utils_isPlaylistValid = (isPlaylistValid);
// CONCATENATED MODULE: ./src/utils/getTrackSources.js
const blankSources = [{
src: ''
}];
function getTrackSources(playlist, index) {
if (!utils_isPlaylistValid(playlist)) {
return blankSources;
}
const _playlist$index = playlist[index],
sources = _playlist$index.sources,
url = _playlist$index.url;
if (sources) {
return sources.length ? sources : blankSources;
}
return [{
src: url
}];
}
/* harmony default export */ var utils_getTrackSources = (getTrackSources);
// CONCATENATED MODULE: ./src/utils/findTrackIndexByUrl.js
function findTrackIndexByUrl(playlist, url) {
return array_find_index_default()(playlist, track => {
if (track.sources) {
return array_find_index_default()(track.sources, source => source.src === url) !== -1;
}
return track.url && url === track.url;
});
}
/* harmony default export */ var utils_findTrackIndexByUrl = (findTrackIndexByUrl);
// CONCATENATED MODULE: ./src/utils/snapshot.js
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
const veryLongKey = '__highly_unstable_snapshot_internals_which_will_break_your_app_if_you_use_them_directly__';
const versionKey = '__cassette_snapshot_version__'; // IMPORTANT: new migrations *must* always be added to the end since
// the tracked snapshot version is based on the migration index.
// If there is a crash-inducing bug in an existing migration, it can be patched
// in-place, but it should never be removed from the migrations array.
const migrations = [oldSnapshot => {
const __unstable__ = oldSnapshot.__unstable__,
rest = _objectWithoutPropertiesLoose(oldSnapshot, ["__unstable__"]);
return _objectSpread({}, rest, {
[veryLongKey]: __unstable__
});
}];
function getStateSnapshot(state) {
const paused = state.paused,
currentTime = state.currentTime,
activeTrackIndex = state.activeTrackIndex,
volume = state.volume,
muted = state.muted,
loop = state.loop,
cycle = state.cycle,
shuffle = state.shuffle,
playbackRate = state.playbackRate,
duration = state.duration,
__playlist__ = state.__playlist__;
return {
[versionKey]: migrations.length,
[veryLongKey]: {
paused,
// currentTime can't be restored for unbounded live streams
currentTime: duration === Infinity ? 0 : currentTime,
activeTrackIndex,
volume,
muted,
loop,
cycle,
shuffle,
playbackRate,
activeTrackSrc: utils_isPlaylistValid(__playlist__) ? utils_getTrackSources(__playlist__, activeTrackIndex)[0].src : null
}
};
}
function restoreStateFromSnapshot(snapshot, props) {
const migratedSnapshot = migrations.slice(snapshot[versionKey] || 0).reduce((oldSnapshot, migration) => migration(oldSnapshot), snapshot);
const _migratedSnapshot$ver = migratedSnapshot[veryLongKey],
paused = _migratedSnapshot$ver.paused,
currentTime = _migratedSnapshot$ver.currentTime,
activeTrackIndex = _migratedSnapshot$ver.activeTrackIndex,
volume = _migratedSnapshot$ver.volume,
muted = _migratedSnapshot$ver.muted,
loop = _migratedSnapshot$ver.loop,
cycle = _migratedSnapshot$ver.cycle,
shuffle = _migratedSnapshot$ver.shuffle,
playbackRate = _migratedSnapshot$ver.playbackRate,
activeTrackSrc = _migratedSnapshot$ver.activeTrackSrc;
const restoredStateValues = {};
if (utils_isPlaylistValid(props.playlist) && typeof paused === 'boolean') {
// using awaitingPlay instead of paused triggers an animation
restoredStateValues.awaitingPlay = !paused;
}
if (typeof volume === 'number' && volume >= 0 && volume <= 1) {
restoredStateValues.volume = volume;
}
if (typeof muted === 'boolean') {
restoredStateValues.muted = muted;
}
if (typeof loop === 'boolean') {
restoredStateValues.loop = loop;
}
if (typeof cycle === 'boolean') {
restoredStateValues.cycle = cycle;
}
if (typeof shuffle === 'boolean') {
restoredStateValues.shuffle = shuffle;
}
if (typeof playbackRate === 'number') {
restoredStateValues.playbackRate = playbackRate;
}
let useCurrentTime = false;
if (typeof activeTrackSrc === 'string' && typeof activeTrackIndex === 'number' && activeTrackIndex >= 0) {
// let's try staying on the same track index
const currentSrc = props.playlist[activeTrackIndex] && utils_getTrackSources(props.playlist, activeTrackIndex)[0].src;
if (currentSrc && activeTrackSrc === currentSrc) {
restoredStateValues.activeTrackIndex = activeTrackIndex;
useCurrentTime = true;
} else {
/* if the track we were playing before is in the new playlist,
* update the activeTrackIndex.
*/
const newTrackIndex = utils_findTrackIndexByUrl(props.playlist, activeTrackSrc);
if (newTrackIndex !== -1) {
restoredStateValues.activeTrackIndex = newTrackIndex;
useCurrentTime = true;
}
}
}
if (useCurrentTime && typeof currentTime === 'number' && currentTime >= 0) {
restoredStateValues.currentTime = currentTime;
}
return restoredStateValues;
}
// CONCATENATED MODULE: ./src/utils/getSourceList.js
// collapses playlist into flat list containing
// the first source url for each track
function getSourceList(playlist) {
return (playlist || []).map((_, i) => utils_getTrackSources(playlist, i)[0].src);
}
/* harmony default export */ var utils_getSourceList = (getSourceList);
// CONCATENATED MODULE: ./src/utils/getTimeRangesArray.js
function getTimeRangesArray(timeRangesObj) {
const timeRangesArray = Array(timeRangesObj.length);
for (let i = 0; i < timeRangesObj.length; i++) {
timeRangesArray[i] = {
start: timeRangesObj.start(i),
end: timeRangesObj.end(i)
};
}
return timeRangesArray;
}
/* harmony default export */ var utils_getTimeRangesArray = (getTimeRangesArray);
// CONCATENATED MODULE: ./src/utils/getRepeatStrategy.js
function getRepeatStrategy(loop, cycle) {
if (loop) {
return 'track';
}
if (cycle) {
return 'playlist';
}
return 'none';
}
/* harmony default export */ var utils_getRepeatStrategy = (getRepeatStrategy);
// CONCATENATED MODULE: ./src/utils/convertToNumberWithinIntervalBounds.js
function convertToNumberWithinIntervalBounds(number, min, max) {
min = typeof min === 'number' ? min : -Infinity;
max = typeof max === 'number' ? max : Infinity;
return Math.max(min, Math.min(number, max));
}
/* harmony default export */ var utils_convertToNumberWithinIntervalBounds = (convertToNumberWithinIntervalBounds);
// CONCATENATED MODULE: ./src/utils/getDisplayText.js
function getDisplayText(track) {
if (!track) {
return '';
}
if (track.title && track.artist) {
return `${track.artist} - ${track.title}`;
}
return track.title || track.artist || track.album || '';
}
/* harmony default export */ var utils_getDisplayText = (getDisplayText);
// CONCATENATED MODULE: ./src/utils/parseTimeString.js
function parseTimeString(str) {
let seconds = 0;
let factor = 1;
const times = str.split(':').slice(-3);
while (times.length > 0) {
seconds += factor * parseInt(times.pop(), 10);
factor *= 60;
}
return seconds;
}
/* harmony default export */ var utils_parseTimeString = (parseTimeString);
// CONCATENATED MODULE: ./src/utils/getInitialDuration.js
function getInitialDuration(track) {
let duration = 0;
if (track.duration) {
if (typeof track.duration === 'string') {
duration = utils_parseTimeString(track.duration);
} else {
duration = track.duration;
}
}
return duration;
}
/* harmony default export */ var utils_getInitialDuration = (getInitialDuration);
// CONCATENATED MODULE: ./src/PlayerContextProvider.js
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function PlayerContextProvider_objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function PlayerContextProvider_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { PlayerContextProvider_defineProperty(target, key, source[key]); }); } return target; }
function PlayerContextProvider_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function playErrorHandler(err) {
Object(console["a" /* logError */])(err);
if (err.name === 'NotAllowedError') {
const warningMessage = 'Media playback failed at ' + new Date().toLocaleTimeString() + '! (Perhaps autoplay is disabled in this browser.)';
Object(console["b" /* logWarning */])(warningMessage);
}
} // Existing Media Session API implementations have default handlers
// for play/pause, and may yield unexpected behavior if custom
// play/pause handlers are defined - so let's leave them be.
const supportableMediaSessionActions = ['previoustrack', 'nexttrack', 'seekbackward', 'seekforward'];
const defaultState = {
// indicates whether media player should be paused
paused: true,
// elapsed time for active track, in seconds
currentTime: 0,
// The most recent targeted time, in seconds, for seek preview
seekPreviewTime: 0,
/* true if the user is currently dragging the mouse
* to seek a new track position
*/
seekInProgress: false,
/* true if media was playing when seek previewing began,
* it was paused, and it should be resumed on seek
* complete
*/
awaitingResumeOnSeekComplete: false,
// true if media will play once new track has loaded
awaitingPlayAfterTrackLoad: false,
// the duration in seconds of the loaded track
duration: 0,
// array describing the buffered ranges in the loaded track
bufferedRanges: [],
// array describing the already-played ranges in the loaded track
playedRanges: [],
// array describing the seekable ranges in the loaded track
seekableRanges: [],
// true if the media is currently stalled pending data buffering
stalled: false,
// true if the active track should play on the next componentDidUpdate
shouldRequestPlayOnNextUpdate: false,
/* true if an error occurs while fetching the active track media data
* or if its type is not a supported media format
*/
mediaCannotPlay: false,
// maximum currentTime since the current track has been playing
maxKnownTime: 0
}; // assumes playlist is valid
function getGoToTrackState(_ref) {
let prevState = _ref.prevState,
index = _ref.index,
track = _ref.track,
_ref$shouldPlay = _ref.shouldPlay,
shouldPlay = _ref$shouldPlay === void 0 ? true : _ref$shouldPlay,
_ref$shouldForceLoad = _ref.shouldForceLoad,
shouldForceLoad = _ref$shouldForceLoad === void 0 ? false : _ref$shouldForceLoad,
startingTime = _ref.startingTime;
const isNewTrack = prevState.activeTrackIndex !== index;
const shouldLoadAsNew = Boolean(isNewTrack || shouldForceLoad);
const currentTime = startingTime || track.startingTime || 0;
return {
duration: utils_getInitialDuration(track),
activeTrackIndex: index,
trackLoading: shouldLoadAsNew,
mediaCannotPlay: prevState.mediaCannotPlay && !shouldLoadAsNew,
currentTime: utils_convertToNumberWithinIntervalBounds(currentTime, 0),
loop: shouldLoadAsNew ? false : prevState.loop,
shouldRequestPlayOnNextUpdate: Boolean(shouldPlay),
awaitingPlayAfterTrackLoad: Boolean(shouldPlay),
awaitingForceLoad: Boolean(shouldForceLoad),
maxKnownTime: shouldLoadAsNew ? 0 : prevState.maxKnownTime
};
}
/**
* Wraps an area which shares a common [`playerContext`](#playercontext)
*/
class PlayerContextProvider_PlayerContextProvider extends external_root_React_commonjs_react_commonjs2_react_amd_react_["Component"] {
constructor(props) {
super(props);
let currentTime = 0;
let activeTrackIndex = utils_convertToNumberWithinIntervalBounds(props.startingTrackIndex, 0);
const playlistIsValid = utils_isPlaylistValid(props.playlist);
if (playlistIsValid && props.playlist[activeTrackIndex]) {
currentTime = props.playlist[activeTrackIndex].startingTime || 0;
}
const initialStateSnapshot = props.initialStateSnapshot;
let restoredStateFromSnapshot = {};
if (initialStateSnapshot) {
try {
restoredStateFromSnapshot = restoreStateFromSnapshot(initialStateSnapshot, props);
const _restoredStateFromSna = restoredStateFromSnapshot,
a = _restoredStateFromSna.activeTrackIndex,
c = _restoredStateFromSna.currentTime;
if (typeof a === 'number') {
activeTrackIndex = a;
}
if (typeof c === 'number') {
currentTime = c;
}
} catch (err) {
Object(console["b" /* logWarning */])(err);
Object(console["b" /* logWarning */])('Loading Cassette state from snapshot failed.');
Object(console["b" /* logWarning */])(`Failed snapshot:\n${JSON.stringify(initialStateSnapshot, null, 2)}`);
}
}
this.state = PlayerContextProvider_objectSpread({}, defaultState, {
// index matching requested track (whether track has loaded or not)
activeTrackIndex,
// whether we're waiting on loading metadata for the active track
trackLoading: utils_isPlaylistValid(props.playlist),
// the current timestamp on the active track in seconds
currentTime: utils_convertToNumberWithinIntervalBounds(currentTime, 0),
// the latest volume of the media, between 0 and 1.
volume: utils_convertToNumberWithinIntervalBounds(props.defaultVolume, 0, 1),
// true if the media has been muted
muted: props.defaultMuted,
// whether to loop the active track
loop: props.defaultRepeatStrategy === 'track',
// true if playlist should continue at start after completion
cycle: props.defaultRepeatStrategy === 'playlist',
// whether to randomly pick next track from playlist after one finishes
shuffle: props.defaultShuffle,
// Rate at which media should be played. 1.0 is normal speed.
playbackRate: props.defaultPlaybackRate,
// true if user is currently dragging mouse to change the volume
setVolumeInProgress: false,
// initialize shouldRequestPlayOnNextUpdate from autoplay prop
shouldRequestPlayOnNextUpdate: props.autoplay && playlistIsValid,
awaitingForceLoad: false,
// duration might be set on track object
duration: utils_getInitialDuration(playlistIsValid && props.playlist[activeTrackIndex]),
// playlist prop copied to state (for getDerivedStateFromProps)
__playlist__: props.playlist
}, restoredStateFromSnapshot); // volume at last time we were unmuted and not actively setting volume
this.lastStableVolume = this.state.volume; // used to keep track of play history when we are shuffling
this.shuffler = new utils_ShuffleManager(utils_getSourceList(props.playlist), {
allowBackShuffle: props.allowBackShuffle
}); // html media element used for playback
this.media = null;
this.videoHostElementList = [];
this.videoHostOccupiedCallbacks = new Map();
this.videoHostVacatedCallbacks = new Map(); // bind internal methods
this.handleTrackPlaybackFailure = this.handleTrackPlaybackFailure.bind(this);
this.handlePlayerOnlineAfterFailure = this.handlePlayerOnlineAfterFailure.bind(this); // bind callback methods to pass to descendant elements
this.togglePause = this.togglePause.bind(this);
this.selectTrackIndex = this.selectTrackIndex.bind(this);
this.forwardSkip = this.forwardSkip.bind(this);
this.backSkip = this.backSkip.bind(this);
this.seekPreview = this.seekPreview.bind(this);
this.seekComplete = this.seekComplete.bind(this);
this.setVolume = this.setVolume.bind(this);
this.setVolumeComplete = this.setVolumeComplete.bind(this);
this.toggleMuted = this.toggleMuted.bind(this);
this.toggleShuffle = this.toggleShuffle.bind(this);
this.setRepeatStrategy = this.setRepeatStrategy.bind(this);
this.setPlaybackRate = this.setPlaybackRate.bind(this);
this.registerVideoHostElement = this.registerVideoHostElement.bind(this);
this.renderVideoIntoHostElement = this.renderVideoIntoHostElement.bind(this);
this.unregisterVideoHostElement = this.unregisterVideoHostElement.bind(this);
this.updateVideoHostElement = this.updateVideoHostElement.bind(this); // bind media event handlers
this.handleMediaPlay = this.handleMediaPlay.bind(this);
this.handleMediaPause = this.handleMediaPause.bind(this);
this.handleMediaSrcrequest = this.handleMediaSrcrequest.bind(this);
this.handleMediaEnded = this.handleMediaEnded.bind(this);
this.handleMediaEmptied = this.handleMediaEmptied.bind(this);
this.handleMediaStalled = this.handleMediaStalled.bind(this);
this.handleMediaCanplaythrough = this.handleMediaCanplaythrough.bind(this);
this.handleMediaCanplay = this.handleMediaCanplay.bind(this);
this.handleMediaTimeupdate = this.handleMediaTimeupdate.bind(this);
this.handleMediaLoadeddata = this.handleMediaLoadeddata.bind(this);
this.handleMediaVolumechange = this.handleMediaVolumechange.bind(this);
this.handleMediaDurationchange = this.handleMediaDurationchange.bind(this);
this.handleMediaProgress = this.handleMediaProgress.bind(this);
this.handleMediaLoopchange = this.handleMediaLoopchange.bind(this);
this.handleMediaRatechange = this.handleMediaRatechange.bind(this);
}
componentDidMount() {
const media = this.media = factories_createCustomMediaElement(this.props.createMediaElement());
const _this$props = this.props,
defaultPlaybackRate = _this$props.defaultPlaybackRate,
crossOrigin = _this$props.crossOrigin,
playlist = _this$props.playlist,
autoplayDelayInSeconds = _this$props.autoplayDelayInSeconds,
mediaElementRef = _this$props.mediaElementRef,
getPosterImageForTrack = _this$props.getPosterImageForTrack,
getMediaTitleAttributeForTrack = _this$props.getMediaTitleAttributeForTrack,
onActiveTrackUpdate = _this$props.onActiveTrackUpdate;
const _this$state = this.state,
volume = _this$state.volume,
muted = _this$state.muted,
playbackRate = _this$state.playbackRate,
loop = _this$state.loop,
activeTrackIndex = _this$state.activeTrackIndex,
shouldRequestPlayOnNextUpdate = _this$state.shouldRequestPlayOnNextUpdate; // initialize media properties
// We used to set currentTime here.. now waiting for loadeddata.
// This avoids an issue where some browsers ignore or delay currentTime
// updates when in the HAVE_NOTHING state.
media.defaultPlaybackRate = defaultPlaybackRate;
if (crossOrigin) {
media.crossOrigin = crossOrigin;
}
media.volume = volume;
media.muted = muted;
media.playbackRate = playbackRate;
media.loop = loop;
media.setAttribute('playsinline', '');
media.setAttribute('webkit-playsinline', '');
media.setAttribute('preload', 'metadata');
media.setAttribute('poster', getPosterImageForTrack(playlist[activeTrackIndex]));
media.setAttribute('title', getMediaTitleAttributeForTrack(playlist[activeTrackIndex])); // add listeners for media events
media.addEventListener('play', this.handleMediaPlay);
media.addEventListener('pause', this.handleMediaPause);
media.addEventListener('ended', this.handleMediaEnded);
media.addEventListener('stalled', this.handleMediaStalled);
media.addEventListener('emptied', this.handleMediaEmptied);
media.addEventListener('canplay', this.handleMediaCanplay);
media.addEventListener('canplaythrough', this.handleMediaCanplaythrough);
media.addEventListener('timeupdate', this.handleMediaTimeupdate);
media.addEventListener('loadeddata', this.handleMediaLoadeddata);
media.addEventListener('volumechange', this.handleMediaVolumechange);
media.addEventListener('durationchange', this.handleMediaDurationchange);
media.addEventListener('progress', this.handleMediaProgress);
media.addEventListener('ratechange', this.handleMediaRatechange);
media.addEventListener('error', this.handleTrackPlaybackFailure); // add listeners for special events
media.addEventListener('srcrequest', this.handleMediaSrcrequest);
media.addEventListener('loopchange', this.handleMediaLoopchange); // set source elements for current track
this.setMediaElementSources(); // initially mount media element in the hidden container (this may change)
this.mediaContainer.appendChild(media);
if (shouldRequestPlayOnNextUpdate) {
this.setState({
shouldRequestPlayOnNextUpdate: false
});
this.delayTimeout = setTimeout(() => {
this.togglePause(false);
}, autoplayDelayInSeconds * 1000);
}
if (mediaElementRef) {
mediaElementRef(media);
}
if (onActiveTrackUpdate) {
onActiveTrackUpdate({
track: playlist[activeTrackIndex],
trackIndex: activeTrackIndex,
previousTrack: null,
previousTrackIndex: null
});
}
}
static getDerivedStateFromProps(nextProps, prevState) {
const newPlaylist = nextProps.playlist;
if (newPlaylist === prevState.__playlist__) {
// reference comparison is equal so we'll
// assume the playlist is unchanged.
return null;
}
const baseNewState = {
__playlist__: newPlaylist
}; // check if the new playlist is invalid
if (!utils_isPlaylistValid(newPlaylist)) {
return PlayerContextProvider_objectSpread({}, defaultState, baseNewState, {
activeTrackIndex: 0,
trackLoading: false
});
} // check if the activeTrackIndex doesn't need to be updated
const prevSources = utils_getTrackSources(prevState.__playlist__, prevState.activeTrackIndex);
if (newPlaylist[prevState.activeTrackIndex]) {
// the sources if we stay on the same track index
const currentSources = utils_getTrackSources(newPlaylist, prevState.activeTrackIndex); // non-comprehensive but probably accurate check
if (prevSources[0].src === currentSources[0].src) {
// our active track index already matches
return baseNewState;
}
}
/* if the track we're already playing is in the new playlist, update the
* activeTrackIndex.
*/
const newTrackIndex = utils_findTrackIndexByUrl(newPlaylist, prevSources[0].src);
if (newTrackIndex !== -1) {
return PlayerContextProvider_objectSpread({}, baseNewState, {
activeTrackIndex: newTrackIndex
});
} // if not, then load the first track in the new playlist, and pause.
return PlayerContextProvider_objectSpread({}, baseNewState, getGoToTrackState({
prevState,
track: newPlaylist[0],
index: 0,
shouldPlay: false,
shouldForceLoad: true
}), {
mediaCannotPlay: false,
awaitingPlayAfterTrackLoad: false
});
}
componentDidUpdate(prevProps, prevState) {
this.media.defaultPlaybackRate = this.props.defaultPlaybackRate;
this.media.crossOrigin = this.props.crossOrigin;
this.shuffler.setList(utils_getSourceList(this.props.playlist));
this.shuffler.setOptions({
allowBackShuffle: this.props.allowBackShuffle
});
const prevSources = utils_getTrackSources(prevProps.playlist, prevState.activeTrackIndex);
const newSources = utils_getTrackSources(this.props.playlist, this.state.activeTrackIndex);
const prevTrack = prevProps.playlist[prevState.activeTrackIndex];
const newTrack = this.props.playlist[this.state.activeTrackIndex];
if (this.state.awaitingForceLoad || prevSources[0].src !== newSources[0].src) {
this.setMediaElementSources();
this.media.setAttribute('poster', this.props.getPosterImageForTrack(newTrack));
this.media.setAttribute('title', this.props.getMediaTitleAttributeForTrack(newTrack));
this.setState({
awaitingForceLoad: false
});
if (!this.state.shuffle) {
// after toggling off shuffle, we defer clearing the shuffle
// history until we actually change tracks - if the user quickly
// toggles shuffle off then back on again, we don't want to have
// lost our history.
this.shuffler.clear();
} // If track changes before player is back online, the previous track
// shouldn't be reloaded.
window.removeEventListener('online', this.handlePlayerOnlineAfterFailure);
}
if (this.props.onActiveTrackUpdate && prevTrack !== newTrack) {
this.props.onActiveTrackUpdate({
track: newTrack,
trackIndex: this.state.activeTrackIndex,
previousTrack: prevTrack,
previousTrackIndex: prevState.activeTrackIndex
});
}
if (prevProps !== this.props && !this.media.paused) {
// update running media session based on new props
this.stealMediaSession();
}
if (this.state.shouldRequestPlayOnNextUpdate) {
this.setState({
shouldRequestPlayOnNextUpdate: false
}); // media.currentSrc is updated asynchronously so we should
// play async to avoid weird intermediate state issues
setTimeout(() => {
this.togglePause(false);
});
}
clearTimeout(this.snapshotUpdateTimeout);
this.snapshotUpdateTimeout = setTimeout(() => {
if (this.props.onStateSnapshot) {
this.props.onStateSnapshot(getStateSnapshot(this.state));
}
}, 100);
}
componentWillUnmount() {
const media = this.media; // Media element creation will have failed if MutationObserver isn't
// supported by the browser. The parent might use an Error Boundary
// to display a fallback and so we try to avoid triggering *additional*
//