视频播放器仓库

video.js 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /**
  2. * This is a HoC that finds a single
  3. * <video> in a component and makes
  4. * all its PROPERTIES available as props.
  5. */
  6. import React, { Component } from 'react';
  7. import { findDOMNode } from 'react-dom';
  8. import {
  9. EVENTS,
  10. PROPERTIES,
  11. TRACKEVENTS
  12. } from './constants';
  13. const defaultMapStateToProps = (state = {}) => Object.assign({
  14. video: {
  15. ...state
  16. }
  17. });
  18. const defaultMapVideoElToProps = (videoEl) => ({
  19. videoEl
  20. });
  21. const defaultMergeProps = (
  22. stateProps = {},
  23. videoElProps = {},
  24. ownProps = {}
  25. ) => Object.assign({}, stateProps, videoElProps, ownProps);
  26. export default (
  27. BaseComponent,
  28. mapStateToProps = defaultMapStateToProps,
  29. mapVideoElToProps = defaultMapVideoElToProps,
  30. mergeProps = defaultMergeProps
  31. ) => class Video extends Component {
  32. constructor(props) {
  33. super(props);
  34. this.updateState = this.updateState.bind(this);
  35. this.state = {};
  36. }
  37. updateState() {
  38. this.setState(
  39. PROPERTIES.reduce((p, c) => {
  40. p[c] = this.videoEl && this.videoEl[c];
  41. if (c == 'playbackrates' && this.videoEl.dataset[c]) {
  42. p[c] = this.videoEl && JSON.parse(this.videoEl.dataset[c]);
  43. }
  44. return p;
  45. }, {})
  46. );
  47. }
  48. bindEventsToUpdateState() {
  49. EVENTS.forEach(event => {
  50. this.videoEl.addEventListener(event.toLowerCase(), this.updateState);
  51. });
  52. TRACKEVENTS.forEach(event => {
  53. // TODO: JSDom does not have this method on
  54. // `textTracks`. Investigate so we can test this without this check.
  55. this.videoEl.textTracks.addEventListener
  56. && this.videoEl.textTracks.addEventListener(event.toLowerCase(), this.updateState);
  57. });
  58. // If <source> elements are used instead of a src attribute then
  59. // errors for unsupported format do not bubble up to the <video>.
  60. // Do this manually by listening to the last <source> error event
  61. // to force an update.
  62. // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML5_audio_and_video
  63. const sources = this.videoEl.getElementsByTagName('source');
  64. if (sources.length) {
  65. const lastSource = sources[sources.length - 1];
  66. lastSource.addEventListener('error', this.updateState);
  67. }
  68. }
  69. unbindEvents() {
  70. EVENTS.forEach(event => {
  71. this.videoEl.removeEventListener(event.toLowerCase(), this.updateState);
  72. });
  73. TRACKEVENTS.forEach(event => {
  74. // TODO: JSDom does not have this method on
  75. // `textTracks`. Investigate so we can test this without this check.
  76. this.videoEl.textTracks.removeEventListener
  77. && this.videoEl.textTracks.removeEventListener(event.toLowerCase(), this.updateState);
  78. });
  79. const sources = this.videoEl.getElementsByTagName('source');
  80. if (sources.length) {
  81. const lastSource = sources[sources.length - 1];
  82. lastSource.removeEventListener('error', this.updateState);
  83. }
  84. }
  85. componentWillUnmount() {
  86. this.unbindEvents();
  87. }
  88. // Stop `this.el` from being null briefly on every render,
  89. // see: https://github.com/mderrick/react-html5video/pull/65
  90. setRef(el) {
  91. this.el = findDOMNode(el);
  92. }
  93. componentDidMount() {
  94. this.videoEl = this.el.getElementsByTagName('video')[0];
  95. this.bindEventsToUpdateState();
  96. }
  97. render() {
  98. const stateProps = mapStateToProps(
  99. this.state,
  100. this.props
  101. );
  102. const videoElProps = mapVideoElToProps(
  103. this.videoEl,
  104. this.state,
  105. this.props
  106. );
  107. return (
  108. <div ref={this.setRef.bind(this)}>
  109. <BaseComponent
  110. {...mergeProps(
  111. stateProps,
  112. videoElProps,
  113. this.props)} />
  114. </div>
  115. );
  116. }
  117. }