check for spotify auth on run, updated helper text

This commit is contained in:
aj 2019-08-08 00:18:41 +01:00
parent f1dcf6fdd6
commit 61ac85d41f
4 changed files with 237 additions and 96 deletions

View File

@ -9,7 +9,39 @@ class Index extends Component{
} }
render(){ render(){
return <h1 className="center-text text-no-select">welcome to playlist manager!</h1>; return (
<table className="app-table">
<thead>
<tr>
<th>
<h1 className="center-text text-no-select">playlist manager</h1>
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="center-text text-no-select ui-text" style={{fontSize: "20px"}}>
construct playlists from selections of other playlists
</td>
</tr>
<tr>
<td className="center-text text-no-select ui-text">
group sub-genre playlists
</td>
</tr>
<tr>
<td className="center-text text-no-select ui-text">
optionally append recommendations generated by spotify
</td>
</tr>
<tr>
<td className="center-text text-no-select ui-text">
<br></br>playlists are run multiple times a day
</td>
</tr>
</tbody>
</table>
);
} }
} }

View File

@ -10,53 +10,83 @@ class NewPlaylist extends Component {
super(props); super(props);
this.state = { this.state = {
name: '', name: '',
type: 'normal' type: 'default',
description: ''
} }
this.handleInputChange = this.handleInputChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
} }
componentDidMount(){
this.setDescription('default');
}
setDescription(value){
switch(value){
case 'default':
this.setState({
description: 'merge playlists as-is with deduplication by spotify id'
})
break;
case 'recents':
this.setState({
description: "select songs from playlists which have been added since a variable number of days"
})
break;
}
}
handleInputChange(event){ handleInputChange(event){
this.setState({ this.setState({
[event.target.name]: event.target.value [event.target.name]: event.target.value
}); });
this.setDescription(event.target.value);
} }
handleSubmit(event){ handleSubmit(event){
axios.get('/api/playlists') var name = this.state.name;
.then((response) => { this.setState({
name: ''
var names = response.data.playlists.map(entry => entry.name)
var sameName = names.includes(this.state.name);
if(sameName == false){
axios.put('/api/playlist', {
name: this.state.name,
parts: [],
playlist_references: [],
shuffle: false,
type: this.state.type,
}).then((response) => {
showMessage(`${this.state.name} created`);
}).catch((error) => {
showMessage(`error creating playlist (${error.response.status})`);
});
}else{
showMessage('named playlist already exists');
}
})
.catch((error) => {
showMessage(`error getting playlists (${error.response.status})`);
}); });
if(name.length != 0){
axios.get('/api/playlists')
.then((response) => {
var names = response.data.playlists.map(entry => entry.name)
var sameName = names.includes(this.state.name);
if(sameName == false){
axios.put('/api/playlist', {
name: name,
parts: [],
playlist_references: [],
shuffle: false,
type: this.state.type,
}).then((response) => {
showMessage(`${this.state.name} created`);
}).catch((error) => {
showMessage(`error creating playlist (${error.response.status})`);
});
}else{
showMessage('named playlist already exists');
}
})
.catch((error) => {
showMessage(`error getting playlists (${error.response.status})`);
});
}else{
showMessage('enter name');
}
} }
render(){ render(){
return ( return (
<table className="app-table"> <table className="app-table max-width">
<thead> <thead>
<tr> <tr>
<th colSpan="2"> <th colSpan="2">
<h1 className="ui-text center-text">new playlist</h1> <h1 className="ui-text center-text text-no-select">new playlist</h1>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -64,7 +94,7 @@ class NewPlaylist extends Component {
<tr> <tr>
<td> <td>
<select className="full-width" name="type" onChange={this.handleInputChange}> <select className="full-width" name="type" onChange={this.handleInputChange}>
<option value="default">normal</option> <option value="default">default</option>
<option value="recents">recents</option> <option value="recents">recents</option>
</select> </select>
</td> </td>
@ -83,6 +113,11 @@ class NewPlaylist extends Component {
<input type="submit" className="button full-width" onClick={this.handleSubmit} value="create" /> <input type="submit" className="button full-width" onClick={this.handleSubmit} value="create" />
</td> </td>
</tr> </tr>
<tr>
<td colSpan="2" className="ui-text text-no-select center-text">
<br></br>{this.state.description}
</td>
</tr>
</tbody> </tbody>
</table> </table>
); );

View File

@ -11,6 +11,7 @@ class PlaylistView extends Component{
name: this.props.match.params.name, name: this.props.match.params.name,
parts: [], parts: [],
playlists: [], playlists: [],
filteredPlaylists: [],
playlist_references: [], playlist_references: [],
type: null, type: null,
@ -50,8 +51,13 @@ class PlaylistView extends Component{
return 0; return 0;
}); });
var filteredPlaylists = playlists.data.playlists.filter((entry) => entry.name != this.state.name);
this.setState(info.data); this.setState(info.data);
this.setState({playlists: playlists.data.playlists}); this.setState({
playlists: playlists.data.playlists,
newPlaylistReference: filteredPlaylists.length > 0 ? filteredPlaylists[0].name : ''
});
})) }))
.catch((error) => { .catch((error) => {
showMessage(`error getting playlist info (${error.response.status})`); showMessage(`error getting playlist info (${error.response.status})`);
@ -67,6 +73,7 @@ class PlaylistView extends Component{
} }
handleInputChange(event){ handleInputChange(event){
this.setState({ this.setState({
[event.target.name]: event.target.value [event.target.name]: event.target.value
}); });
@ -122,60 +129,75 @@ class PlaylistView extends Component{
} }
handleAddPart(event){ handleAddPart(event){
if(this.state.newPlaylistName.length != 0){
var check = this.state.parts.filter((e) => { var check = this.state.parts.includes(this.state.newPlaylistName);
return e == event.target.value;
});
if(check.length == 0) { if(check == false) {
var parts = this.state.parts.slice(); var parts = this.state.parts.slice();
parts.push(this.state.newPlaylistName); parts.push(this.state.newPlaylistName);
parts.sort(function(a, b){ parts.sort(function(a, b){
if(a < b) { return -1; } if(a < b) { return -1; }
if(a > b) { return 1; } if(a > b) { return 1; }
return 0; return 0;
}); });
this.setState({ this.setState({
parts: parts, parts: parts,
newPlaylistName: '' newPlaylistName: ''
}); });
axios.post('/api/playlist', { axios.post('/api/playlist', {
name: this.state.name, name: this.state.name,
parts: parts parts: parts
}).catch((error) => { }).catch((error) => {
showMessage(`error adding part (${error.response.status})`); showMessage(`error adding part (${error.response.status})`);
}); });
}else{
showMessage('playlist already added');
}
}else{
showMessage('enter playlist name');
} }
} }
handleAddReference(event){ handleAddReference(event){
var check = this.state.playlist_references.filter((e) => {
return e == event.target.value;
});
if(check.length == 0) { if(this.state.newPlaylistReference.length != 0){
var playlist_references = this.state.playlist_references.slice();
playlist_references.push(this.state.newPlaylistReference);
playlist_references.sort(function(a, b){ var check = this.state.playlist_references.includes(this.state.newPlaylistReference);
if(a < b) { return -1; }
if(a > b) { return 1; } if(check == false) {
return 0; var playlist_references = this.state.playlist_references.slice();
}); playlist_references.push(this.state.newPlaylistReference);
this.setState({ playlist_references.sort(function(a, b){
playlist_references: playlist_references, if(a < b) { return -1; }
newPlaylistReference: '' if(a > b) { return 1; }
}); return 0;
axios.post('/api/playlist', { });
name: this.state.name,
playlist_references: playlist_references var filteredPlaylists = this.state.playlists.filter((entry) => entry.name != this.state.name);
}).catch((error) => {
showMessage(`error adding reference (${error.response.status})`); this.setState({
}); playlist_references: playlist_references,
newPlaylistReference: filteredPlaylists.length > 0 ? filteredPlaylists[0].name : ''
});
axios.post('/api/playlist', {
name: this.state.name,
playlist_references: playlist_references
}).catch((error) => {
showMessage(`error adding reference (${error.response.status})`);
});
}else{
showMessage('playlist already added');
}
}else{
showMessage('no other playlists to add');
} }
} }
@ -218,11 +240,20 @@ class PlaylistView extends Component{
} }
handleRun(event){ handleRun(event){
axios.get('/api/playlist/run', {params: {name: this.state.name}}) axios.get('/api/user')
.then((reponse) => { .then((response) => {
showMessage(`${this.state.name} ran`); if(response.data.spotify_linked == true){
}) axios.get('/api/playlist/run', {params: {name: this.state.name}})
.catch((error) => { .then((reponse) => {
showMessage(`${this.state.name} ran`);
})
.catch((error) => {
showMessage(`error running ${this.state.name} (${error.response.status})`);
});
}else{
showMessage(`link spotify before running`);
}
}).catch((error) => {
showMessage(`error running ${this.state.name} (${error.response.status})`); showMessage(`error running ${this.state.name} (${error.response.status})`);
}); });
} }
@ -239,6 +270,11 @@ class PlaylistView extends Component{
{ this.state.playlist_references.length > 0 && <ListBlock name="managed" handler={this.handleRemoveRefRow} list={this.state.playlist_references}/> } { this.state.playlist_references.length > 0 && <ListBlock name="managed" handler={this.handleRemoveRefRow} list={this.state.playlist_references}/> }
{ this.state.parts.length > 0 && <ListBlock name="spotify" handler={this.handleRemoveRow} list={this.state.parts}/> } { this.state.parts.length > 0 && <ListBlock name="spotify" handler={this.handleRemoveRow} list={this.state.parts}/> }
<tbody> <tbody>
<tr>
<td colSpan="2" className="center-text ui-text text-no-select" style={{fontStyle: "italic"}}>
<br></br>spotify playlist can be the name of either your own created playlist or one you follow, names are case sensitive
</td>
</tr>
<tr> <tr>
<td> <td>
<input type="text" <input type="text"
@ -246,7 +282,7 @@ class PlaylistView extends Component{
className="full-width" className="full-width"
value={this.state.newPlaylistName} value={this.state.newPlaylistName}
onChange={this.handleInputChange} onChange={this.handleInputChange}
placeholder="spotify playlist"></input> placeholder="spotify playlist name"></input>
</td> </td>
<td> <td>
<button className="button full-width" onClick={this.handleAddPart}>add</button> <button className="button full-width" onClick={this.handleAddPart}>add</button>
@ -258,7 +294,9 @@ class PlaylistView extends Component{
className="full-width" className="full-width"
value={this.state.newPlaylistReference} value={this.state.newPlaylistReference}
onChange={this.handleInputChange}> onChange={this.handleInputChange}>
{ this.state.playlists.map((entry) => <ReferenceEntry name={entry.name} key={entry.name} />) } { this.state.playlists
.filter((entry) => entry.name != this.state.name)
.map((entry) => <ReferenceEntry name={entry.name} key={entry.name} />) }
</select> </select>
</td> </td>
<td> <td>
@ -287,7 +325,7 @@ class PlaylistView extends Component{
</tr> </tr>
<tr> <tr>
<td className="center-text ui-text text-no-select"> <td className="center-text ui-text text-no-select">
recommendations sample size number of recommendations
</td> </td>
<td> <td>
<input type="number" <input type="number"
@ -298,7 +336,7 @@ class PlaylistView extends Component{
</td> </td>
</tr> </tr>
{ this.state.type == 'recents' && { this.state.type == 'recents' &&
<tr> <tr>
<td className="center-text ui-text text-no-select"> <td className="center-text ui-text text-no-select">
added since (days) added since (days)
</td> </td>
@ -309,7 +347,16 @@ class PlaylistView extends Component{
value={this.state.day_boundary} value={this.state.day_boundary}
onChange={this.handleInputChange}></input> onChange={this.handleInputChange}></input>
</td> </td>
</tr> </tr>
}
{ this.state.type == 'recents' &&
<tr>
<td colSpan="2" className="center-text ui-text text-no-select" style={{fontStyle: "italic"}}>
<br></br>'recents' playlists search for and include this months and last months playlists when named in the format
<br></br>[month] [year]
<br></br>e.g july 19 (lowercase)
</td>
</tr>
} }
<tr> <tr>
<td colSpan="2"> <td colSpan="2">

View File

@ -41,12 +41,21 @@ class PlaylistsView extends Component {
} }
handleRunPlaylist(name, event){ handleRunPlaylist(name, event){
axios.get('/api/playlist/run', {params: {name: name}}) axios.get('/api/user')
.then((response) => { .then((response) => {
showMessage(`${name} ran`); if(response.data.spotify_linked == true){
}) axios.get('/api/playlist/run', {params: {name: name}})
.catch((error) => { .then((response) => {
showMessage(`error running ${name} (${error.response.status})`); showMessage(`${name} ran`);
})
.catch((error) => {
showMessage(`error running ${name} (${error.response.status})`);
});
}else{
showMessage(`link spotify before running`);
}
}).catch((error) => {
showMessage(`error running ${this.state.name} (${error.response.status})`);
}); });
} }
@ -61,12 +70,21 @@ class PlaylistsView extends Component {
} }
handleRunAll(event){ handleRunAll(event){
axios.get('/api/playlist/run/user') axios.get('/api/user')
.then((response) => { .then((response) => {
showMessage("all playlists ran"); if(response.data.spotify_linked == true){
}) axios.get('/api/playlist/run/user')
.catch((error) => { .then((response) => {
showMessage(`error running all (${error.response.status})`); showMessage("all playlists ran");
})
.catch((error) => {
showMessage(`error running all (${error.response.status})`);
});
}else{
showMessage(`link spotify before running`);
}
}).catch((error) => {
showMessage(`error running ${this.state.name} (${error.response.status})`);
}); });
} }
@ -88,16 +106,25 @@ class PlaylistsView extends Component {
function Table(props){ function Table(props){
return ( return (
<table className="app-table max-width"> <table className="app-table max-width">
{ props.playlists.length == 0 ? (
<tbody>
<tr>
<td className="ui-text text-no-select center-text">
no playlists
</td>
</tr>
</tbody>
) : (
<tbody> <tbody>
{ props.playlists.map((playlist) => <Row playlist={ playlist } { props.playlists.map((playlist) => <Row playlist={ playlist }
handleRunPlaylist={props.handleRunPlaylist} handleRunPlaylist={props.handleRunPlaylist}
handleDeletePlaylist={props.handleDeletePlaylist} handleDeletePlaylist={props.handleDeletePlaylist}
key={ playlist.name }/>) } key={ playlist.name }/>) }
{ props.playlists.length > 0 &&
<tr> <tr>
<td colSpan="3"><button className="full-width button" onClick={props.handleRunAll}>run all</button></td> <td colSpan="3"><button className="full-width button" onClick={props.handleRunAll}>run all</button></td>
</tr> } </tr>
</tbody> </tbody>
)}
</table> </table>
); );
} }