2016-08-10 23:50:23 +02:00
|
|
|
// NPM imports
|
2016-08-05 00:00:25 +02:00
|
|
|
import React, { Component } from "react";
|
2016-08-07 00:58:36 +02:00
|
|
|
import { bindActionCreators } from "redux";
|
|
|
|
import { connect } from "react-redux";
|
|
|
|
import { Howl } from "howler";
|
2016-08-06 15:30:03 +02:00
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
// Actions
|
2016-08-07 00:58:36 +02:00
|
|
|
import * as actionCreators from "../actions";
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
// Components
|
2016-08-05 00:00:25 +02:00
|
|
|
import WebPlayerComponent from "../components/elements/WebPlayer";
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Webplayer container.
|
|
|
|
*/
|
2016-08-07 00:58:36 +02:00
|
|
|
class WebPlayer extends Component {
|
2016-08-10 23:50:23 +02:00
|
|
|
constructor(props) {
|
2016-08-07 00:58:36 +02:00
|
|
|
super(props);
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
// Data attributes
|
2016-08-07 00:58:36 +02:00
|
|
|
this.howl = null;
|
2016-08-10 23:50:23 +02:00
|
|
|
|
|
|
|
// Bind this
|
|
|
|
this.startPlaying = this.startPlaying.bind(this);
|
2016-08-07 00:58:36 +02:00
|
|
|
}
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
componentDidMount() {
|
|
|
|
// Start playback upon component mount
|
|
|
|
this.startPlaying(this.props);
|
2016-08-07 00:58:36 +02:00
|
|
|
}
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
componentWillUpdate(nextProps) {
|
|
|
|
// Handle stop
|
|
|
|
if (!nextProps.currentSong || nextProps.playlist.size < 1) {
|
|
|
|
if (this.howl) {
|
|
|
|
this.howl.stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-07 00:58:36 +02:00
|
|
|
// Toggle play / pause
|
|
|
|
if (nextProps.isPlaying != this.props.isPlaying) {
|
2016-08-10 23:50:23 +02:00
|
|
|
// This check ensure we do not start playing multiple times the
|
|
|
|
// same song
|
|
|
|
this.startPlaying(nextProps);
|
2016-08-07 00:58:36 +02:00
|
|
|
}
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
// If something is playing back
|
2016-08-07 00:58:36 +02:00
|
|
|
if (this.howl) {
|
2016-08-10 23:50:23 +02:00
|
|
|
// Set mute / unmute
|
2016-08-07 00:58:36 +02:00
|
|
|
this.howl.mute(nextProps.isMute);
|
2016-08-10 23:50:23 +02:00
|
|
|
// Set volume
|
|
|
|
this.howl.volume(nextProps.volume / 100);
|
2016-08-07 00:58:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
/**
|
|
|
|
* Handle playback through Howler and Web Audio API.
|
|
|
|
*
|
|
|
|
* @params props A set of props to use for setting play parameters.
|
|
|
|
*/
|
|
|
|
startPlaying(props) {
|
|
|
|
if (props.isPlaying && props.currentSong) {
|
|
|
|
// If it should be playing any song
|
2016-08-07 00:58:36 +02:00
|
|
|
if (!this.howl) {
|
2016-08-10 23:50:23 +02:00
|
|
|
// Build a new Howler object with current song to play
|
|
|
|
const url = props.currentSong.get("url");
|
2016-08-07 00:58:36 +02:00
|
|
|
if (!url) {
|
|
|
|
// TODO: Error handling
|
2016-08-10 23:50:23 +02:00
|
|
|
console.error("URL not found.");
|
2016-08-07 00:58:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.howl = new Howl({
|
|
|
|
src: [url],
|
2016-08-10 23:50:23 +02:00
|
|
|
html5: true, // Use HTML5 by default to allow streaming
|
2016-08-07 00:58:36 +02:00
|
|
|
mute: props.isMute,
|
2016-08-10 23:50:23 +02:00
|
|
|
volume: props.volume / 100, // Set current volume
|
|
|
|
autoplay: false, // No autoplay, we handle it manually
|
2016-08-07 00:58:36 +02:00
|
|
|
});
|
2016-08-10 23:50:23 +02:00
|
|
|
} else {
|
|
|
|
// Else, something is playing
|
|
|
|
// TODO If it is not the expected song, change it
|
2016-08-07 00:58:36 +02:00
|
|
|
}
|
2016-08-10 23:50:23 +02:00
|
|
|
// Start playing
|
2016-08-07 00:58:36 +02:00
|
|
|
this.howl.play();
|
|
|
|
}
|
|
|
|
else {
|
2016-08-10 23:50:23 +02:00
|
|
|
// If it should not be playing
|
2016-08-07 00:58:36 +02:00
|
|
|
if (this.howl) {
|
2016-08-10 23:50:23 +02:00
|
|
|
// Pause any running music
|
2016-08-07 00:58:36 +02:00
|
|
|
this.howl.pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-05 00:00:25 +02:00
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
render() {
|
2016-08-05 00:00:25 +02:00
|
|
|
const webplayerProps = {
|
2016-08-07 00:58:36 +02:00
|
|
|
isPlaying: this.props.isPlaying,
|
|
|
|
isRandom: this.props.isRandom,
|
|
|
|
isRepeat: this.props.isRepeat,
|
|
|
|
isMute: this.props.isMute,
|
2016-08-10 23:50:23 +02:00
|
|
|
volume: this.props.volume,
|
|
|
|
currentIndex: this.props.currentIndex,
|
|
|
|
playlist: this.props.playlist,
|
|
|
|
currentSong: this.props.currentSong,
|
|
|
|
currentArtist: this.props.currentArtist,
|
|
|
|
// Use a lambda to ensure no first argument is passed to
|
|
|
|
// togglePlaying
|
2016-08-07 00:58:36 +02:00
|
|
|
onPlayPause: (() => this.props.actions.togglePlaying()),
|
|
|
|
onPrev: this.props.actions.playPrevious,
|
|
|
|
onSkip: this.props.actions.playNext,
|
|
|
|
onRandom: this.props.actions.toggleRandom,
|
|
|
|
onRepeat: this.props.actions.toggleRepeat,
|
2016-08-10 23:50:23 +02:00
|
|
|
onMute: this.props.actions.toggleMute,
|
2016-08-05 00:00:25 +02:00
|
|
|
};
|
|
|
|
return (
|
|
|
|
<WebPlayerComponent {...webplayerProps} />
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2016-08-10 23:50:23 +02:00
|
|
|
const mapStateToProps = (state) => {
|
|
|
|
const currentIndex = state.webplayer.currentIndex;
|
|
|
|
const playlist = state.webplayer.playlist;
|
2016-08-07 00:58:36 +02:00
|
|
|
|
2016-08-10 23:50:23 +02:00
|
|
|
// Get current song and artist from entities store
|
|
|
|
const currentSong = state.entities.getIn(["entities", "song", playlist.get(currentIndex)]);
|
|
|
|
let currentArtist = undefined;
|
|
|
|
if (currentSong) {
|
|
|
|
currentArtist = state.entities.getIn(["entities", "artist", currentSong.get("artist")]);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
isPlaying: state.webplayer.isPlaying,
|
|
|
|
isRandom: state.webplayer.isRandom,
|
|
|
|
isRepeat: state.webplayer.isRepeat,
|
|
|
|
isMute: state.webplayer.isMute,
|
|
|
|
volume: state.webplayer.volume,
|
|
|
|
currentIndex: currentIndex,
|
|
|
|
playlist: playlist,
|
|
|
|
currentSong: currentSong,
|
|
|
|
currentArtist: currentArtist,
|
|
|
|
};
|
|
|
|
};
|
2016-08-07 00:58:36 +02:00
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
2016-08-10 23:50:23 +02:00
|
|
|
actions: bindActionCreators(actionCreators, dispatch),
|
2016-08-07 00:58:36 +02:00
|
|
|
});
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(WebPlayer);
|