Player.jsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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("loudness"),
  10. muted: state.volume.get("muted"),
  11. songId: state.station.currentSong.get("songId"),
  12. startedAt: state.station.currentSong.getIn(["timings", "startedAt"]),
  13. timePaused: state.station.currentSong.getIn(["timings", "timePaused"]),
  14. skipDuration: state.station.currentSong.getIn(["timings", "skipDuration"]),
  15. pausedAt: state.station.info.get("pausedAt"),
  16. exists: state.station.currentSong.get("songId") !== "",
  17. paused: state.station.info.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 = (force) => {
  97. if ((this.state.player.ready || this.state.player.initializing) && !force) return;
  98. if (!force) {
  99. this.setState({
  100. player: {
  101. ...this.state.player,
  102. initializing: true,
  103. },
  104. });
  105. }
  106. if (!YT.Player) {
  107. setTimeout(() => {
  108. this.initializePlayer(true);
  109. }, 100);
  110. } else {
  111. this.player = new YT.Player("player", {
  112. height: 270,
  113. width: 480,
  114. videoId: "",
  115. playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
  116. events: {
  117. "onReady": () => {
  118. this.setState({
  119. player: {
  120. ...this.state.player,
  121. initializing: false,
  122. ready: true,
  123. },
  124. });
  125. getPlayerCallbacks.forEach((cb) => {
  126. cb(this.player);
  127. });
  128. this.player.setVolume(this.props.volume);
  129. if (this.props.muted) this.mute();
  130. else this.unmute();
  131. },
  132. "onError": function (err) {
  133. console.log("iframe error", err);
  134. // VOTE TO SKIP SONG
  135. },
  136. "onStateChange": (event) => {
  137. this.getPlayer((player) => {
  138. if (event.data === YT.PlayerState.PLAYING) {
  139. if (this.state.player.loading) this.setState({
  140. player: {
  141. ...this.state.player,
  142. loading: false,
  143. },
  144. });
  145. if (this.props.paused) player.pauseVideo();
  146. if (this.props.paused || this.state.player.loading) player.seekTo(this.getProperVideoTime(), true);
  147. }
  148. if (event.data === YT.PlayerState.PAUSED) {
  149. if (!this.props.paused) {
  150. player.seekTo(this.getProperVideoTime(), true);
  151. player.playVideo();
  152. }
  153. }
  154. });
  155. },
  156. },
  157. });
  158. }
  159. };
  160. getPlayer(cb) {
  161. if (!this.state.player.ready) getPlayerCallbacks.push(cb);
  162. else cb(this.player);
  163. };
  164. componentWillUpdate(nextProps) {
  165. if (nextProps.volume !== this.props.volume) {
  166. this.getPlayer((player) => {
  167. player.setVolume(nextProps.volume);
  168. });
  169. }
  170. }
  171. componentDidUpdate(prevProps, prevState) {
  172. if (this.props.songId !== prevProps.songId && this.props.startedAt !== prevProps.startedAt) { //Add unique token instead of comparing startedAt
  173. if (this.props.exists) {
  174. this.playSong();
  175. } else this.clearSong();
  176. }
  177. if (this.props.paused !== prevProps.paused) { //Add unique token instead of comparing startedAt
  178. if (this.props.paused) this.pause();
  179. else this.resume();
  180. }
  181. if (this.props.muted !== prevProps.muted) {
  182. if (this.props.muted) this.mute();
  183. else this.unmute();
  184. }
  185. }
  186. render() {
  187. return (
  188. <div id="player"/>
  189. );
  190. }
  191. }