视频播放器仓库

DefaultPlayer.js 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import videoConnect from './../video/video';
  4. import copy from './copy';
  5. import {
  6. setVolume,
  7. showTrack,
  8. showSpeed,
  9. toggleTracks,
  10. toggleSpeeds,
  11. toggleMute,
  12. togglePause,
  13. setCurrentTime,
  14. toggleFullscreen,
  15. getPercentagePlayed,
  16. getPercentageBuffered
  17. } from './../video/api';
  18. import styles from './DefaultPlayer.css';
  19. import Time from './Time/Time';
  20. import Seek from './Seek/Seek';
  21. import Volume from './Volume/Volume';
  22. import Captions from './Captions/Captions';
  23. import Speed from './Speed/Speed';
  24. import PlayPause from './PlayPause/PlayPause';
  25. import Fullscreen from './Fullscreen/Fullscreen';
  26. import Overlay from './Overlay/Overlay';
  27. const DefaultPlayer = ({
  28. copy,
  29. video,
  30. style,
  31. controls,
  32. children,
  33. className,
  34. onSeekChange,
  35. onVolumeChange,
  36. onVolumeClick,
  37. onCaptionsClick,
  38. onSpeedClick,
  39. onPlayPauseClick,
  40. onFullscreenClick,
  41. onCaptionsItemClick,
  42. onSpeedsItemClick,
  43. ...restProps
  44. }) => {
  45. let playbackrates = restProps['data-playbackrates'];
  46. if (playbackrates) {
  47. playbackrates = JSON.parse(playbackrates);
  48. }
  49. let onScreenClickCallback = restProps['onScreenClickCallback'];
  50. return (
  51. <div className={[
  52. styles.component,
  53. className
  54. ].join(' ')}
  55. style={style}>
  56. <video
  57. className={styles.video}
  58. {...restProps}>
  59. { children }
  60. </video>
  61. <Overlay
  62. onClick={onPlayPauseClick}
  63. {...video} />
  64. { controls && controls.length && !video.error
  65. ? <div className={styles.controls}>
  66. { controls.map((control, i) => {
  67. switch (control) {
  68. case 'Seek':
  69. return <Seek
  70. key={i}
  71. ariaLabel={copy.seek}
  72. className={styles.seek}
  73. onChange={onSeekChange}
  74. {...video} />;
  75. case 'PlayPause':
  76. return <PlayPause
  77. key={i}
  78. ariaLabelPlay={copy.play}
  79. ariaLabelPause={copy.pause}
  80. onClick={onPlayPauseClick}
  81. {...video} />;
  82. case 'Fullscreen':
  83. return <Fullscreen
  84. key={i}
  85. ariaLabel={copy.fullscreen}
  86. onClick={onFullscreenClick}
  87. onScreenClickCallback={onScreenClickCallback}
  88. {...video} />;
  89. case 'Time':
  90. return <Time
  91. key={i}
  92. {...video} />;
  93. case 'Volume':
  94. return <Volume
  95. key={i}
  96. onClick={onVolumeClick}
  97. onChange={onVolumeChange}
  98. ariaLabelMute={copy.mute}
  99. ariaLabelUnmute={copy.unmute}
  100. ariaLabelVolume={copy.volume}
  101. {...video} />;
  102. case 'Captions':
  103. return video.textTracks && video.textTracks.length
  104. ? <Captions
  105. key={i}
  106. onClick={onCaptionsClick}
  107. ariaLabel={copy.captions}
  108. onItemClick={onCaptionsItemClick}
  109. {...video}/>
  110. : null;
  111. case 'Speed':
  112. return playbackrates && playbackrates.length > 0
  113. ? <Speed
  114. key={i}
  115. onClick={onSpeedClick}
  116. ariaLabel={copy.captions}
  117. onItemClick={onSpeedsItemClick}
  118. playbackrates={playbackrates}
  119. {...video}/>
  120. : null;
  121. default:
  122. return null;
  123. }
  124. }) }
  125. </div>
  126. : null }
  127. </div>
  128. );
  129. };
  130. const controls = ['PlayPause', 'Seek', 'Time', 'Volume', 'Captions', 'Speed', 'Fullscreen'];
  131. DefaultPlayer.defaultProps = {
  132. copy,
  133. controls,
  134. video: {}
  135. };
  136. DefaultPlayer.propTypes = {
  137. copy: PropTypes.object.isRequired,
  138. controls: PropTypes.arrayOf(PropTypes.oneOf(controls)),
  139. video: PropTypes.object.isRequired
  140. };
  141. const connectedPlayer = videoConnect(
  142. DefaultPlayer,
  143. ({ networkState, readyState, error, ...restState }) => ({
  144. video: {
  145. readyState,
  146. networkState,
  147. error: error || networkState === 3,
  148. // TODO: This is not pretty. Doing device detection to remove
  149. // spinner on iOS devices for a quick and dirty win. We should see if
  150. // we can use the same readyState check safely across all browsers.
  151. loading: readyState < (/iPad|iPhone|iPod/.test(navigator.userAgent) ? 1 : 4),
  152. percentagePlayed: getPercentagePlayed(restState),
  153. percentageBuffered: getPercentageBuffered(restState),
  154. ...restState
  155. }
  156. }),
  157. (videoEl, state) => ({
  158. onFullscreenClick: () => toggleFullscreen(videoEl.parentElement),
  159. onVolumeClick: () => toggleMute(videoEl, state),
  160. onCaptionsClick: () => toggleTracks(state),
  161. onSpeedClick: () => toggleSpeeds(videoEl, state),
  162. onPlayPauseClick: () => togglePause(videoEl, state),
  163. onCaptionsItemClick: (track) => showTrack(state, track),
  164. onSpeedsItemClick: (speed) => showSpeed(videoEl, state, speed),
  165. onVolumeChange: (e) => setVolume(videoEl, state, e.target.value),
  166. onSeekChange: (e) => setCurrentTime(videoEl, state, e.target.value * state.duration / 100)
  167. })
  168. );
  169. export {
  170. connectedPlayer as default,
  171. DefaultPlayer,
  172. Time,
  173. Seek,
  174. Volume,
  175. Captions,
  176. PlayPause,
  177. Fullscreen,
  178. Overlay
  179. };