maestro-react-player
Version:
A React component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia and DailyMotion
590 lines (513 loc) • 20.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VAST = undefined;
var _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; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _utils = require('../utils');
var _singlePlayer = require('../singlePlayer');
var _singlePlayer2 = _interopRequireDefault(_singlePlayer);
var _ima = require('@alugha/ima');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var IOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
var PLAYER_ID_PREFIX = 'vast-player-';
var AUTOPLAY_ID_PREFIX = 'vast-autoplay-';
var CONTENT_ID_PREFIX = 'vast-content-';
var PLAY_BUTTON_ID_PREFIX = 'vast-play-';
var MATCH_URL = /^VAST:https:\/\//i;
// Example: https://github.com/googleads/googleads-ima-html5/blob/master/attempt_to_autoplay/ads.js
var VAST = exports.VAST = function (_Component) {
_inherits(VAST, _Component);
function VAST() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, VAST);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = VAST.__proto__ || Object.getPrototypeOf(VAST)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
adDisplayContainer: null,
autoplayChecksResolved: false,
adObject: null,
adsInitialized: false,
adsLoader: null,
adsManager: null,
adsRequest: null,
adsUrl: null,
autoplayAllowed: null,
autoplayRequiresMuted: null,
ima: null,
preMuteVolume: 0,
showPlayButton: false
}, _this.autoplayID = AUTOPLAY_ID_PREFIX + (0, _utils.randomString)(), _this.playerID = PLAYER_ID_PREFIX + (0, _utils.randomString)(), _this.contentID = CONTENT_ID_PREFIX + (0, _utils.randomString)(), _this.playButtonID = PLAY_BUTTON_ID_PREFIX + (0, _utils.randomString)(), _this.callPlayer = _utils.callPlayer, _this.mute = function () {
var adsManager = _this.state.adsManager;
if (!adsManager) return null;
_this.setState({ preMuteVolume: adsManager.getVolume() });
_this.setVolume(0.0);
}, _this.unmute = function () {
var preMuteVolume = _this.state.preMuteVolume;
_this.setVolume(preMuteVolume);
}, _this.containerRef = function (container) {
_this.container = container;
}, _this.playerRef = function (player) {
_this.player = player;
}, _this.autoplayRef = function (player) {
_this.autoplayTestPlayer = player;
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(VAST, [{
key: 'onAutoplayWithSoundSuccess',
value: function onAutoplayWithSoundSuccess() {
// If we make it here, unmuted autoplay works.
this.autoplayTestPlayer.pause();
this.setState({
autoplayAllowed: true,
autoplayRequiresMuted: false,
autoplayChecksResolved: true
});
}
}, {
key: 'onMutedAutoplaySuccess',
value: function onMutedAutoplaySuccess() {
// If we make it here, muted autoplay works but unmuted autoplay does not.
this.autoplayTestPlayer.pause();
this.setState({
autoplayAllowed: true,
autoplayRequiresMuted: true,
autoplayChecksResolved: true
});
}
}, {
key: 'onMutedAutoplayFail',
value: function onMutedAutoplayFail() {
// Both muted and unmuted autoplay failed. Fall back to click to play.
this.autoplayTestPlayer.volume = 1;
this.autoplayTestPlayer.muted = false;
this.setState({
autoplayAllowed: false,
autoplayRequiresMuted: false,
autoplayChecksResolved: true
});
}
}, {
key: 'checkMutedAutoplaySupport',
value: function checkMutedAutoplaySupport() {
var _this2 = this;
this.autoplayTestPlayer.volume = 0;
this.autoplayTestPlayer.muted = true;
var promise = this.autoplayTestPlayer.play();
if (promise !== undefined) {
promise.then(function () {
_this2.onMutedAutoplaySuccess();
})['catch'](function () {
_this2.onMutedAutoplayFail();
});
}
}
}, {
key: 'checkAutoplaySupport',
value: function checkAutoplaySupport() {
var _this3 = this;
var promise = this.autoplayTestPlayer.play();
if (promise !== undefined) {
promise.then(function () {
_this3.onAutoplayWithSoundSuccess();
})['catch'](function () {
_this3.checkMutedAutoplaySupport();
});
}
}
}, {
key: 'addListeners',
value: function addListeners() {
var playsinline = this.props.playsinline;
if (playsinline) {
this.player.setAttribute('playsinline', '');
this.player.setAttribute('webkit-playsinline', '');
this.player.setAttribute('x5-playsinline', '');
}
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.addListeners();
if (IOS) {
this.player.load();
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
// sdk is loaded
if (prevState.adsUrl === null && this.state.adsUrl) {
return this.checkAutoplaySupport();
}
// autoplay settings determined
if (prevState.autoplayChecksResolved === false && this.state.autoplayChecksResolved === true) {
return this.onAutoplayChecksResolved();
}
// adsManager events listening
if (prevState.adsManager === null && this.state.adsManager) {
this.onReady();
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.removeListeners();
}
}, {
key: 'removeListeners',
value: function removeListeners() {
var adsManager = this.state.adsManager;
window.removeEventListener('resize', this._onWindowResize);
if (adsManager && this.eventMap) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Object.entries(this.eventMap)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _ref2 = _step.value;
var _ref3 = _slicedToArray(_ref2, 2);
var event = _ref3[0];
var fn = _ref3[1];
adsManager.removeEventListener(event, fn);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}
}, {
key: 'onWindowResize',
value: function onWindowResize() {
var _state = this.state,
adsManager = _state.adsManager,
ima = _state.ima;
var _player = this.player,
height = _player.offsetHeight,
width = _player.offsetWidth;
if (!adsManager) return null;
adsManager.resize(width, height, ima.ViewMode.NORMAL);
}
}, {
key: 'onLoaded',
value: function onLoaded(adEvent) {
var onPlay = this.props.onPlay;
var adObject = adEvent.getAd();
onPlay();
this.setState({ adObject: adObject });
}
}, {
key: 'onVolumeChange',
value: function onVolumeChange() {
var onVolumeChange = this.props.onVolumeChange;
var adsManager = this.state.adsManager;
return onVolumeChange(adsManager.getVolume());
}
}, {
key: 'onAdsManagerLoaded',
value: function onAdsManagerLoaded(adsManagerLoadedEvent) {
var _eventMap;
var _props = this.props,
onEnded = _props.onEnded,
onPause = _props.onPause,
onPlay = _props.onPlay;
var ima = this.state.ima;
var adsManager = adsManagerLoadedEvent.getAdsManager(this.player);
// bind resize event
this._onWindowResize = this.onWindowResize.bind(this);
window.addEventListener('resize', this._onWindowResize);
this.eventMap = (_eventMap = {}, _defineProperty(_eventMap, ima.AdErrorEvent.Type.AD_ERROR, this.onError.bind(this)), _defineProperty(_eventMap, ima.AdEvent.Type.COMPLETE, onEnded.bind(this)), _defineProperty(_eventMap, ima.AdEvent.Type.LOADED, this.onLoaded.bind(this)), _defineProperty(_eventMap, ima.AdEvent.Type.RESUMED, onPlay.bind(this)), _defineProperty(_eventMap, ima.AdEvent.Type.PAUSED, onPause.bind(this)), _defineProperty(_eventMap, ima.AdEvent.Type.VOLUME_CHANGED, this.onVolumeChange.bind(this)), _eventMap);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = Object.entries(this.eventMap)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _ref4 = _step2.value;
var _ref5 = _slicedToArray(_ref4, 2);
var event = _ref5[0];
var fn = _ref5[1];
adsManager.addEventListener(event, fn);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
this.setState({ adsManager: adsManager });
}
}, {
key: 'onAutoplayChecksResolved',
value: function onAutoplayChecksResolved() {
var _state2 = this.state,
adsUrl = _state2.adsUrl,
adsLoader = _state2.adsLoader,
autoplayAllowed = _state2.autoplayAllowed,
autoplayRequiresMuted = _state2.autoplayRequiresMuted,
ima = _state2.ima;
// setup ads request
var adsRequest = new ima.AdsRequest();
adsRequest.adTagUrl = adsUrl;
adsRequest.setAdWillAutoPlay(autoplayAllowed);
adsRequest.setAdWillPlayMuted(autoplayRequiresMuted);
adsLoader.requestAds(adsRequest);
}
}, {
key: 'onReady',
value: function onReady() {
var _props2 = this.props,
playing = _props2.playing,
onReady = _props2.onReady;
var _state3 = this.state,
autoplayAllowed = _state3.autoplayAllowed,
autoplayRequiresMuted = _state3.autoplayRequiresMuted;
if (autoplayAllowed && playing) {
if (autoplayRequiresMuted) {
this.setVolume(0.0);
}
this.playAds();
} else {
this.setState({ showPlayButton: true });
}
onReady();
}
}, {
key: 'playAds',
value: function playAds() {
var _state4 = this.state,
adDisplayContainer = _state4.adDisplayContainer,
adsManager = _state4.adsManager,
adsInitialized = _state4.adsInitialized,
ima = _state4.ima;
try {
if (!adsInitialized) {
adDisplayContainer.initialize();
this.setState({ adsInitialized: true });
}
// Initialize the ads manager. Ad rules playlist will start at this time.
adsManager.init(640, 360, ima.ViewMode.NORMAL);
// trigger window resize
this.onWindowResize();
// Call play to start showing the ad. Single video and overlay ads will
// start at this time; the call will be ignored for ad rules.
adsManager.start();
} catch (adError) {
// An error may be thrown if there was a problem with the VAST response.
this.props.onEnded();
}
}
}, {
key: 'load',
value: function load(rawUrl) {
var _this4 = this;
// replace [RANDOM] or [random] with a randomly generated cache value
var ord = Math.random() * 10000000000000000;
var url = rawUrl.replace(/\[random]/ig, ord);
(0, _ima.loadImaSdk)().then(function (ima) {
// Create the ad display container.
var adDisplayContainer = new ima.AdDisplayContainer(_this4.container, _this4.player);
// Create the ads loader.
var adsLoader = new ima.AdsLoader(adDisplayContainer);
// Listen and respond to ads loaded and error events
adsLoader.addEventListener(ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, _this4.onAdsManagerLoaded.bind(_this4), false);
adsLoader.addEventListener(ima.AdErrorEvent.Type.AD_ERROR, _this4.onError.bind(_this4), false);
_this4.setState({
ima: ima,
adDisplayContainer: adDisplayContainer,
adsLoader: adsLoader,
adsUrl: url.slice('VAST:'.length)
});
})['catch'](function () {
// error loading ima, probably because of adblock. just fire onended
_this4.props.onEnded();
});
}
}, {
key: 'onError',
value: function onError(error) {
var onError = this.props.onError;
var adsManager = this.state.adsManager;
if (adsManager) {
adsManager.destroy();
}
onError(error);
}
}, {
key: 'pause',
value: function pause() {
var adsManager = this.state.adsManager;
adsManager.pause();
}
}, {
key: 'seekTo',
value: function seekTo() {}
}, {
key: 'stop',
value: function stop() {
var adsManager = this.state.adsManager;
if (!adsManager) return null;
adsManager.destroy();
}
}, {
key: 'setVolume',
value: function setVolume(fraction) {
var adsManager = this.state.adsManager;
if (!adsManager) return null;
adsManager.setVolume(fraction);
}
}, {
key: 'getDuration',
value: function getDuration() {
var adObject = this.state.adObject;
if (!adObject) return null;
var duration = adObject.getDuration();
return duration > 0 ? duration : null;
}
}, {
key: 'getCurrentTime',
value: function getCurrentTime() {
var adsManager = this.state.adsManager;
if (!adsManager) return null;
var duration = this.getDuration();
var remainingTime = adsManager.getRemainingTime();
if (Number.isFinite(duration) && Number.isFinite(remainingTime)) {
return duration - remainingTime;
}
return null;
}
}, {
key: 'getSecondsLoaded',
value: function getSecondsLoaded() {
return null;
}
}, {
key: 'play',
value: function play(event) {
// Initialize the container. Must be done via a user action where autoplay
// is not allowed.
var _state5 = this.state,
adDisplayContainer = _state5.adDisplayContainer,
adsInitialized = _state5.adsInitialized,
adsManager = _state5.adsManager,
autoplayRequiresMuted = _state5.autoplayRequiresMuted,
autoplayAllowed = _state5.autoplayAllowed;
if (!adsManager) return null;
if (adsInitialized) {
adsManager.resume();
} else {
// non-user event, and autoplay not allowed
if (!event && !autoplayAllowed) return null;
adDisplayContainer.initialize();
this.setState({
adsInitialized: true,
showPlayButton: false
});
this.player.load();
// must mute if autoplay requires am ute
if (!event && autoplayRequiresMuted) {
this.setVolume(0.0);
}
this.playAds();
}
}
// used to determine autoplay
}, {
key: 'renderTestPlayer',
value: function renderTestPlayer() {
return _react2['default'].createElement(
'video',
{
ref: this.autoplayRef,
controls: false,
style: { display: 'none' },
id: this.autoplayID
},
_react2['default'].createElement('source', { src: 'https://storage.googleapis.com/maestro-vast/google_ddm_animation_480P.mp4' }),
_react2['default'].createElement('source', { src: 'https://storage.googleapis.com/maestro-vast/google_ddm_animation_480P.webm' })
);
}
}, {
key: 'render',
value: function render() {
var _this5 = this;
var _props3 = this.props,
width = _props3.width,
height = _props3.height;
var _state6 = this.state,
autoplayChecksResolved = _state6.autoplayChecksResolved,
showPlayButton = _state6.showPlayButton;
var dimensions = {
width: width === 'auto' ? width : '100%',
height: height === 'auto' ? height : '100%'
};
var contentStyle = _extends({}, dimensions, {
top: 0,
left: 0,
position: 'absolute',
zIndex: 1
});
var playButtonStyle = {
borderStyle: 'solid',
borderWidth: '16px 0 16px 26px',
borderColor: 'transparent transparent transparent white',
cursor: 'pointer',
left: '50%',
marginLeft: '-8px',
marginTop: '-8px',
position: 'absolute',
top: '50%',
zIndex: 2
};
return _react2['default'].createElement(
'div',
{ style: _extends({}, dimensions, { position: 'relative' }) },
showPlayButton && _react2['default'].createElement('div', { style: playButtonStyle, id: this.playButtonID, onClick: function onClick(e) {
return _this5.play(e);
} }),
_react2['default'].createElement('video', {
ref: this.playerRef,
controls: false,
style: dimensions,
id: this.playerID
}),
!autoplayChecksResolved && this.renderTestPlayer(),
_react2['default'].createElement('div', { id: this.contentID, style: contentStyle, ref: this.containerRef })
);
}
}]);
return VAST;
}(_react.Component);
VAST.displayName = 'VAST';
VAST.canPlay = function (url) {
return MATCH_URL.test(url);
};
exports['default'] = (0, _singlePlayer2['default'])(VAST);