parent
e3fd4bb6d5
commit
3c248fd728
@ -8,10 +8,8 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from music.api.decorators import login_required, login_or_basic_auth, admin_required, gae_cron, cloud_task
|
from music.api.decorators import login_required, login_or_basic_auth, admin_required, gae_cron, cloud_task
|
||||||
from music.cloud.tasks import execute_all_user_playlists, execute_user_playlists, create_run_user_playlist_task, \
|
from music.cloud.tasks import update_all_user_playlists, update_playlists, run_user_playlist_task
|
||||||
create_play_user_playlist_task
|
|
||||||
from music.tasks.run_user_playlist import run_user_playlist as run_user_playlist
|
from music.tasks.run_user_playlist import run_user_playlist as run_user_playlist
|
||||||
from music.tasks.play_user_playlist import play_user_playlist as play_user_playlist
|
|
||||||
|
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.playlist import Playlist
|
from music.model.playlist import Playlist
|
||||||
@ -25,7 +23,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@blueprint.route('/playlists', methods=['GET'])
|
@blueprint.route('/playlists', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def get_playlists(user=None):
|
def all_playlists_route(user=None):
|
||||||
assert user is not None
|
assert user is not None
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'playlists': [i.to_dict() for i in Playlist.collection.parent(user.key).fetch()]
|
'playlists': [i.to_dict() for i in Playlist.collection.parent(user.key).fetch()]
|
||||||
@ -188,8 +186,9 @@ def playlist_route(user=None):
|
|||||||
updating_playlist.chart_limit = playlist_chart_limit
|
updating_playlist.chart_limit = playlist_chart_limit
|
||||||
|
|
||||||
if playlist_type is not None:
|
if playlist_type is not None:
|
||||||
# TODO check acceptable value
|
playlist_type = playlist_type.strip().lower()
|
||||||
updating_playlist.type = playlist_type
|
if playlist_type in ['default', 'recents', 'fmchart']:
|
||||||
|
updating_playlist.type = playlist_type
|
||||||
|
|
||||||
updating_playlist.update()
|
updating_playlist.update()
|
||||||
logger.info(f'updated {user.username} / {playlist_name}')
|
logger.info(f'updated {user.username} / {playlist_name}')
|
||||||
@ -241,7 +240,7 @@ def user_route(user=None):
|
|||||||
@blueprint.route('/users', methods=['GET'])
|
@blueprint.route('/users', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@admin_required
|
@admin_required
|
||||||
def users(user=None):
|
def all_users_route(user=None):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'accounts': [i.to_dict() for i in User.collection.fetch()]
|
'accounts': [i.to_dict() for i in User.collection.fetch()]
|
||||||
}), 200
|
}), 200
|
||||||
@ -274,82 +273,6 @@ def change_password(user=None):
|
|||||||
return jsonify({'error': 'malformed request, no old_password/new_password'}), 400
|
return jsonify({'error': 'malformed request, no old_password/new_password'}), 400
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist/play', methods=['POST'])
|
|
||||||
@login_or_basic_auth
|
|
||||||
def play_playlist(user=None):
|
|
||||||
|
|
||||||
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)
|
|
||||||
request_add_this_month = request_json.get('add_this_month', False)
|
|
||||||
request_add_last_month = request_json.get('add_last_month', False)
|
|
||||||
|
|
||||||
request_device_name = request_json.get('device_name', None)
|
|
||||||
|
|
||||||
logger.info(f'playing {user.username}')
|
|
||||||
|
|
||||||
if (request_parts and len(request_parts) > 0) or (request_playlists and len(request_playlists) > 0):
|
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
|
||||||
create_play_user_playlist_task(user.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,
|
|
||||||
add_this_month=request_add_this_month,
|
|
||||||
add_last_month=request_add_last_month,
|
|
||||||
device_name=request_device_name)
|
|
||||||
else:
|
|
||||||
play_user_playlist(user.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,
|
|
||||||
add_this_month=request_add_this_month,
|
|
||||||
add_last_month=request_add_last_month,
|
|
||||||
device_name=request_device_name)
|
|
||||||
|
|
||||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
|
||||||
else:
|
|
||||||
logger.error(f'no playlists/parts {user.username}')
|
|
||||||
return jsonify({'error': 'insufficient playlist sources'}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist/play/task', methods=['POST'])
|
|
||||||
@cloud_task
|
|
||||||
def play_playlist_task():
|
|
||||||
payload = request.get_data(as_text=True)
|
|
||||||
if payload:
|
|
||||||
payload = json.loads(payload)
|
|
||||||
logger.info(f'playing {payload["username"]}')
|
|
||||||
|
|
||||||
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'],
|
|
||||||
add_this_month=payload['add_this_month'],
|
|
||||||
add_last_month=payload['add_last_month'],
|
|
||||||
device_name=payload['device_name'])
|
|
||||||
|
|
||||||
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist/run', methods=['GET'])
|
@blueprint.route('/playlist/run', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def run_playlist(user=None):
|
def run_playlist(user=None):
|
||||||
@ -359,7 +282,7 @@ def run_playlist(user=None):
|
|||||||
if playlist_name:
|
if playlist_name:
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_run_user_playlist_task(user.username, playlist_name)
|
run_user_playlist_task(user.username, playlist_name)
|
||||||
else:
|
else:
|
||||||
run_user_playlist(user.username, playlist_name)
|
run_user_playlist(user.username, playlist_name)
|
||||||
|
|
||||||
@ -394,7 +317,7 @@ def run_user(user=None):
|
|||||||
else:
|
else:
|
||||||
user_name = user.username
|
user_name = user.username
|
||||||
|
|
||||||
execute_user_playlists(user_name)
|
update_playlists(user_name)
|
||||||
|
|
||||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||||
|
|
||||||
@ -405,7 +328,7 @@ def run_user_task():
|
|||||||
|
|
||||||
payload = request.get_data(as_text=True)
|
payload = request.get_data(as_text=True)
|
||||||
if payload:
|
if payload:
|
||||||
execute_user_playlists(payload)
|
update_playlists(payload)
|
||||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
@ -414,7 +337,7 @@ def run_user_task():
|
|||||||
@admin_required
|
@admin_required
|
||||||
def run_users(user=None):
|
def run_users(user=None):
|
||||||
|
|
||||||
execute_all_user_playlists()
|
update_all_user_playlists()
|
||||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
@ -422,7 +345,7 @@ def run_users(user=None):
|
|||||||
@gae_cron
|
@gae_cron
|
||||||
def run_users_cron():
|
def run_users_cron():
|
||||||
|
|
||||||
execute_all_user_playlists()
|
update_all_user_playlists()
|
||||||
return jsonify({'status': 'success'}), 200
|
return jsonify({'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
|
|
||||||
from music.api.decorators import admin_required, login_or_basic_auth, lastfm_username_required, spotify_link_required, cloud_task, gae_cron
|
from music.api.decorators import admin_required, login_or_basic_auth, lastfm_username_required, spotify_link_required, cloud_task, gae_cron
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
from music.cloud.tasks import execute_all_user_playlist_stats, execute_user_playlist_stats, create_refresh_playlist_task
|
from music.cloud.tasks import refresh_all_user_playlist_stats, refresh_user_playlist_stats, refresh_playlist_task
|
||||||
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats, \
|
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats, \
|
||||||
refresh_lastfm_album_stats, \
|
refresh_lastfm_album_stats, \
|
||||||
refresh_lastfm_artist_stats
|
refresh_lastfm_artist_stats
|
||||||
@ -74,7 +74,7 @@ def playlist_refresh(user=None):
|
|||||||
if playlist_name:
|
if playlist_name:
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_refresh_playlist_task(user.username, playlist_name)
|
refresh_playlist_task(user.username, playlist_name)
|
||||||
else:
|
else:
|
||||||
refresh_lastfm_track_stats(user.username, playlist_name)
|
refresh_lastfm_track_stats(user.username, playlist_name)
|
||||||
refresh_lastfm_album_stats(user.username, playlist_name)
|
refresh_lastfm_album_stats(user.username, playlist_name)
|
||||||
@ -136,14 +136,14 @@ def run_playlist_artist_task():
|
|||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@admin_required
|
@admin_required
|
||||||
def run_users(user=None):
|
def run_users(user=None):
|
||||||
execute_all_user_playlist_stats()
|
refresh_all_user_playlist_stats()
|
||||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist/refresh/users/cron', methods=['GET'])
|
@blueprint.route('/playlist/refresh/users/cron', methods=['GET'])
|
||||||
@gae_cron
|
@gae_cron
|
||||||
def run_users_task():
|
def run_users_task():
|
||||||
execute_all_user_playlist_stats()
|
refresh_all_user_playlist_stats()
|
||||||
return jsonify({'status': 'success'}), 200
|
return jsonify({'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ def run_user(user=None):
|
|||||||
else:
|
else:
|
||||||
user_name = user.username
|
user_name = user.username
|
||||||
|
|
||||||
execute_user_playlist_stats(user_name)
|
refresh_user_playlist_stats(user_name)
|
||||||
|
|
||||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||||
|
|
||||||
@ -167,5 +167,5 @@ def run_user_task():
|
|||||||
|
|
||||||
payload = request.get_data(as_text=True)
|
payload = request.get_data(as_text=True)
|
||||||
if payload:
|
if payload:
|
||||||
execute_user_playlist_stats(payload)
|
refresh_user_playlist_stats(payload)
|
||||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||||
|
@ -2,9 +2,11 @@ from flask import Blueprint, jsonify, request
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
from music.api.decorators import login_or_basic_auth
|
from music.api.decorators import login_or_basic_auth, gae_cron, cloud_task
|
||||||
from music.cloud.function import update_tag as serverless_update_tag
|
from music.cloud.function import update_tag as serverless_update_tag
|
||||||
|
from music.cloud.tasks import update_all_user_tags
|
||||||
from music.tasks.update_tag import update_tag
|
from music.tasks.update_tag import update_tag
|
||||||
|
|
||||||
from music.model.tag import Tag
|
from music.model.tag import Tag
|
||||||
@ -60,10 +62,7 @@ def put_tag(tag_id, user):
|
|||||||
if request_json.get('name'):
|
if request_json.get('name'):
|
||||||
db_tag.name = request_json['name'].strip()
|
db_tag.name = request_json['name'].strip()
|
||||||
|
|
||||||
update_required = False
|
|
||||||
|
|
||||||
if request_json.get('tracks') is not None:
|
if request_json.get('tracks') is not None:
|
||||||
update_required = True
|
|
||||||
db_tag.tracks = [
|
db_tag.tracks = [
|
||||||
{
|
{
|
||||||
'name': track['name'].strip(),
|
'name': track['name'].strip(),
|
||||||
@ -74,7 +73,6 @@ def put_tag(tag_id, user):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if request_json.get('albums') is not None:
|
if request_json.get('albums') is not None:
|
||||||
update_required = True
|
|
||||||
db_tag.albums = [
|
db_tag.albums = [
|
||||||
{
|
{
|
||||||
'name': album['name'].strip(),
|
'name': album['name'].strip(),
|
||||||
@ -85,7 +83,6 @@ def put_tag(tag_id, user):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if request_json.get('artists') is not None:
|
if request_json.get('artists') is not None:
|
||||||
update_required = True
|
|
||||||
db_tag.artists = [
|
db_tag.artists = [
|
||||||
{
|
{
|
||||||
'name': artist['name'].strip()
|
'name': artist['name'].strip()
|
||||||
@ -96,13 +93,6 @@ def put_tag(tag_id, user):
|
|||||||
|
|
||||||
db_tag.update()
|
db_tag.update()
|
||||||
|
|
||||||
if update_required:
|
|
||||||
# queue serverless refresh
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
|
||||||
serverless_update_tag(username=user.username, tag_id=tag_id)
|
|
||||||
else:
|
|
||||||
update_tag(username=user.username, tag_id=tag_id)
|
|
||||||
|
|
||||||
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
||||||
|
|
||||||
|
|
||||||
@ -113,7 +103,7 @@ def post_tag(tag_id, user):
|
|||||||
|
|
||||||
existing_ids = [i.tag_id for i in Tag.collection.parent(user.key).fetch()]
|
existing_ids = [i.tag_id for i in Tag.collection.parent(user.key).fetch()]
|
||||||
while tag_id in existing_ids:
|
while tag_id in existing_ids:
|
||||||
tag_id += '1'
|
tag_id += '_'
|
||||||
|
|
||||||
tag = Tag(parent=user.key)
|
tag = Tag(parent=user.key)
|
||||||
tag.tag_id = tag_id
|
tag.tag_id = tag_id
|
||||||
@ -144,3 +134,26 @@ def tag_refresh(tag_id, user=None):
|
|||||||
update_tag(username=user.username, tag_id=tag_id)
|
update_tag(username=user.username, tag_id=tag_id)
|
||||||
|
|
||||||
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/tag/update/task', methods=['POST'])
|
||||||
|
@cloud_task
|
||||||
|
def run_tag_task():
|
||||||
|
|
||||||
|
payload = request.get_data(as_text=True)
|
||||||
|
if payload:
|
||||||
|
payload = json.loads(payload)
|
||||||
|
|
||||||
|
logger.info(f'running {payload["username"]} / {payload["tag_id"]}')
|
||||||
|
|
||||||
|
serverless_update_tag(username=payload['username'], tag_id=payload['tag_id'])
|
||||||
|
|
||||||
|
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/tag/update/users/cron', methods=['GET'])
|
||||||
|
@gae_cron
|
||||||
|
def run_tags_cron():
|
||||||
|
|
||||||
|
update_all_user_tags()
|
||||||
|
return jsonify({'status': 'success'}), 200
|
||||||
|
@ -6,12 +6,12 @@ import logging
|
|||||||
from google.cloud import tasks_v2
|
from google.cloud import tasks_v2
|
||||||
from google.protobuf import timestamp_pb2
|
from google.protobuf import timestamp_pb2
|
||||||
|
|
||||||
from music.db import database as database
|
|
||||||
from music.tasks.run_user_playlist import run_user_playlist
|
from music.tasks.run_user_playlist import run_user_playlist
|
||||||
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats
|
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats
|
||||||
|
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.playlist import Playlist
|
from music.model.playlist import Playlist
|
||||||
|
from music.model.tag import Tag
|
||||||
|
|
||||||
tasker = tasks_v2.CloudTasksClient()
|
tasker = tasks_v2.CloudTasksClient()
|
||||||
task_path = tasker.queue_path('sarsooxyz', 'europe-west2', 'spotify-executions')
|
task_path = tasker.queue_path('sarsooxyz', 'europe-west2', 'spotify-executions')
|
||||||
@ -19,7 +19,7 @@ task_path = tasker.queue_path('sarsooxyz', 'europe-west2', 'spotify-executions')
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def execute_all_user_playlists():
|
def update_all_user_playlists():
|
||||||
"""Create user playlist refresh task for all users"""
|
"""Create user playlist refresh task for all users"""
|
||||||
|
|
||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
@ -48,7 +48,7 @@ def execute_all_user_playlists():
|
|||||||
seconds_delay += 30
|
seconds_delay += 30
|
||||||
|
|
||||||
|
|
||||||
def execute_user_playlists(username):
|
def update_playlists(username):
|
||||||
"""Refresh all playlists for given user, environment dependent"""
|
"""Refresh all playlists for given user, environment dependent"""
|
||||||
|
|
||||||
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
@ -66,14 +66,14 @@ def execute_user_playlists(username):
|
|||||||
if iterate_playlist.uri is not None:
|
if iterate_playlist.uri is not None:
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_run_user_playlist_task(username, iterate_playlist.name, seconds_delay)
|
run_user_playlist_task(username, iterate_playlist.name, seconds_delay)
|
||||||
else:
|
else:
|
||||||
run_user_playlist(username, iterate_playlist.name)
|
run_user_playlist(username, iterate_playlist.name)
|
||||||
|
|
||||||
seconds_delay += 6
|
seconds_delay += 6
|
||||||
|
|
||||||
|
|
||||||
def create_run_user_playlist_task(username, playlist_name, delay=0):
|
def run_user_playlist_task(username, playlist_name, delay=0):
|
||||||
"""Create tasks for a users given playlist"""
|
"""Create tasks for a users given playlist"""
|
||||||
|
|
||||||
task = {
|
task = {
|
||||||
@ -101,54 +101,7 @@ def create_run_user_playlist_task(username, playlist_name, delay=0):
|
|||||||
tasker.create_task(task_path, task)
|
tasker.create_task(task_path, task)
|
||||||
|
|
||||||
|
|
||||||
def create_play_user_playlist_task(username,
|
def refresh_all_user_playlist_stats():
|
||||||
parts=None,
|
|
||||||
playlist_type='default',
|
|
||||||
playlists=None,
|
|
||||||
shuffle=False,
|
|
||||||
include_recommendations=False,
|
|
||||||
recommendation_sample=10,
|
|
||||||
day_boundary=10,
|
|
||||||
add_this_month=False,
|
|
||||||
add_last_month=False,
|
|
||||||
delay=0,
|
|
||||||
device_name=None):
|
|
||||||
"""Create tasks for a users given scratch playlist"""
|
|
||||||
|
|
||||||
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,
|
|
||||||
'add_this_month': add_this_month,
|
|
||||||
'add_last_month': add_last_month,
|
|
||||||
'device_name': device_name
|
|
||||||
}).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 execute_all_user_playlist_stats():
|
|
||||||
""""Create user playlist stats refresh task for all users"""
|
""""Create user playlist stats refresh task for all users"""
|
||||||
|
|
||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
@ -160,9 +113,9 @@ def execute_all_user_playlist_stats():
|
|||||||
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_refresh_user_task(username=iter_user.username, delay=seconds_delay)
|
refresh_user_stats_task(username=iter_user.username, delay=seconds_delay)
|
||||||
else:
|
else:
|
||||||
execute_user_playlist_stats(username=iter_user.username)
|
refresh_user_playlist_stats(username=iter_user.username)
|
||||||
|
|
||||||
seconds_delay += 2400
|
seconds_delay += 2400
|
||||||
|
|
||||||
@ -170,7 +123,7 @@ def execute_all_user_playlist_stats():
|
|||||||
logger.debug(f'skipping {iter_user.username}')
|
logger.debug(f'skipping {iter_user.username}')
|
||||||
|
|
||||||
|
|
||||||
def execute_user_playlist_stats(username):
|
def refresh_user_playlist_stats(username):
|
||||||
"""Refresh all playlist stats for given user, environment dependent"""
|
"""Refresh all playlist stats for given user, environment dependent"""
|
||||||
|
|
||||||
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
@ -188,7 +141,7 @@ def execute_user_playlist_stats(username):
|
|||||||
if playlist.uri is not None:
|
if playlist.uri is not None:
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_refresh_playlist_task(username, playlist.name, seconds_delay)
|
refresh_playlist_task(username, playlist.name, seconds_delay)
|
||||||
else:
|
else:
|
||||||
refresh_lastfm_track_stats(username, playlist.name)
|
refresh_lastfm_track_stats(username, playlist.name)
|
||||||
|
|
||||||
@ -197,7 +150,7 @@ def execute_user_playlist_stats(username):
|
|||||||
logger.error('no last.fm username')
|
logger.error('no last.fm username')
|
||||||
|
|
||||||
|
|
||||||
def create_refresh_user_task(username, delay=0):
|
def refresh_user_stats_task(username, delay=0):
|
||||||
"""Create user playlist stats refresh task"""
|
"""Create user playlist stats refresh task"""
|
||||||
|
|
||||||
task = {
|
task = {
|
||||||
@ -219,7 +172,7 @@ def create_refresh_user_task(username, delay=0):
|
|||||||
tasker.create_task(task_path, task)
|
tasker.create_task(task_path, task)
|
||||||
|
|
||||||
|
|
||||||
def create_refresh_playlist_task(username, playlist_name, delay=0):
|
def refresh_playlist_task(username, playlist_name, delay=0):
|
||||||
"""Create user playlist stats refresh tasks"""
|
"""Create user playlist stats refresh tasks"""
|
||||||
|
|
||||||
track_task = {
|
track_task = {
|
||||||
@ -274,3 +227,37 @@ def create_refresh_playlist_task(username, playlist_name, delay=0):
|
|||||||
tasker.create_task(task_path, track_task)
|
tasker.create_task(task_path, track_task)
|
||||||
tasker.create_task(task_path, album_task)
|
tasker.create_task(task_path, album_task)
|
||||||
tasker.create_task(task_path, artist_task)
|
tasker.create_task(task_path, artist_task)
|
||||||
|
|
||||||
|
|
||||||
|
def update_all_user_tags():
|
||||||
|
"""Create user tag refresh task sfor all users"""
|
||||||
|
|
||||||
|
seconds_delay = 0
|
||||||
|
logger.info('running')
|
||||||
|
|
||||||
|
for iter_user in User.collection.fetch():
|
||||||
|
|
||||||
|
if iter_user.lastfm_username and len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
||||||
|
|
||||||
|
for tag in Tag.collection.parent(iter_user.key).fetch():
|
||||||
|
|
||||||
|
task = {
|
||||||
|
'app_engine_http_request': { # Specify the type of request.
|
||||||
|
'http_method': 'POST',
|
||||||
|
'relative_uri': '/api/tag/update/task',
|
||||||
|
'body': json.dumps({
|
||||||
|
'username': iter_user.username,
|
||||||
|
'tag_id': tag.tag_id
|
||||||
|
}).encode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds_delay)
|
||||||
|
|
||||||
|
timestamp = timestamp_pb2.Timestamp()
|
||||||
|
timestamp.FromDatetime(d)
|
||||||
|
|
||||||
|
task['schedule_time'] = timestamp
|
||||||
|
|
||||||
|
tasker.create_task(task_path, task)
|
||||||
|
seconds_delay += 10
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from spotframework.engine.playlistengine import PlaylistEngine, PlaylistSource, RecommendationSource
|
|
||||||
from spotframework.engine.processor.shuffle import Shuffle
|
|
||||||
from spotframework.engine.processor.sort import SortReleaseDate
|
|
||||||
from spotframework.engine.processor.deduplicate import DeduplicateByID
|
|
||||||
|
|
||||||
from spotframework.player.player import Player
|
|
||||||
import music.db.database as database
|
|
||||||
from music.db.part_generator import PartGenerator
|
|
||||||
from music.model.user import User
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def play_user_playlist(username,
|
|
||||||
playlist_type='default',
|
|
||||||
parts=None,
|
|
||||||
playlists=None,
|
|
||||||
shuffle=False,
|
|
||||||
include_recommendations=True,
|
|
||||||
recommendation_sample=10,
|
|
||||||
day_boundary=10,
|
|
||||||
add_this_month=False,
|
|
||||||
add_last_month=False,
|
|
||||||
device_name=None):
|
|
||||||
|
|
||||||
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(f'playing for {username}')
|
|
||||||
|
|
||||||
if parts is None and playlists is None:
|
|
||||||
logger.critical(f'no playlists to use for creation ({username})')
|
|
||||||
return None
|
|
||||||
|
|
||||||
if parts is None:
|
|
||||||
parts = []
|
|
||||||
|
|
||||||
if playlists is None:
|
|
||||||
playlists = []
|
|
||||||
|
|
||||||
if len(parts) == 0 and len(playlists) == 0:
|
|
||||||
logger.critical(f'no playlists to use for creation ({username})')
|
|
||||||
return None
|
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(user)
|
|
||||||
|
|
||||||
device = None
|
|
||||||
if device_name:
|
|
||||||
devices = net.get_available_devices()
|
|
||||||
if devices and len(devices) > 0:
|
|
||||||
device = next((i for i in devices if i.name == device_name), None)
|
|
||||||
if device is None:
|
|
||||||
logger.error(f'error selecting device {device_name} to play on')
|
|
||||||
else:
|
|
||||||
logger.warning(f'no available devices to play')
|
|
||||||
|
|
||||||
engine = PlaylistEngine(net)
|
|
||||||
|
|
||||||
player = Player(net)
|
|
||||||
|
|
||||||
processors = [DeduplicateByID()]
|
|
||||||
|
|
||||||
if shuffle:
|
|
||||||
processors.append(Shuffle())
|
|
||||||
else:
|
|
||||||
processors.append(SortReleaseDate(reverse=True))
|
|
||||||
|
|
||||||
submit_parts = parts
|
|
||||||
|
|
||||||
part_generator = PartGenerator(user=user)
|
|
||||||
|
|
||||||
for part in playlists:
|
|
||||||
submit_parts += part_generator.get_recursive_parts(part)
|
|
||||||
|
|
||||||
submit_parts = [i for i in {j for j in submit_parts}]
|
|
||||||
|
|
||||||
params = [
|
|
||||||
PlaylistSource.Params(names=submit_parts, processors=processors)
|
|
||||||
]
|
|
||||||
|
|
||||||
if include_recommendations:
|
|
||||||
params.append(RecommendationSource.Params(recommendation_limit=int(recommendation_sample)))
|
|
||||||
|
|
||||||
if playlist_type == 'recents':
|
|
||||||
boundary_date = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=int(day_boundary))
|
|
||||||
tracks = engine.get_recent_playlist(params=params,
|
|
||||||
boundary_date=boundary_date,
|
|
||||||
add_this_month=add_this_month,
|
|
||||||
add_last_month=add_last_month)
|
|
||||||
else:
|
|
||||||
tracks = engine.make_playlist(params=params)
|
|
||||||
|
|
||||||
player.play(tracks=tracks, device=device)
|
|
@ -1,20 +1,20 @@
|
|||||||
astroid==2.4.0
|
astroid==2.4.1
|
||||||
cachetools==4.1.0
|
cachetools==4.1.0
|
||||||
certifi==2020.4.5.1
|
certifi==2020.4.5.1
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
click==7.1.2
|
click==7.1.2
|
||||||
fireo==1.2.4
|
fireo==1.2.8
|
||||||
Flask==1.1.2
|
Flask==1.1.2
|
||||||
google-api-core==1.17.0
|
google-api-core==1.17.0
|
||||||
google-auth==1.14.1
|
google-auth==1.14.3
|
||||||
google-cloud-core==1.3.0
|
google-cloud-core==1.3.0
|
||||||
google-cloud-firestore==1.6.2
|
google-cloud-firestore==1.6.2
|
||||||
google-cloud-logging==1.15.0
|
google-cloud-logging==1.15.0
|
||||||
google-cloud-pubsub==1.4.3
|
google-cloud-pubsub==1.5.0
|
||||||
google-cloud-tasks==1.5.0
|
google-cloud-tasks==1.5.0
|
||||||
googleapis-common-protos==1.51.0
|
googleapis-common-protos==1.51.0
|
||||||
grpc-google-iam-v1==0.12.3
|
grpc-google-iam-v1==0.12.3
|
||||||
grpcio==1.28.1
|
grpcio==1.29.0
|
||||||
idna==2.9
|
idna==2.9
|
||||||
isort==4.3.21
|
isort==4.3.21
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
@ -22,18 +22,18 @@ Jinja2==2.11.2
|
|||||||
lazy-object-proxy==1.4.3
|
lazy-object-proxy==1.4.3
|
||||||
MarkupSafe==1.1.1
|
MarkupSafe==1.1.1
|
||||||
mccabe==0.6.1
|
mccabe==0.6.1
|
||||||
numpy==1.18.3
|
numpy==1.18.4
|
||||||
opencv-python==4.2.0.34
|
opencv-python==4.2.0.34
|
||||||
protobuf==3.11.3
|
protobuf==3.11.3
|
||||||
pyasn1==0.4.8
|
pyasn1==0.4.8
|
||||||
pyasn1-modules==0.2.8
|
pyasn1-modules==0.2.8
|
||||||
pylint==2.5.0
|
pylint==2.5.2
|
||||||
pytz==2020.1
|
pytz==2020.1
|
||||||
requests==2.23.0
|
requests==2.23.0
|
||||||
rsa==4.0
|
rsa==4.0
|
||||||
six==1.14.0
|
six==1.14.0
|
||||||
tabulate==0.8.7
|
tabulate==0.8.7
|
||||||
toml==0.10.0
|
toml==0.10.1
|
||||||
urllib3==1.25.9
|
urllib3==1.25.9
|
||||||
Werkzeug==1.0.1
|
Werkzeug==1.0.1
|
||||||
wrapt==1.12.1
|
wrapt==1.12.1
|
||||||
|
@ -3,7 +3,6 @@ import { Route, Switch } from "react-router-dom";
|
|||||||
|
|
||||||
import PlaylistsView from "./PlaylistsList.js"
|
import PlaylistsView from "./PlaylistsList.js"
|
||||||
import NewPlaylist from "./New.js";
|
import NewPlaylist from "./New.js";
|
||||||
import ScratchView from "./ScratchView.js";
|
|
||||||
|
|
||||||
class Playlists extends Component {
|
class Playlists extends Component {
|
||||||
render(){
|
render(){
|
||||||
@ -12,7 +11,6 @@ class Playlists extends Component {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={`${this.props.match.url}/`} component={PlaylistsView} />
|
<Route exact path={`${this.props.match.url}/`} component={PlaylistsView} />
|
||||||
<Route path={`${this.props.match.url}/new`} component={NewPlaylist} />
|
<Route path={`${this.props.match.url}/new`} component={NewPlaylist} />
|
||||||
<Route path={`${this.props.match.url}/play`} component={ScratchView} />
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,410 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
const axios = require('axios');
|
|
||||||
|
|
||||||
import showMessage from "../Toast.js"
|
|
||||||
|
|
||||||
var thisMonth = [
|
|
||||||
'january',
|
|
||||||
'february',
|
|
||||||
'march',
|
|
||||||
'april',
|
|
||||||
'may',
|
|
||||||
'june',
|
|
||||||
'july',
|
|
||||||
'august',
|
|
||||||
'septempber',
|
|
||||||
'october',
|
|
||||||
'november',
|
|
||||||
'december'
|
|
||||||
];
|
|
||||||
|
|
||||||
var lastMonth = [
|
|
||||||
'december',
|
|
||||||
'january',
|
|
||||||
'february',
|
|
||||||
'march',
|
|
||||||
'april',
|
|
||||||
'may',
|
|
||||||
'june',
|
|
||||||
'july',
|
|
||||||
'august',
|
|
||||||
'septempber',
|
|
||||||
'october',
|
|
||||||
'november'
|
|
||||||
];
|
|
||||||
|
|
||||||
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: '',
|
|
||||||
newReferenceName: '',
|
|
||||||
|
|
||||||
shuffle: false,
|
|
||||||
include_recommendations: false,
|
|
||||||
add_this_month: false,
|
|
||||||
add_last_month: false
|
|
||||||
}
|
|
||||||
this.handleAddPart = this.handleAddPart.bind(this);
|
|
||||||
this.handleAddReference = this.handleAddReference.bind(this);
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
|
||||||
this.handleRemovePart = this.handleRemovePart.bind(this);
|
|
||||||
this.handleRemoveReference = this.handleRemoveReference.bind(this);
|
|
||||||
|
|
||||||
this.handleRun = this.handleRun.bind(this);
|
|
||||||
|
|
||||||
this.handleShuffleChange = this.handleShuffleChange.bind(this);
|
|
||||||
this.handleIncludeRecommendationsChange = this.handleIncludeRecommendationsChange.bind(this);
|
|
||||||
this.handleThisMonthChange = this.handleThisMonthChange.bind(this);
|
|
||||||
this.handleLastMonthChange = this.handleLastMonthChange.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,
|
|
||||||
newReferenceName: 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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleShuffleChange(event) {
|
|
||||||
this.setState({
|
|
||||||
shuffle: event.target.checked
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleThisMonthChange(event) {
|
|
||||||
this.setState({
|
|
||||||
add_this_month: event.target.checked
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLastMonthChange(event) {
|
|
||||||
this.setState({
|
|
||||||
add_last_month: event.target.checked
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleIncludeRecommendationsChange(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.newReferenceName.length != 0){
|
|
||||||
|
|
||||||
var check = this.state.playlist_references.includes(this.state.newReferenceName);
|
|
||||||
|
|
||||||
if(check == false) {
|
|
||||||
var playlist_references = this.state.playlist_references.slice();
|
|
||||||
playlist_references.push(this.state.newReferenceName);
|
|
||||||
|
|
||||||
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,
|
|
||||||
newReferenceName: filteredPlaylists.length > 0 ? filteredPlaylists[0].name : ''
|
|
||||||
});
|
|
||||||
|
|
||||||
}else{
|
|
||||||
showMessage('Playlist Already Added');
|
|
||||||
}
|
|
||||||
|
|
||||||
}else{
|
|
||||||
showMessage('No Other Playlists To Add');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRemovePart(id, event){
|
|
||||||
var parts = this.state.parts;
|
|
||||||
parts = parts.filter(e => e !== id);
|
|
||||||
this.setState({
|
|
||||||
parts: parts
|
|
||||||
});
|
|
||||||
|
|
||||||
if(parts.length == 0) {
|
|
||||||
parts = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRemoveReference(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,
|
|
||||||
add_this_month: this.state.add_this_month,
|
|
||||||
add_last_month: this.state.add_last_month
|
|
||||||
})
|
|
||||||
.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(){
|
|
||||||
|
|
||||||
var date = new Date();
|
|
||||||
|
|
||||||
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.handleRemoveReference} list={this.state.playlist_references}/> }
|
|
||||||
{ this.state.parts.length > 0 && <ListBlock name="spotify" handler={this.handleRemovePart} 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="newReferenceName"
|
|
||||||
className="full-width"
|
|
||||||
value={this.state.newReferenceName}
|
|
||||||
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.handleIncludeRecommendationsChange}></input>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className="center-text ui-text text-no-select">
|
|
||||||
Recommendation Size
|
|
||||||
</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>
|
|
||||||
}
|
|
||||||
{ this.state.type == 'recents' &&
|
|
||||||
<tr>
|
|
||||||
<td className="center-text ui-text text-no-select">
|
|
||||||
Include {thisMonth[date.getMonth()]} Playlist
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox"
|
|
||||||
checked={this.state.add_this_month}
|
|
||||||
onChange={this.handleThisMonthChange}></input>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
{ this.state.type == 'recents' &&
|
|
||||||
<tr>
|
|
||||||
<td className="center-text ui-text text-no-select">
|
|
||||||
Include {lastMonth[date.getMonth()]} Playlist
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox"
|
|
||||||
checked={this.state.add_last_month}
|
|
||||||
onChange={this.handleLastMonthChange}></input>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
<tr>
|
|
||||||
<td className="center-text ui-text text-no-select">
|
|
||||||
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>
|
|
||||||
<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