parent
e3fd4bb6d5
commit
3c248fd728
@ -8,10 +8,8 @@ import logging
|
||||
from datetime import datetime
|
||||
|
||||
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, \
|
||||
create_play_user_playlist_task
|
||||
from music.cloud.tasks import update_all_user_playlists, update_playlists, run_user_playlist_task
|
||||
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.playlist import Playlist
|
||||
@ -25,7 +23,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
@blueprint.route('/playlists', methods=['GET'])
|
||||
@login_or_basic_auth
|
||||
def get_playlists(user=None):
|
||||
def all_playlists_route(user=None):
|
||||
assert user is not None
|
||||
return jsonify({
|
||||
'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
|
||||
|
||||
if playlist_type is not None:
|
||||
# TODO check acceptable value
|
||||
updating_playlist.type = playlist_type
|
||||
playlist_type = playlist_type.strip().lower()
|
||||
if playlist_type in ['default', 'recents', 'fmchart']:
|
||||
updating_playlist.type = playlist_type
|
||||
|
||||
updating_playlist.update()
|
||||
logger.info(f'updated {user.username} / {playlist_name}')
|
||||
@ -241,7 +240,7 @@ def user_route(user=None):
|
||||
@blueprint.route('/users', methods=['GET'])
|
||||
@login_or_basic_auth
|
||||
@admin_required
|
||||
def users(user=None):
|
||||
def all_users_route(user=None):
|
||||
return jsonify({
|
||||
'accounts': [i.to_dict() for i in User.collection.fetch()]
|
||||
}), 200
|
||||
@ -274,82 +273,6 @@ def change_password(user=None):
|
||||
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'])
|
||||
@login_or_basic_auth
|
||||
def run_playlist(user=None):
|
||||
@ -359,7 +282,7 @@ def run_playlist(user=None):
|
||||
if playlist_name:
|
||||
|
||||
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:
|
||||
run_user_playlist(user.username, playlist_name)
|
||||
|
||||
@ -394,7 +317,7 @@ def run_user(user=None):
|
||||
else:
|
||||
user_name = user.username
|
||||
|
||||
execute_user_playlists(user_name)
|
||||
update_playlists(user_name)
|
||||
|
||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||
|
||||
@ -405,7 +328,7 @@ def run_user_task():
|
||||
|
||||
payload = request.get_data(as_text=True)
|
||||
if payload:
|
||||
execute_user_playlists(payload)
|
||||
update_playlists(payload)
|
||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||
|
||||
|
||||
@ -414,7 +337,7 @@ def run_user_task():
|
||||
@admin_required
|
||||
def run_users(user=None):
|
||||
|
||||
execute_all_user_playlists()
|
||||
update_all_user_playlists()
|
||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||
|
||||
|
||||
@ -422,7 +345,7 @@ def run_users(user=None):
|
||||
@gae_cron
|
||||
def run_users_cron():
|
||||
|
||||
execute_all_user_playlists()
|
||||
update_all_user_playlists()
|
||||
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
|
||||
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, \
|
||||
refresh_lastfm_album_stats, \
|
||||
refresh_lastfm_artist_stats
|
||||
@ -74,7 +74,7 @@ def playlist_refresh(user=None):
|
||||
if playlist_name:
|
||||
|
||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||
create_refresh_playlist_task(user.username, playlist_name)
|
||||
refresh_playlist_task(user.username, playlist_name)
|
||||
else:
|
||||
refresh_lastfm_track_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
|
||||
@admin_required
|
||||
def run_users(user=None):
|
||||
execute_all_user_playlist_stats()
|
||||
refresh_all_user_playlist_stats()
|
||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||
|
||||
|
||||
@blueprint.route('/playlist/refresh/users/cron', methods=['GET'])
|
||||
@gae_cron
|
||||
def run_users_task():
|
||||
execute_all_user_playlist_stats()
|
||||
refresh_all_user_playlist_stats()
|
||||
return jsonify({'status': 'success'}), 200
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ def run_user(user=None):
|
||||
else:
|
||||
user_name = user.username
|
||||
|
||||
execute_user_playlist_stats(user_name)
|
||||
refresh_user_playlist_stats(user_name)
|
||||
|
||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||
|
||||
@ -167,5 +167,5 @@ def run_user_task():
|
||||
|
||||
payload = request.get_data(as_text=True)
|
||||
if payload:
|
||||
execute_user_playlist_stats(payload)
|
||||
refresh_user_playlist_stats(payload)
|
||||
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
||||
|
@ -2,9 +2,11 @@ from flask import Blueprint, jsonify, request
|
||||
|
||||
import logging
|
||||
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.tasks import update_all_user_tags
|
||||
from music.tasks.update_tag import update_tag
|
||||
|
||||
from music.model.tag import Tag
|
||||
@ -60,10 +62,7 @@ def put_tag(tag_id, user):
|
||||
if request_json.get('name'):
|
||||
db_tag.name = request_json['name'].strip()
|
||||
|
||||
update_required = False
|
||||
|
||||
if request_json.get('tracks') is not None:
|
||||
update_required = True
|
||||
db_tag.tracks = [
|
||||
{
|
||||
'name': track['name'].strip(),
|
||||
@ -74,7 +73,6 @@ def put_tag(tag_id, user):
|
||||
]
|
||||
|
||||
if request_json.get('albums') is not None:
|
||||
update_required = True
|
||||
db_tag.albums = [
|
||||
{
|
||||
'name': album['name'].strip(),
|
||||
@ -85,7 +83,6 @@ def put_tag(tag_id, user):
|
||||
]
|
||||
|
||||
if request_json.get('artists') is not None:
|
||||
update_required = True
|
||||
db_tag.artists = [
|
||||
{
|
||||
'name': artist['name'].strip()
|
||||
@ -96,13 +93,6 @@ def put_tag(tag_id, user):
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -113,7 +103,7 @@ def post_tag(tag_id, user):
|
||||
|
||||
existing_ids = [i.tag_id for i in Tag.collection.parent(user.key).fetch()]
|
||||
while tag_id in existing_ids:
|
||||
tag_id += '1'
|
||||
tag_id += '_'
|
||||
|
||||
tag = Tag(parent=user.key)
|
||||
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)
|
||||
|
||||
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.protobuf import timestamp_pb2
|
||||
|
||||
from music.db import database as database
|
||||
from music.tasks.run_user_playlist import run_user_playlist
|
||||
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats
|
||||
|
||||
from music.model.user import User
|
||||
from music.model.playlist import Playlist
|
||||
from music.model.tag import Tag
|
||||
|
||||
tasker = tasks_v2.CloudTasksClient()
|
||||
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__)
|
||||
|
||||
|
||||
def execute_all_user_playlists():
|
||||
def update_all_user_playlists():
|
||||
"""Create user playlist refresh task for all users"""
|
||||
|
||||
seconds_delay = 0
|
||||
@ -48,7 +48,7 @@ def execute_all_user_playlists():
|
||||
seconds_delay += 30
|
||||
|
||||
|
||||
def execute_user_playlists(username):
|
||||
def update_playlists(username):
|
||||
"""Refresh all playlists for given user, environment dependent"""
|
||||
|
||||
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 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:
|
||||
run_user_playlist(username, iterate_playlist.name)
|
||||
|
||||
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"""
|
||||
|
||||
task = {
|
||||
@ -101,54 +101,7 @@ def create_run_user_playlist_task(username, playlist_name, delay=0):
|
||||
tasker.create_task(task_path, task)
|
||||
|
||||
|
||||
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,
|
||||
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():
|
||||
def refresh_all_user_playlist_stats():
|
||||
""""Create user playlist stats refresh task for all users"""
|
||||
|
||||
seconds_delay = 0
|
||||
@ -160,9 +113,9 @@ def execute_all_user_playlist_stats():
|
||||
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
||||
|
||||
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:
|
||||
execute_user_playlist_stats(username=iter_user.username)
|
||||
refresh_user_playlist_stats(username=iter_user.username)
|
||||
|
||||
seconds_delay += 2400
|
||||
|
||||
@ -170,7 +123,7 @@ def execute_all_user_playlist_stats():
|
||||
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"""
|
||||
|
||||
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 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:
|
||||
refresh_lastfm_track_stats(username, playlist.name)
|
||||
|
||||
@ -197,7 +150,7 @@ def execute_user_playlist_stats(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"""
|
||||
|
||||
task = {
|
||||
@ -219,7 +172,7 @@ def create_refresh_user_task(username, delay=0):
|
||||
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"""
|
||||
|
||||
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, album_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
|
||||
certifi==2020.4.5.1
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
fireo==1.2.4
|
||||
fireo==1.2.8
|
||||
Flask==1.1.2
|
||||
google-api-core==1.17.0
|
||||
google-auth==1.14.1
|
||||
google-auth==1.14.3
|
||||
google-cloud-core==1.3.0
|
||||
google-cloud-firestore==1.6.2
|
||||
google-cloud-logging==1.15.0
|
||||
google-cloud-pubsub==1.4.3
|
||||
google-cloud-pubsub==1.5.0
|
||||
google-cloud-tasks==1.5.0
|
||||
googleapis-common-protos==1.51.0
|
||||
grpc-google-iam-v1==0.12.3
|
||||
grpcio==1.28.1
|
||||
grpcio==1.29.0
|
||||
idna==2.9
|
||||
isort==4.3.21
|
||||
itsdangerous==1.1.0
|
||||
@ -22,18 +22,18 @@ Jinja2==2.11.2
|
||||
lazy-object-proxy==1.4.3
|
||||
MarkupSafe==1.1.1
|
||||
mccabe==0.6.1
|
||||
numpy==1.18.3
|
||||
numpy==1.18.4
|
||||
opencv-python==4.2.0.34
|
||||
protobuf==3.11.3
|
||||
pyasn1==0.4.8
|
||||
pyasn1-modules==0.2.8
|
||||
pylint==2.5.0
|
||||
pylint==2.5.2
|
||||
pytz==2020.1
|
||||
requests==2.23.0
|
||||
rsa==4.0
|
||||
six==1.14.0
|
||||
tabulate==0.8.7
|
||||
toml==0.10.0
|
||||
toml==0.10.1
|
||||
urllib3==1.25.9
|
||||
Werkzeug==1.0.1
|
||||
wrapt==1.12.1
|
||||
|
@ -3,7 +3,6 @@ import { Route, Switch } from "react-router-dom";
|
||||
|
||||
import PlaylistsView from "./PlaylistsList.js"
|
||||
import NewPlaylist from "./New.js";
|
||||
import ScratchView from "./ScratchView.js";
|
||||
|
||||
class Playlists extends Component {
|
||||
render(){
|
||||
@ -12,7 +11,6 @@ class Playlists extends Component {
|
||||
<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>
|
||||
);
|
||||
|
@ -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