added play scratch pad
This commit is contained in:
parent
57408efcda
commit
dcfd197d93
2
app.yaml
2
app.yaml
@ -9,3 +9,5 @@ handlers:
|
||||
script: auto
|
||||
secure: always
|
||||
|
||||
env_variables:
|
||||
DEPLOY_DESTINATION: 'PROD'
|
@ -1,5 +1,6 @@
|
||||
from flask import Blueprint, session, request, jsonify
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import json
|
||||
|
||||
@ -10,6 +11,7 @@ from google.protobuf import timestamp_pb2
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
from spotify.tasks.run_user_playlist import run_user_playlist as run_user_playlist
|
||||
from spotify.tasks.play_user_playlist import play_user_playlist as play_user_playlist
|
||||
|
||||
import spotify.api.database as database
|
||||
|
||||
@ -169,6 +171,9 @@ def playlist():
|
||||
if playlist_recommendation_sample is not None:
|
||||
dic['recommendation_sample'] = playlist_recommendation_sample
|
||||
|
||||
if playlist_type is not None:
|
||||
dic['type'] = playlist_type
|
||||
|
||||
if len(dic) == 0:
|
||||
return jsonify({"message": 'no changes to make', "status": "error"}), 400
|
||||
|
||||
@ -300,6 +305,76 @@ def change_password():
|
||||
return jsonify({'error': 'not logged in'}), 401
|
||||
|
||||
|
||||
@blueprint.route('/playlist/play', methods=['POST'])
|
||||
def play_playlist():
|
||||
|
||||
if 'username' in session:
|
||||
|
||||
request_json = request.get_json()
|
||||
|
||||
request_parts = request_json.get('parts', None)
|
||||
request_playlist_type = request_json.get('playlist_type', 'default')
|
||||
request_playlists = request_json.get('playlists', None)
|
||||
request_shuffle = request_json.get('shuffle', False)
|
||||
request_include_recommendations = request_json.get('include_recommendations', True)
|
||||
request_recommendation_sample = request_json.get('recommendation_sample', 10)
|
||||
request_day_boundary = request_json.get('day_boundary', 10)
|
||||
|
||||
if request_parts or request_playlists:
|
||||
if len(request_parts) > 0 or len(request_playlists) > 0:
|
||||
|
||||
if os.environ.get('DEPLOY_DESTINATION', None) and os.environ['DEPLOY_DESTINATION'] == 'PROD':
|
||||
create_play_user_playlist_task(session['username'],
|
||||
parts=request_parts,
|
||||
playlist_type=request_playlist_type,
|
||||
playlists=request_playlists,
|
||||
shuffle=request_shuffle,
|
||||
include_recommendations=request_include_recommendations,
|
||||
recommendation_sample=request_recommendation_sample,
|
||||
day_boundary=request_day_boundary)
|
||||
else:
|
||||
play_user_playlist(session['username'],
|
||||
parts=request_parts,
|
||||
playlist_type=request_playlist_type,
|
||||
playlists=request_playlists,
|
||||
shuffle=request_shuffle,
|
||||
include_recommendations=request_include_recommendations,
|
||||
recommendation_sample=request_recommendation_sample,
|
||||
day_boundary=request_day_boundary)
|
||||
|
||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
||||
|
||||
else:
|
||||
return jsonify({'error': 'insufficient playlist sources'}), 400
|
||||
|
||||
else:
|
||||
return jsonify({'error': 'insufficient playlist sources'}), 400
|
||||
|
||||
else:
|
||||
return jsonify({'error': 'not logged in'}), 401
|
||||
|
||||
|
||||
@blueprint.route('/playlist/play/task', methods=['POST'])
|
||||
def play_playlist_task():
|
||||
if request.headers.get('X-AppEngine-QueueName', None):
|
||||
payload = request.get_data(as_text=True)
|
||||
if payload:
|
||||
payload = json.loads(payload)
|
||||
|
||||
play_user_playlist(payload['username'],
|
||||
parts=payload['parts'],
|
||||
playlist_type=payload['playlist_type'],
|
||||
playlists=payload['playlists'],
|
||||
shuffle=payload['shuffle'],
|
||||
include_recommendations=payload['include_recommendations'],
|
||||
recommendation_sample=payload['recommendation_sample'],
|
||||
day_boundary=payload['day_boundary'])
|
||||
|
||||
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
||||
else:
|
||||
return jsonify({'error': 'unauthorized'}), 401
|
||||
|
||||
|
||||
@blueprint.route('/playlist/run', methods=['GET'])
|
||||
def run_playlist():
|
||||
|
||||
@ -309,6 +384,9 @@ def run_playlist():
|
||||
|
||||
if playlist_name:
|
||||
|
||||
if os.environ.get('DEPLOY_DESTINATION', None) and os.environ['DEPLOY_DESTINATION'] == 'PROD':
|
||||
create_run_user_playlist_task(session['username'], playlist_name)
|
||||
else:
|
||||
run_user_playlist(session['username'], playlist_name)
|
||||
|
||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
||||
@ -432,18 +510,30 @@ def execute_user(username):
|
||||
if len(iterate_playlist['parts']) > 0 or len(iterate_playlist['playlist_references']) > 0:
|
||||
if iterate_playlist.get('playlist_id', None):
|
||||
|
||||
if os.environ.get('DEPLOY_DESTINATION', None) and os.environ['DEPLOY_DESTINATION'] == 'PROD':
|
||||
create_run_user_playlist_task(username, iterate_playlist['name'], seconds_delay)
|
||||
else:
|
||||
run_playlist(username, iterate_playlist['name'])
|
||||
|
||||
seconds_delay += 6
|
||||
|
||||
|
||||
def create_run_user_playlist_task(username, playlist_name, delay=0):
|
||||
|
||||
task = {
|
||||
'app_engine_http_request': { # Specify the type of request.
|
||||
'http_method': 'POST',
|
||||
'relative_uri': '/api/playlist/run/task',
|
||||
'body': json.dumps({
|
||||
'username': username,
|
||||
'name': iterate_playlist['name']
|
||||
'name': playlist_name
|
||||
}).encode()
|
||||
}
|
||||
}
|
||||
|
||||
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds_delay)
|
||||
if delay > 0:
|
||||
|
||||
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay)
|
||||
|
||||
# Create Timestamp protobuf.
|
||||
timestamp = timestamp_pb2.Timestamp()
|
||||
@ -454,9 +544,44 @@ def execute_user(username):
|
||||
|
||||
tasker.create_task(task_path, task)
|
||||
|
||||
seconds_delay += 10
|
||||
|
||||
# execute_playlist(username, iterate_playlist['name'])
|
||||
def create_play_user_playlist_task(username,
|
||||
parts=None,
|
||||
playlist_type='default',
|
||||
playlists=None,
|
||||
shuffle=False,
|
||||
include_recommendations=False,
|
||||
recommendation_sample=10,
|
||||
day_boundary=10,
|
||||
delay=0):
|
||||
task = {
|
||||
'app_engine_http_request': { # Specify the type of request.
|
||||
'http_method': 'POST',
|
||||
'relative_uri': '/api/playlist/play/task',
|
||||
'body': json.dumps({
|
||||
'username': username,
|
||||
'playlist_type': playlist_type,
|
||||
'parts': parts,
|
||||
'playlists': playlists,
|
||||
'shuffle': shuffle,
|
||||
'include_recommendations': include_recommendations,
|
||||
'recommendation_sample': recommendation_sample,
|
||||
'day_boundary': day_boundary
|
||||
}).encode()
|
||||
}
|
||||
}
|
||||
|
||||
if delay > 0:
|
||||
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay)
|
||||
|
||||
# Create Timestamp protobuf.
|
||||
timestamp = timestamp_pb2.Timestamp()
|
||||
timestamp.FromDatetime(d)
|
||||
|
||||
# Add the timestamp to the tasks.
|
||||
task['schedule_time'] = timestamp
|
||||
|
||||
tasker.create_task(task_path, task)
|
||||
|
||||
|
||||
def push_run_user_playlist_message(username, name):
|
||||
|
@ -127,7 +127,7 @@ def auth():
|
||||
{
|
||||
'client_id': client_id,
|
||||
'response_type': 'code',
|
||||
'scope': 'playlist-modify-public playlist-modify-private playlist-read-private',
|
||||
'scope': 'playlist-modify-public playlist-modify-private playlist-read-private user-modify-playback-state',
|
||||
'redirect_uri': 'https://spotify.sarsoo.xyz/auth/spotify/token'
|
||||
}
|
||||
)
|
||||
|
@ -19,6 +19,7 @@ logger.setLevel('INFO')
|
||||
log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s'
|
||||
formatter = logging.Formatter(log_format)
|
||||
|
||||
if os.environ.get('DEPLOY_DESTINATION', None) and os.environ['DEPLOY_DESTINATION'] == 'PROD':
|
||||
client = glogging.Client()
|
||||
handler = CloudLoggingHandler(client)
|
||||
|
||||
@ -26,6 +27,12 @@ handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(handler)
|
||||
|
||||
else:
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), '..', 'build'), template_folder="templates")
|
||||
app.secret_key = db.collection(u'spotify').document(u'config').get().to_dict()['secret_key']
|
||||
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
||||
|
111
spotify/tasks/play_user_playlist.py
Normal file
111
spotify/tasks/play_user_playlist.py
Normal file
@ -0,0 +1,111 @@
|
||||
from google.cloud import firestore
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from spotframework.engine.playlistengine import PlaylistEngine
|
||||
from spotframework.engine.filter.shuffle import Shuffle
|
||||
from spotframework.engine.filter.sortreversereleasedate import SortReverseReleaseDate
|
||||
from spotframework.engine.filter.deduplicatebyid import DeduplicateByID
|
||||
|
||||
from spotframework.net.network import Network
|
||||
from spotframework.net.user import User
|
||||
|
||||
db = firestore.Client()
|
||||
|
||||
captured_playlists = []
|
||||
|
||||
|
||||
def play_user_playlist(username,
|
||||
playlist_type='default',
|
||||
parts=None,
|
||||
playlists=None,
|
||||
shuffle=False,
|
||||
include_recommendations=True,
|
||||
recommendation_sample=10,
|
||||
day_boundary=10):
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
users = [i for i in db.collection(u'spotify_users').where(u'username', u'==', username).stream()]
|
||||
|
||||
logger.info(f'{username}')
|
||||
|
||||
if len(users) == 1:
|
||||
|
||||
user_dict = users[0].to_dict()
|
||||
|
||||
if not parts and not playlists:
|
||||
logger.critical(f'no playlists to use for creation ({username})')
|
||||
return
|
||||
|
||||
if len(parts) == 0 and len(playlists) == 0:
|
||||
logger.critical(f'no playlists to use for creation ({username})')
|
||||
return
|
||||
|
||||
spotify_keys = db.document('key/spotify').get().to_dict()
|
||||
|
||||
net = Network(User(spotify_keys['clientid'],
|
||||
spotify_keys['clientsecret'],
|
||||
user_dict['access_token'],
|
||||
user_dict['refresh_token']))
|
||||
|
||||
engine = PlaylistEngine(net)
|
||||
engine.load_user_playlists()
|
||||
|
||||
processors = [DeduplicateByID()]
|
||||
|
||||
if shuffle:
|
||||
processors.append(Shuffle())
|
||||
else:
|
||||
processors.append(SortReverseReleaseDate())
|
||||
|
||||
global captured_playlists
|
||||
captured_playlists = []
|
||||
|
||||
if not parts:
|
||||
parts = []
|
||||
|
||||
submit_parts = parts
|
||||
|
||||
for part in playlists:
|
||||
submit_parts += generate_parts(users[0].id, part)
|
||||
|
||||
submit_parts = [i for i in {j for j in submit_parts}]
|
||||
|
||||
if playlist_type == 'recents':
|
||||
boundary_date = datetime.datetime.now() - datetime.timedelta(days=int(day_boundary))
|
||||
tracks = engine.get_recent_playlist(boundary_date,
|
||||
submit_parts,
|
||||
processors,
|
||||
include_recommendations=include_recommendations,
|
||||
recommendation_limit=int(recommendation_sample))
|
||||
else:
|
||||
tracks = engine.make_playlist(submit_parts,
|
||||
processors,
|
||||
include_recommendations=include_recommendations,
|
||||
recommendation_limit=int(recommendation_sample))
|
||||
|
||||
net.play(uris=[i['uri'] for i in tracks])
|
||||
|
||||
else:
|
||||
logger.critical(f'multiple/no user(s) found ({username})')
|
||||
return
|
||||
|
||||
|
||||
def generate_parts(user_id, name):
|
||||
|
||||
playlist_doc = [i.to_dict() for i in
|
||||
db.document(u'spotify_users/{}'.format(user_id))
|
||||
.collection(u'playlists')
|
||||
.where(u'name', '==', name).stream()][0]
|
||||
|
||||
return_parts = playlist_doc['parts']
|
||||
|
||||
captured_playlists.append(name)
|
||||
|
||||
for i in playlist_doc['playlist_references']:
|
||||
if i not in captured_playlists:
|
||||
return_parts += generate_parts(user_id, i)
|
||||
|
||||
return return_parts
|
@ -13,7 +13,7 @@ class PlaylistView extends Component{
|
||||
playlists: [],
|
||||
filteredPlaylists: [],
|
||||
playlist_references: [],
|
||||
type: null,
|
||||
type: 'default',
|
||||
|
||||
day_boundary: '',
|
||||
recommendation_sample: '',
|
||||
@ -84,6 +84,9 @@ class PlaylistView extends Component{
|
||||
if(event.target.name == 'recommendation_sample'){
|
||||
this.handleRecSampleChange(event.target.value);
|
||||
}
|
||||
if(event.target.name == 'type'){
|
||||
this.handleTypeChange(event.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleDayBoundaryChange(boundary) {
|
||||
@ -104,6 +107,15 @@ class PlaylistView extends Component{
|
||||
});
|
||||
}
|
||||
|
||||
handleTypeChange(sample){
|
||||
axios.post('/api/playlist', {
|
||||
name: this.state.name,
|
||||
type: sample
|
||||
}).catch((error) => {
|
||||
showMessage(`error updating type (${error.response.status})`);
|
||||
});
|
||||
}
|
||||
|
||||
handleShuffleChange(event) {
|
||||
this.setState({
|
||||
shuffle: event.target.checked
|
||||
@ -240,6 +252,7 @@ class PlaylistView extends Component{
|
||||
}
|
||||
|
||||
handleRun(event){
|
||||
if(this.state.playlist_references.length > 0 || this.state.parts.length > 0){
|
||||
axios.get('/api/user')
|
||||
.then((response) => {
|
||||
if(response.data.spotify_linked == true){
|
||||
@ -256,6 +269,9 @@ class PlaylistView extends Component{
|
||||
}).catch((error) => {
|
||||
showMessage(`error running ${this.state.name} (${error.response.status})`);
|
||||
});
|
||||
}else{
|
||||
showMessage(`add either playlists or parts`);
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
@ -349,6 +365,20 @@ class PlaylistView extends Component{
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
playlist type
|
||||
</td>
|
||||
<td>
|
||||
<select className="full-width"
|
||||
name="type"
|
||||
onChange={this.handleInputChange}
|
||||
value={this.state.type}>
|
||||
<option value="default">default</option>
|
||||
<option value="recents">recents</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
{ this.state.type == 'recents' &&
|
||||
<tr>
|
||||
<td colSpan="2" className="center-text ui-text text-no-select" style={{fontStyle: "italic"}}>
|
||||
|
@ -4,6 +4,7 @@ const axios = require('axios');
|
||||
|
||||
import PlaylistsView from "./PlaylistsView.js"
|
||||
import NewPlaylist from "./NewPlaylist.js";
|
||||
import ScratchView from "./ScratchView.js";
|
||||
|
||||
class Playlists extends Component {
|
||||
render(){
|
||||
@ -11,11 +12,13 @@ class Playlists extends Component {
|
||||
<div>
|
||||
<ul className="navbar" style={{width: "100%"}}>
|
||||
<li><Link to={`${this.props.match.url}/new`}>new</Link></li>
|
||||
<li><Link to={`${this.props.match.url}/play`}>play</Link></li>
|
||||
</ul>
|
||||
|
||||
<Switch>
|
||||
<Route exact path={`${this.props.match.url}/`} component={PlaylistsView} />
|
||||
<Route path={`${this.props.match.url}/new`} component={NewPlaylist} />
|
||||
<Route path={`${this.props.match.url}/play`} component={ScratchView} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
|
354
src/js/Playlist/ScratchView.js
Normal file
354
src/js/Playlist/ScratchView.js
Normal file
@ -0,0 +1,354 @@
|
||||
import React, { Component } from "react";
|
||||
const axios = require('axios');
|
||||
|
||||
import showMessage from "../Toast.js"
|
||||
|
||||
class ScratchView extends Component{
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'play',
|
||||
parts: [],
|
||||
playlists: [],
|
||||
filteredPlaylists: [],
|
||||
playlist_references: [],
|
||||
type: 'default',
|
||||
|
||||
day_boundary: 5,
|
||||
recommendation_sample: 5,
|
||||
newPlaylistName: '',
|
||||
newPlaylistReference: '',
|
||||
|
||||
shuffle: false,
|
||||
include_recommendations: false
|
||||
}
|
||||
this.handleAddPart = this.handleAddPart.bind(this);
|
||||
this.handleAddReference = this.handleAddReference.bind(this);
|
||||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
this.handleRemoveRow = this.handleRemoveRow.bind(this);
|
||||
this.handleRemoveRefRow = this.handleRemoveRefRow.bind(this);
|
||||
|
||||
this.handleRun = this.handleRun.bind(this);
|
||||
|
||||
this.handleShuffleChange = this.handleShuffleChange.bind(this);
|
||||
this.handleRecChange = this.handleRecChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
|
||||
this.getPlaylists();
|
||||
}
|
||||
|
||||
|
||||
getPlaylists(){
|
||||
return axios.get(`/api/playlists`)
|
||||
.then((response) => {
|
||||
var filteredPlaylists = response.data.playlists.filter((entry) => entry.name != this.state.name);
|
||||
|
||||
this.setState({
|
||||
playlists: response.data.playlists,
|
||||
newPlaylistReference: filteredPlaylists.length > 0 ? filteredPlaylists[0].name : ''
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
showMessage(`error getting playlists (${error.response.status})`);
|
||||
});
|
||||
}
|
||||
|
||||
handleInputChange(event){
|
||||
this.setState({
|
||||
[event.target.name]: event.target.value
|
||||
});
|
||||
}
|
||||
|
||||
handleTypeChange(sample){
|
||||
axios.post('/api/playlist', {
|
||||
name: this.state.name,
|
||||
type: sample
|
||||
}).catch((error) => {
|
||||
showMessage(`error updating type (${error.response.status})`);
|
||||
});
|
||||
}
|
||||
|
||||
handleShuffleChange(event) {
|
||||
this.setState({
|
||||
shuffle: event.target.checked
|
||||
});
|
||||
}
|
||||
|
||||
handleRecChange(event) {
|
||||
this.setState({
|
||||
include_recommendations: event.target.checked
|
||||
});
|
||||
}
|
||||
|
||||
handleAddPart(event){
|
||||
|
||||
if(this.state.newPlaylistName.length != 0){
|
||||
|
||||
var check = this.state.parts.includes(this.state.newPlaylistName);
|
||||
|
||||
if(check == false) {
|
||||
var parts = this.state.parts.slice();
|
||||
parts.push(this.state.newPlaylistName);
|
||||
|
||||
parts.sort(function(a, b){
|
||||
if(a < b) { return -1; }
|
||||
if(a > b) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
parts: parts,
|
||||
newPlaylistName: ''
|
||||
});
|
||||
}else{
|
||||
showMessage('playlist already added');
|
||||
}
|
||||
|
||||
}else{
|
||||
showMessage('enter playlist name');
|
||||
}
|
||||
}
|
||||
|
||||
handleAddReference(event){
|
||||
|
||||
if(this.state.newPlaylistReference.length != 0){
|
||||
|
||||
var check = this.state.playlist_references.includes(this.state.newPlaylistReference);
|
||||
|
||||
if(check == false) {
|
||||
var playlist_references = this.state.playlist_references.slice();
|
||||
playlist_references.push(this.state.newPlaylistReference);
|
||||
|
||||
playlist_references.sort(function(a, b){
|
||||
if(a < b) { return -1; }
|
||||
if(a > b) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
var filteredPlaylists = this.state.playlists.filter((entry) => entry.name != this.state.name);
|
||||
|
||||
this.setState({
|
||||
playlist_references: playlist_references,
|
||||
newPlaylistReference: filteredPlaylists.length > 0 ? filteredPlaylists[0].name : ''
|
||||
});
|
||||
|
||||
}else{
|
||||
showMessage('playlist already added');
|
||||
}
|
||||
|
||||
}else{
|
||||
showMessage('no other playlists to add');
|
||||
}
|
||||
}
|
||||
|
||||
handleRemoveRow(id, event){
|
||||
var parts = this.state.parts;
|
||||
parts = parts.filter(e => e !== id);
|
||||
this.setState({
|
||||
parts: parts
|
||||
});
|
||||
|
||||
if(parts.length == 0) {
|
||||
parts = -1;
|
||||
}
|
||||
}
|
||||
|
||||
handleRemoveRefRow(id, event){
|
||||
var playlist_references = this.state.playlist_references;
|
||||
playlist_references = playlist_references.filter(e => e !== id);
|
||||
this.setState({
|
||||
playlist_references: playlist_references
|
||||
});
|
||||
}
|
||||
|
||||
handleRun(event){
|
||||
if(this.state.playlist_references.length > 0 || this.state.parts.length > 0){
|
||||
axios.get('/api/user')
|
||||
.then((response) => {
|
||||
if(response.data.spotify_linked == true){
|
||||
axios.post('/api/playlist/play', {
|
||||
parts: this.state.parts,
|
||||
playlists: this.state.playlist_references,
|
||||
shuffle: this.state.shuffle,
|
||||
include_recommendations: this.state.include_recommendations,
|
||||
recommendation_sample: this.state.recommendation_sample,
|
||||
day_boundary: this.state.day_boundary,
|
||||
playlist_type: this.state.type
|
||||
})
|
||||
.then((reponse) => {
|
||||
showMessage(`played`);
|
||||
})
|
||||
.catch((error) => {
|
||||
showMessage(`error playing (${error.response.status})`);
|
||||
});
|
||||
}else{
|
||||
showMessage(`link spotify before running`);
|
||||
}
|
||||
}).catch((error) => {
|
||||
showMessage(`error playing (${error.response.status})`);
|
||||
});
|
||||
}else{
|
||||
showMessage(`add either playlists or parts`);
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
const table = (
|
||||
<table className="app-table max-width">
|
||||
{/* <thead>
|
||||
<tr>
|
||||
<th colSpan="2"><h1 className="text-no-select">{ this.state.name }</h1></th>
|
||||
</tr>
|
||||
</thead> */}
|
||||
{ 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}/> }
|
||||
<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>
|
||||
<td>
|
||||
<input type="text"
|
||||
name="newPlaylistName"
|
||||
className="full-width"
|
||||
value={this.state.newPlaylistName}
|
||||
onChange={this.handleInputChange}
|
||||
placeholder="spotify playlist name"></input>
|
||||
</td>
|
||||
<td>
|
||||
<button className="button full-width" onClick={this.handleAddPart}>add</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<select name="newPlaylistReference"
|
||||
className="full-width"
|
||||
value={this.state.newPlaylistReference}
|
||||
onChange={this.handleInputChange}>
|
||||
{ this.state.playlists
|
||||
.filter((entry) => entry.name != this.state.name)
|
||||
.map((entry) => <ReferenceEntry name={entry.name} key={entry.name} />) }
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button className="button full-width" onClick={this.handleAddReference}>add</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
shuffle output?
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox"
|
||||
name="shuffle"
|
||||
checked={this.state.shuffle}
|
||||
onChange={this.handleShuffleChange}></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
include recommendations?
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox"
|
||||
name="include_recommendations"
|
||||
checked={this.state.include_recommendations}
|
||||
onChange={this.handleRecChange}></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
number of recommendations
|
||||
</td>
|
||||
<td>
|
||||
<input type="number"
|
||||
name="recommendation_sample"
|
||||
className="full-width"
|
||||
value={this.state.recommendation_sample}
|
||||
onChange={this.handleInputChange}></input>
|
||||
</td>
|
||||
</tr>
|
||||
{ this.state.type == 'recents' &&
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
added since (days)
|
||||
</td>
|
||||
<td>
|
||||
<input type="number"
|
||||
name="day_boundary"
|
||||
className="full-width"
|
||||
value={this.state.day_boundary}
|
||||
onChange={this.handleInputChange}></input>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td className="center-text ui-text text-no-select">
|
||||
playlist type
|
||||
</td>
|
||||
<td>
|
||||
<select className="full-width"
|
||||
name="type"
|
||||
onChange={this.handleInputChange}
|
||||
value={this.state.type}>
|
||||
<option value="default">default</option>
|
||||
<option value="recents">recents</option>
|
||||
</select>
|
||||
</td>
|
||||
</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>
|
||||
<td colSpan="2">
|
||||
<button className="button full-width" onClick={this.handleRun}>play</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
const error = <p style={{textAlign: "center"}}>{ this.state.error_text }</p>;
|
||||
|
||||
return this.state.error ? error : table;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function ReferenceEntry(props) {
|
||||
return (
|
||||
<option value={props.name}>{props.name}</option>
|
||||
);
|
||||
}
|
||||
|
||||
function ListBlock(props) {
|
||||
return (
|
||||
<tbody>
|
||||
<tr><td colSpan="2" className="ui-text center-text text-no-select" style={{fontStyle: 'italic'}}>{props.name}</td></tr>
|
||||
{ props.list.map((part) => <Row part={ part } key={ part } handler={props.handler}/>) }
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
|
||||
function Row (props) {
|
||||
return (
|
||||
<tr>
|
||||
<td className="ui-text center-text text-no-select">{ props.part }</td>
|
||||
<td><button className="ui-text center-text button full-width" onClick={(e) => props.handler(props.part, e)}>remove</button></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScratchView;
|
Loading…
Reference in New Issue
Block a user