Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 68 additions & 18 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class App extends React.Component {
mounts: [],
remotes: [],
playing: null,
erroredStreams: [],

// Note: the crossOrigin is needed to fix a CORS JavaScript requirement

Expand Down Expand Up @@ -186,12 +187,8 @@ export default class App extends React.Component {
{
audioConfig
},
function() {
this.updateVolume();
}
this.updateVolume
);

return this;
}

fadeUp() {
Expand Down Expand Up @@ -252,26 +249,41 @@ export default class App extends React.Component {
}
}

// choose either high or low bitrate
bitrateFinder(streames, low = false) {
let arr = streames.sort(
(a, b) => parseFloat(a.bitrate) - parseFloat(b.bitrate)
);
if (low) return arr[0].url;
return arr[arr.length - 1].url;
}
sortStreams = (streams, lowBitrate = false) => {
return streams.sort((a, b) => {
if (lowBitrate) {
// sort by bitrate from low to high
if (parseFloat(a.bitrate) < parseFloat(b.bitrate)) return -1;
if (parseFloat(a.bitrate) > parseFloat(b.bitrate)) return 1;
} else {
// sort by bitrate, from high to low
if (parseFloat(a.bitrate) < parseFloat(b.bitrate)) return 1;
if (parseFloat(a.bitrate) > parseFloat(b.bitrate)) return -1;
}

// if both items have the same bitrate, sort by listeners from low to high
if (a.listeners.current < b.listeners.current) return -1;
if (a.listeners.current > b.listeners.current) return 1;
return 0;
});
};

getStreamUrl = (streams, lowBitrate) => {
const sorted = this.sortStreams(streams, lowBitrate);
return sorted[0].url;
};

// choose the stream based on the connection and availablity of relay(remotes)
setMountToConnection(mounts = [], remotes = []) {
let url = null;
if (this.state.fastConnection === false && remotes.length > 0) {
url = this.bitrateFinder(remotes, true);
url = this.getStreamUrl(remotes, true);
} else if (this.state.fastConnection && remotes.length > 0) {
url = this.bitrateFinder(remotes);
url = this.getStreamUrl(remotes);
} else if (this.state.fastConnection === false) {
url = this.bitrateFinder(mounts, true);
url = this.getStreamUrl(mounts, true);
} else {
url = this.bitrateFinder(mounts);
url = this.getStreamUrl(mounts);
}
this._player.src = url;
this.setState({
Expand Down Expand Up @@ -328,6 +340,40 @@ export default class App extends React.Component {
)
);

onPlayerError = () => {
/*
* This error handler works as follows:
* - When the player cannot play the url:
* - If the url is already in the `erroredStreams` list: try another url
* - If the url is not in `erroredStreams`: add the url to the list and try another url
* - If `erroredStreams` has as many items as the list of available streams:
* - Pause the player because this means all of our urls are having issues
*/

const { mounts, remotes, erroredStreams, url } = this.state;
const sortedStreams = this.sortStreams([...mounts, ...remotes]);

// Pause if all streams are in the errored list
if (erroredStreams.length === sortedStreams.length) {
this.pause();
return;
}

const availableStreams = sortedStreams.filter(stream => stream.url !== url);
const currentStream = sortedStreams.find(stream => stream.url === url);

// If the url is already in the errored list, use another url
if (erroredStreams.some(stream => stream.url === url)) {
this.setUrl(availableStreams[0].url);
} else {
// Otherwise, add the url to the errored list, then use another url
this.setState(
{ erroredStreams: [...erroredStreams, currentStream] },
() => this.setUrl(availableStreams[0].url)
);
}
};

render() {
return (
<GlobalHotKeys handlers={this.handlers} keyMap={this.keyMap}>
Expand All @@ -338,7 +384,11 @@ export default class App extends React.Component {
player={this._player}
playing={this.state.playing}
/>
<audio crossOrigin="anonymous" ref={a => (this._player = a)} />
<audio
crossOrigin="anonymous"
onError={this.onPlayerError}
ref={a => (this._player = a)}
/>
<Footer
currentSong={this.state.currentSong}
currentVolume={this.state.audioConfig.currentVolume}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export default class Footer extends React.Component {
mountOptions = (
<select
data-meta="stream-select"
defaultValue={this.props.url}
onChange={this.handleChange.bind(this)}
value={this.props.url}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is to make the dropdown renders the correct selected url.

>
{alternativeMounts.map((mount, index) => (
<option key={index} value={mount.url}>
Expand Down