Adding ReactHowler to handle playback
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -100,7 +100,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1584404587";
|
||||
var build = "1584604056";
|
||||
var version = "3.46.0";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
5
package.json
Executable file → Normal file
5
package.json
Executable file → Normal file
@ -18,11 +18,12 @@
|
||||
"react-dom": "^16.12.0",
|
||||
"react-fontawesome": "^1.7.1",
|
||||
"react-ga": "^2.7.0",
|
||||
"react-howler": "^3.7.4",
|
||||
"react-input-range": "1.3.0",
|
||||
"react-redux": "^6",
|
||||
"react-router": "^5",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-sortablejs": "^1.5.0",
|
||||
"react-router": "^5"
|
||||
"react-sortablejs": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
|
||||
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import ReactHowler from 'react-howler'
|
||||
import ReactGA from 'react-ga';
|
||||
|
||||
import Sidebar from './components/Sidebar';
|
||||
|
||||
@ -12,6 +12,7 @@ import Dater from './Dater';
|
||||
import LinksSentence from './LinksSentence';
|
||||
import Thumbnail from './Thumbnail';
|
||||
import Icon from './Icon';
|
||||
import Stream from './Stream';
|
||||
import { scrollTo } from '../util/helpers';
|
||||
import * as uiActions from '../services/ui/actions';
|
||||
import * as coreActions from '../services/core/actions';
|
||||
@ -40,64 +41,6 @@ class PlaybackControls extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
playStream(props = this.props) {
|
||||
if (!this.stream) {
|
||||
this.stream = new Audio();
|
||||
} else {
|
||||
this.stream.src = null;
|
||||
}
|
||||
|
||||
if (!props.http_streaming_enabled || !props.http_streaming_url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.stream.src = `${props.http_streaming_url}?cb=${props.http_streaming_cachebuster}`;
|
||||
this.stream.muted = props.http_streaming_mute;
|
||||
this.stream.volume = props.http_streaming_volume / 100;
|
||||
this.stream.play();
|
||||
|
||||
console.log(`Playing stream: ${this.stream.src}`);
|
||||
}
|
||||
|
||||
componentDidUpdate = ({
|
||||
http_streaming_cachebuster: prev_http_streaming_cachebuster,
|
||||
http_streaming_enabled: prev_http_streaming_enabled,
|
||||
http_streaming_mute: prev_http_streaming_mute,
|
||||
http_streaming_volume: prev_http_streaming_volume,
|
||||
}) => {
|
||||
const {
|
||||
http_streaming_cachebuster,
|
||||
http_streaming_enabled,
|
||||
http_streaming_mute,
|
||||
http_streaming_volume,
|
||||
} = this.props;
|
||||
|
||||
// Cachebuster changed
|
||||
// This happens when playback changes, so that the stream is "new", rather
|
||||
// than the original stream. This prevents the browser cache from starting
|
||||
// the stream right from the beginning (which could be hours of continuous playback).
|
||||
if (prev_http_streaming_cachebuster !== http_streaming_cachebuster) this.playStream();
|
||||
|
||||
// Just been enabled
|
||||
if (!prev_http_streaming_enabled && http_streaming_enabled) this.playStream();
|
||||
|
||||
if (this.stream) {
|
||||
if (http_streaming_mute !== prev_http_streaming_mute) {
|
||||
this.stream.muted = http_streaming_mute;
|
||||
}
|
||||
|
||||
// Just had volume changed
|
||||
if (http_streaming_volume !== prev_http_streaming_volume) {
|
||||
this.stream.volume = http_streaming_volume / 100;
|
||||
}
|
||||
|
||||
// Just been disabled
|
||||
if (!http_streaming_enabled) {
|
||||
this.stream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps({ current_track }, state) {
|
||||
return {
|
||||
...state,
|
||||
@ -246,6 +189,8 @@ class PlaybackControls extends React.Component {
|
||||
return (
|
||||
<div className={`playback-controls${expanded ? ' playback-controls--expanded' : ''}${touch_enabled ? ' playback-controls--touch-enabled' : ''}`}>
|
||||
|
||||
<Stream />
|
||||
|
||||
{next_track && next_track.images ? <Thumbnail className="hide" size="large" images={next_track.images} /> : null}
|
||||
|
||||
{this.state.transition_track && this.state.transition_direction ? (
|
||||
@ -332,13 +277,8 @@ class PlaybackControls extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
snapcast_enabled: state.pusher.config.snapcast_enabled,
|
||||
http_streaming_enabled: state.core.http_streaming_enabled,
|
||||
http_streaming_volume: state.core.http_streaming_volume >= 0 ? state.core.http_streaming_volume : 50,
|
||||
http_streaming_mute: state.core.http_streaming_mute,
|
||||
http_streaming_url: (state.core.http_streaming_url ? state.core.http_streaming_url : null),
|
||||
http_streaming_cachebuster: state.core.http_streaming_cachebuster,
|
||||
current_track: (state.core.current_track && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null),
|
||||
next_track: (state.core.next_track_uri && state.core.tracks[state.core.next_track_uri] !== undefined ? state.core.tracks[state.core.next_track_uri] : null),
|
||||
radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)),
|
||||
|
||||
65
src/js/components/Stream.js
Executable file
65
src/js/components/Stream.js
Executable file
@ -0,0 +1,65 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactHowler from 'react-howler';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as coreActions from '../services/core/actions';
|
||||
|
||||
class Stream extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
uri: '',
|
||||
play_state: '',
|
||||
cachebuster: '',
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps({ play_state, current_track: { uri } = {} }, state) {
|
||||
if (uri === state.uri && play_state === state.play_state) return null;
|
||||
return {
|
||||
...state,
|
||||
uri,
|
||||
play_state,
|
||||
cachebuster: `${Date.now()}`,
|
||||
};
|
||||
}
|
||||
|
||||
render = () => {
|
||||
const {
|
||||
play_state,
|
||||
enabled,
|
||||
volume,
|
||||
url,
|
||||
} = this.props;
|
||||
const { cachebuster } = this.state;
|
||||
|
||||
if (!enabled || !url) return null;
|
||||
|
||||
return (
|
||||
<ReactHowler
|
||||
src={`${url}?cb=${cachebuster}`}
|
||||
playing={play_state === 'playing'}
|
||||
volume={volume / 100}
|
||||
onLoad={() => console.debug(`Loaded stream ${url}?cb=${cachebuster}`)}
|
||||
html5
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
current_track: state.core.current_track || {},
|
||||
play_state: state.mopidy.play_state,
|
||||
enabled: state.core.http_streaming_enabled,
|
||||
volume: state.core.http_streaming_volume >= 0 ? state.core.http_streaming_volume : 50,
|
||||
url: (state.core.http_streaming_url ? state.core.http_streaming_url : null),
|
||||
cachebuster: state.core.http_streaming_cachebuster,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Stream);
|
||||
Reference in New Issue
Block a user