Player.jsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import React, { Component } from "react";
  2. import PropTypes from "prop-types";
  3. const i18next = require("i18next");
  4. import { connect } from "react-redux";
  5. import { pauseStation, unpauseStation } from "actions/station";
  6. const t = i18next.t;
  7. let getPlayerCallbacks = [];
  8. @connect(state => ({
  9. volume: state.volume.get("volume"),
  10. muted: state.volume.get("muted"),
  11. songId: state.songPlayer.get("songId"),
  12. startedAt: state.songPlayer.get("startedAt"),
  13. timePaused: state.songPlayer.get("timePaused"),
  14. skipDuration: state.songPlayer.get("skipDuration"),
  15. pausedAt: state.songPlayer.get("pausedAt"),
  16. exists: state.songPlayer.get("exists"),
  17. paused: state.station.get("paused"),
  18. }))
  19. export default class Player extends Component {
  20. static propTypes = {
  21. onRef: PropTypes.func,
  22. };
  23. static defaultProps = {
  24. onRef: () => {},
  25. };
  26. constructor(props) {
  27. super(props);
  28. this.state = {
  29. player: {
  30. initializing: false,
  31. ready: false,
  32. loading: false,
  33. },
  34. };
  35. if (props.paused) this.pause();
  36. }
  37. componentDidMount() {
  38. this.props.onRef(this);
  39. this.setState({
  40. seekerbar: this.seekerbar,
  41. });
  42. this.initializePlayer();
  43. }
  44. componentWillUnmount() {
  45. this.props.onRef(null);
  46. }
  47. clearSong() {
  48. this.getPlayer((player) => {
  49. player.loadVideoById("");
  50. });
  51. }
  52. playSong() {
  53. this.getPlayer((player) => {
  54. this.setState({
  55. player: {
  56. ...this.state.player,
  57. loading: true,
  58. },
  59. });
  60. player.loadVideoById(this.props.songId, this.getProperVideoTime());
  61. });
  62. }
  63. getProperVideoTime = () => {
  64. if (this.props.exists) {
  65. return this.getTimeElapsed() / 1000 + this.props.skipDuration;
  66. } else return 0;
  67. };
  68. getTimeElapsed = () => {
  69. if (this.props.exists) {
  70. // TODO Replace with Date.currently
  71. let timePausedNow = 0;
  72. if (this.props.paused) timePausedNow = Date.now() - this.props.pausedAt;
  73. return Date.now() - this.props.startedAt - this.props.timePaused - timePausedNow;
  74. } else return 0;
  75. };
  76. pause() {
  77. this.getPlayer((player) => {
  78. player.pauseVideo();
  79. });
  80. }
  81. resume() {
  82. this.getPlayer((player) => {
  83. player.playVideo();
  84. });
  85. }
  86. mute() {
  87. this.getPlayer((player) => {
  88. player.mute();
  89. });
  90. }
  91. unmute() {
  92. this.getPlayer((player) => {
  93. player.unMute();
  94. });
  95. }
  96. initializePlayer = () => {
  97. // TODO Ensure YT.Player exists
  98. if (this.state.player.ready || this.state.player.initializing) return;
  99. this.setState({
  100. player: {
  101. ...this.state.player,
  102. initializing: true,
  103. },
  104. });
  105. this.player = new YT.Player("player", {
  106. height: 270,
  107. width: 480,
  108. videoId: "",
  109. playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
  110. events: {
  111. "onReady": () => {
  112. this.setState({
  113. player: {
  114. ...this.state.player,
  115. initializing: false,
  116. ready: true,
  117. },
  118. });
  119. getPlayerCallbacks.forEach((cb) => {
  120. cb(this.player);
  121. });
  122. this.player.setVolume(this.props.volume);
  123. if (this.props.muted) this.mute();
  124. else this.unmute();
  125. },
  126. "onError": function(err) {
  127. console.log("iframe error", err);
  128. // VOTE TO SKIP SONG
  129. },
  130. "onStateChange": (event) => {
  131. this.getPlayer((player) => {
  132. if (event.data === YT.PlayerState.PLAYING) {
  133. if (this.state.player.loading) this.setState({
  134. player: {
  135. ...this.state.player,
  136. loading: false,
  137. },
  138. });
  139. if (this.props.paused) player.pauseVideo();
  140. if (this.props.paused || this.state.player.loading) player.seekTo(this.getProperVideoTime(), true);
  141. }
  142. if (event.data === YT.PlayerState.PAUSED) {
  143. if (!this.props.paused) {
  144. player.seekTo(this.getProperVideoTime(), true);
  145. player.playVideo();
  146. }
  147. }
  148. });
  149. },
  150. },
  151. });
  152. };
  153. getPlayer(cb) {
  154. if (!this.state.player.ready) getPlayerCallbacks.push(cb);
  155. else cb(this.player);
  156. };
  157. componentWillUpdate(nextProps) {
  158. if (nextProps.volume !== this.props.volume) {
  159. this.getPlayer((player) => {
  160. player.setVolume(nextProps.volume);
  161. });
  162. }
  163. }
  164. componentDidUpdate(prevProps, prevState) {
  165. if (this.props.songId !== prevProps.songId && this.props.startedAt !== prevProps.startedAt) { //Add unique token instead of comparing startedAt
  166. if (this.props.exists) {
  167. this.playSong();
  168. } else this.clearSong();
  169. }
  170. if (this.props.paused !== prevProps.paused) { //Add unique token instead of comparing startedAt
  171. if (this.props.paused) this.pause();
  172. else this.resume();
  173. }
  174. if (this.props.muted !== prevProps.muted) {
  175. if (this.props.muted) this.mute();
  176. else this.unmute();
  177. }
  178. }
  179. render() {
  180. return (
  181. <div id="player"/>
  182. );
  183. }
  184. }