@lahzenegar/video-react
Version:
Video-React is a web video player built from the ground up for an HTML5 world using React library.
652 lines (540 loc) • 19.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _Manager = require('../Manager');
var _Manager2 = _interopRequireDefault(_Manager);
var _LoadingSpinner = require('./LoadingSpinner');
var _LoadingSpinner2 = _interopRequireDefault(_LoadingSpinner);
var _AudioVisualizer = require('./AudioVisualizer');
var _AudioVisualizer2 = _interopRequireDefault(_AudioVisualizer);
var _PosterImage = require('./PosterImage');
var _PosterImage2 = _interopRequireDefault(_PosterImage);
var _Video = require('./Video');
var _Video2 = _interopRequireDefault(_Video);
var _Bezel = require('./Bezel');
var _Bezel2 = _interopRequireDefault(_Bezel);
var _Shortcut = require('./Shortcut');
var _Shortcut2 = _interopRequireDefault(_Shortcut);
var _ControlBar = require('./control-bar/ControlBar');
var _ControlBar2 = _interopRequireDefault(_ControlBar);
var _utils = require('../utils');
var _fullscreen = require('../utils/fullscreen');
var _fullscreen2 = _interopRequireDefault(_fullscreen);
var _browser = require('../utils/browser');
var browser = _interopRequireWildcard(_browser);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var propTypes = {
children: _propTypes2.default.any,
width: _propTypes2.default.number,
height: _propTypes2.default.number,
fluid: _propTypes2.default.bool,
muted: _propTypes2.default.bool,
aspectRatio: _propTypes2.default.string,
className: _propTypes2.default.string,
videoId: _propTypes2.default.string,
startTime: _propTypes2.default.number,
dvrThreshold: _propTypes2.default.number,
hasAudioSrc: _propTypes2.default.bool,
hasAds: _propTypes2.default.bool,
targetTotalTime: _propTypes2.default.number,
adsTagUrl: _propTypes2.default.string,
loop: _propTypes2.default.bool,
autoPlay: _propTypes2.default.bool,
src: _propTypes2.default.string,
poster: _propTypes2.default.string,
preload: _propTypes2.default.oneOf(['auto', 'metadata', 'none']),
onLoadStart: _propTypes2.default.func,
onWaiting: _propTypes2.default.func,
onCanPlay: _propTypes2.default.func,
onCanPlayThrough: _propTypes2.default.func,
onPlaying: _propTypes2.default.func,
onEnded: _propTypes2.default.func,
onSeeking: _propTypes2.default.func,
onSeeked: _propTypes2.default.func,
onPlay: _propTypes2.default.func,
onPause: _propTypes2.default.func,
onProgress: _propTypes2.default.func,
onDurationChange: _propTypes2.default.func,
onError: _propTypes2.default.func,
onSuspend: _propTypes2.default.func,
onAbort: _propTypes2.default.func,
onEmptied: _propTypes2.default.func,
onStalled: _propTypes2.default.func,
onLoadedMetadata: _propTypes2.default.func,
onLoadedData: _propTypes2.default.func,
onTimeUpdate: _propTypes2.default.func,
onRateChange: _propTypes2.default.func,
onVolumeChange: _propTypes2.default.func,
onStatsChange: _propTypes2.default.func,
onUserActivateChange: _propTypes2.default.func,
onWaitingStop: _propTypes2.default.func,
onStartFirstTime: _propTypes2.default.func,
onSourceChange: _propTypes2.default.func,
onSecondsPlayedReached: _propTypes2.default.func,
onAllAdsCompleted: _propTypes2.default.func,
store: _propTypes2.default.object,
sendAdsEvent: _propTypes2.default.func,
onAdError: _propTypes2.default.func,
onAdStart: _propTypes2.default.func,
debugMode: _propTypes2.default.bool,
onAdPlaying: _propTypes2.default.func,
onAdStartFirstTime: _propTypes2.default.func,
onVASTLoadedFirstTime: _propTypes2.default.func,
onFirstPlayAttempt: _propTypes2.default.func,
onStartError: _propTypes2.default.func,
chapters: _propTypes2.default.array,
storyboard: _propTypes2.default.object
};
var defaultProps = {
dvrThreshold: 300,
hasAudioSrc: false,
hasAds: false,
fluid: true,
muted: false,
targetTotalTime: 10,
aspectRatio: 'auto'
};
var Player = function (_Component) {
(0, _inherits3.default)(Player, _Component);
function Player(props) {
(0, _classCallCheck3.default)(this, Player);
var _this = (0, _possibleConstructorReturn3.default)(this, (Player.__proto__ || (0, _getPrototypeOf2.default)(Player)).call(this, props));
_this.handleClick = function (event) {
if (_this.manager && _this.manager.rootElement && event && event.target) {
if (_this.manager.rootElement.contains(event.target)) {
_this.handleFocus();
} else {
_this.handleBlur();
}
}
};
_this.controlsHideTimer = null;
_this.statsUpdateInterval = null;
_this.lastSecondPlayed = 0;
_this.lastPercentPlayed = 0;
_this.lastWaitingTime = 0;
_this.video = null; // the Video component
_this.manager = new _Manager2.default(props.store);
_this.actions = _this.manager.getActions();
_this.manager.subscribeToPlayerStateChange(_this.handleStateChange.bind(_this));
_this.getStyle = _this.getStyle.bind(_this);
_this.handleResize = _this.handleResize.bind(_this);
_this.getChildren = _this.getChildren.bind(_this);
_this.handleMouseMove = (0, _utils.throttle)(_this.handleMouseMove.bind(_this), 250);
_this.handleMouseDown = _this.handleMouseDown.bind(_this);
_this.startControlsTimer = _this.startControlsTimer.bind(_this);
_this.handleFullScreenChange = _this.handleFullScreenChange.bind(_this);
_this.handleKeyDown = _this.handleKeyDown.bind(_this);
_this.handleFocus = _this.handleFocus.bind(_this);
_this.handleBlur = _this.handleBlur.bind(_this);
return _this;
}
(0, _createClass3.default)(Player, [{
key: 'componentDidMount',
value: function componentDidMount() {
this.handleResize();
this.handleStatsChange();
window.addEventListener('resize', this.handleResize);
document.addEventListener('mousedown', this.handleClick, false);
_fullscreen2.default.addEventListener(this.handleFullScreenChange);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
// Remove event listener
window.removeEventListener('resize', this.handleResize);
document.removeEventListener('mousedown', this.handleClick, false);
_fullscreen2.default.removeEventListener(this.handleFullScreenChange);
if (this.controlsHideTimer) {
window.clearTimeout(this.controlsHideTimer);
}
if (this.statsUpdateInterval) {
clearInterval(this.statsUpdateInterval);
}
}
}, {
key: 'getDefaultChildren',
value: function getDefaultChildren(props, fullProps) {
var _this2 = this;
return [_react2.default.createElement(_Video2.default, (0, _extends3.default)({
ref: function ref(c) {
_this2.video = c;
_this2.manager.video = _this2.video;
},
key: 'video',
order: 0.0
}, fullProps)), _react2.default.createElement(_PosterImage2.default, (0, _extends3.default)({
key: 'poster-image',
order: 1.0
}, props)), _react2.default.createElement(_LoadingSpinner2.default, (0, _extends3.default)({
key: 'loading-spinner',
order: 2.0
}, props)), _react2.default.createElement(_AudioVisualizer2.default, (0, _extends3.default)({
key: 'audio-visualizer',
order: 3.0
}, props)), _react2.default.createElement(_Bezel2.default, (0, _extends3.default)({
key: 'bezel',
order: 4.0
}, props)), _react2.default.createElement(_ControlBar2.default, (0, _extends3.default)({
key: 'control-bar',
order: 6.0
}, props)), _react2.default.createElement(_Shortcut2.default, (0, _extends3.default)({
key: 'shortcut',
order: 99.0
}, props))];
}
}, {
key: 'getChildren',
value: function getChildren(props) {
var propsWithoutChildren = (0, _extends3.default)({}, props, {
children: null
});
var children = _react2.default.Children.toArray(this.props.children).filter(function (e) {
return !(0, _utils.isVideoChild)(e);
});
var defaultChildren = this.getDefaultChildren(propsWithoutChildren, props);
return (0, _utils.mergeAndSortChildren)(defaultChildren, children, propsWithoutChildren);
}
}, {
key: 'getStyle',
value: function getStyle() {
var fluid = this.props.fluid;
var _manager$getState = this.manager.getState(),
player = _manager$getState.player;
var style = {};
var width = void 0;
var height = void 0;
var aspectRatio = void 0;
// The aspect ratio is either used directly or to calculate width and height.
if (this.props.aspectRatio !== undefined && this.props.aspectRatio !== 'auto') {
// Use any aspectRatio that's been specifically set
aspectRatio = this.props.aspectRatio;
} else if (player.videoWidth) {
// Otherwise try to get the aspect ratio from the video metadata
aspectRatio = player.videoWidth + ':' + player.videoHeight;
} else {
// Or use a default. The video element's is 2:1, but 16:9 is more common.
aspectRatio = '16:9';
}
// Get the ratio as a decimal we can use to calculate dimensions
var ratioParts = aspectRatio.split(':');
var ratioMultiplier = ratioParts[1] / ratioParts[0];
if (this.props.width !== undefined) {
// Use any width that's been specifically set
width = this.props.width;
} else if (this.props.height !== undefined) {
// Or calulate the width from the aspect ratio if a height has been set
width = this.props.height / ratioMultiplier;
} else {
// Or use the video's metadata, or use the video el's default of 300
width = player.videoWidth || 400;
}
if (this.props.height !== undefined) {
// Use any height that's been specifically set
height = this.props.height;
} else {
// Otherwise calculate the height from the ratio and the width
height = width * ratioMultiplier;
}
if (fluid) {
style.paddingTop = ratioMultiplier * 100 + '%';
} else {
style.width = width + 'px';
style.height = height + 'px';
}
return style;
}
// get redux state
// { player, operation }
}, {
key: 'getState',
value: function getState() {
return this.manager.getState();
}
// get playback rate
}, {
key: 'play',
// play the video
value: function play() {
this.video.play();
}
// pause the video
}, {
key: 'pause',
value: function pause() {
this.video.pause();
}
// Change the video source and re-load the video:
}, {
key: 'load',
value: function load() {
this.video.load();
}
// Add a new text track to the video
}, {
key: 'addTextTrack',
value: function addTextTrack() {
var _video;
(_video = this.video).addTextTrack.apply(_video, arguments);
}
// Check if your browser can play different types of video:
}, {
key: 'canPlayType',
value: function canPlayType() {
var _video2;
(_video2 = this.video).canPlayType.apply(_video2, arguments);
}
// seek video by time
}, {
key: 'seek',
value: function seek(time) {
this.video.seek(time);
}
// jump forward x seconds
}, {
key: 'forward',
value: function forward(seconds) {
this.video.forward(seconds);
}
// jump back x seconds
}, {
key: 'replay',
value: function replay(seconds) {
this.video.replay(seconds);
}
// enter or exist full screen
}, {
key: 'toggleFullscreen',
value: function toggleFullscreen() {
this.video.toggleFullscreen();
}
// subscribe to player state change
}, {
key: 'subscribeToStateChange',
value: function subscribeToStateChange(listener) {
return this.manager.subscribeToPlayerStateChange(listener);
}
// player resize
}, {
key: 'handleResize',
value: function handleResize() {}
}, {
key: 'handleFullScreenChange',
value: function handleFullScreenChange() {
this.actions.handleFullscreenChange(_fullscreen2.default.isFullscreen);
}
}, {
key: 'handleMouseDown',
value: function handleMouseDown() {
this.startControlsTimer();
}
}, {
key: 'handleMouseMove',
value: function handleMouseMove() {
this.startControlsTimer();
}
}, {
key: 'handleKeyDown',
value: function handleKeyDown() {
this.startControlsTimer();
}
}, {
key: 'startControlsTimer',
value: function startControlsTimer() {
var _this3 = this;
var onUserActivateChange = this.props.onUserActivateChange;
this.actions.userActivate(true);
if (onUserActivateChange) {
onUserActivateChange(true);
}
clearTimeout(this.controlsHideTimer);
this.controlsHideTimer = setTimeout(function () {
_this3.actions.userActivate(false);
if (onUserActivateChange) {
onUserActivateChange(false);
}
}, 3000);
}
}, {
key: 'handleStateChange',
value: function handleStateChange(state, prevState) {
if (state.isFullscreen !== prevState.isFullscreen) {
this.handleResize();
}
this.forceUpdate(); // re-render
}
}, {
key: 'handleFocus',
value: function handleFocus() {
this.actions.activate(true);
}
}, {
key: 'handleBlur',
value: function handleBlur() {
this.actions.activate(false);
}
}, {
key: 'handleStatsChange',
value: function handleStatsChange() {
var _this4 = this;
var onStatsChange = this.props.onStatsChange;
if (this.statsUpdateInterval) {
clearInterval(this.statsUpdateInterval);
}
this.statsUpdateInterval = setInterval(function () {
var _manager$getState2 = _this4.manager.getState(),
_manager$getState2$pl = _manager$getState2.player,
secondsPlayed = _manager$getState2$pl.secondsPlayed,
percentPlayed = _manager$getState2$pl.percentPlayed,
waitingTime = _manager$getState2$pl.waitingTime,
tracks = _manager$getState2$pl.tracks,
realActiveTrack = _manager$getState2$pl.realActiveTrack,
ads = _manager$getState2$pl.ads;
if (ads) {
return;
}
if (onStatsChange) {
var realActiveTrackLabel = null;
if (tracks && tracks.length > 1) {
realActiveTrackLabel = tracks.filter(function (item) {
return item.id === realActiveTrack;
})[0].label;
}
onStatsChange({
percentPlayed: percentPlayed - _this4.lastPercentPlayed,
waitingTime: Math.round((waitingTime - _this4.lastWaitingTime) * 100) / 100,
quality: realActiveTrackLabel,
secondsPlayed: Math.round((secondsPlayed - _this4.lastSecondPlayed) * 100) / 100
});
_this4.lastSecondPlayed = secondsPlayed;
_this4.lastPercentPlayed = percentPlayed;
_this4.lastWaitingTime = waitingTime;
}
}, 10000);
}
}, {
key: 'render',
value: function render() {
var _this5 = this;
var fluid = this.props.fluid;
var _manager$getState3 = this.manager.getState(),
player = _manager$getState3.player;
var paused = player.paused,
hasStarted = player.hasStarted,
waiting = player.waiting,
seeking = player.seeking,
isFullscreen = player.isFullscreen,
userActivity = player.userActivity,
isLive = player.isLive,
ads = player.ads,
contentLoading = player.contentLoading;
var props = (0, _extends3.default)({}, this.props, {
player: player,
actions: this.actions,
manager: this.manager,
store: this.manager.store,
video: this.video ? this.video.video : null
});
var children = this.getChildren(props);
return _react2.default.createElement(
'div',
{
className: (0, _classnames2.default)({
'video-react-controls-enabled': !browser.IS_MOBILE,
'video-react-controls-disabled': browser.IS_MOBILE,
'video-react-native-player': browser.IS_MOBILE,
'video-react-native-seek-bar-disabled': isLive,
'video-react-has-started': hasStarted,
'video-react-paused': paused,
'video-react-playing': !paused,
'video-react-waiting': waiting || contentLoading,
'video-react-seeking': seeking,
'video-react-fluid': fluid,
'video-react-ads-playing': ads,
'video-react-fullscreen': isFullscreen,
'video-react-user-inactive': !userActivity,
'video-react-user-active': userActivity,
'video-react-workinghover': true
}, 'video-react', this.props.className),
style: this.getStyle(),
ref: function ref(c) {
_this5.manager.rootElement = c;
},
onTouchStart: this.handleMouseDown,
onMouseDown: this.handleMouseDown,
onMouseMove: this.handleMouseMove,
onKeyDown: this.handleKeyDown,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
tabIndex: '-1'
},
children
);
}
}, {
key: 'playbackRate',
get: function get() {
return this.video.playbackRate;
}
// set playback rate
// speed of video
,
set: function set(rate) {
this.video.playbackRate = rate;
}
}, {
key: 'muted',
get: function get() {
return this.video.muted;
},
set: function set(val) {
this.video.muted = val;
}
}, {
key: 'volume',
get: function get() {
return this.video.volume;
},
set: function set(val) {
this.video.volume = val;
}
// video width
}, {
key: 'videoWidth',
get: function get() {
return this.video.videoWidth;
}
// video height
}, {
key: 'videoHeight',
get: function get() {
return this.video.videoHeight;
}
}]);
return Player;
}(_react.Component);
exports.default = Player;
Player.contextTypes = { store: _propTypes2.default.object };
Player.propTypes = propTypes;
Player.defaultProps = defaultProps;
Player.displayName = 'Player';