UNPKG

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
'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);