diff --git a/music/api/api.py b/music/api/api.py
index dd02c3a..49aa914 100644
--- a/music/api/api.py
+++ b/music/api/api.py
@@ -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
diff --git a/music/api/spotfm.py b/music/api/spotfm.py
index 6f7b7e6..ca9d06c 100644
--- a/music/api/spotfm.py
+++ b/music/api/spotfm.py
@@ -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
diff --git a/music/api/tag.py b/music/api/tag.py
index 2bb26fe..c7aa9cf 100644
--- a/music/api/tag.py
+++ b/music/api/tag.py
@@ -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
diff --git a/music/cloud/tasks.py b/music/cloud/tasks.py
index 9d880f0..2552c1d 100644
--- a/music/cloud/tasks.py
+++ b/music/cloud/tasks.py
@@ -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
diff --git a/music/tasks/play_user_playlist.py b/music/tasks/play_user_playlist.py
deleted file mode 100644
index 5ca68c5..0000000
--- a/music/tasks/play_user_playlist.py
+++ /dev/null
@@ -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)
diff --git a/requirements.txt b/requirements.txt
index b8889c9..033e195 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
diff --git a/src/js/Playlist/AllPlaylistsRouter.js b/src/js/Playlist/AllPlaylistsRouter.js
index 6f95c59..f6438f7 100644
--- a/src/js/Playlist/AllPlaylistsRouter.js
+++ b/src/js/Playlist/AllPlaylistsRouter.js
@@ -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 {
{ this.state.name } |
- |
---|---|
- Spotify playlist can be the name of either your own created playlist or one you follow, names are case sensitive - |
- |
- - | -- - | -
- - | -- - | -
- Shuffle Output - | -- - | -
- Include Recommendations - | -- - | -
- Recommendation Size - | -- - | -
- Added Since (Days) - | -- - | -
- Include {thisMonth[date.getMonth()]} Playlist - | -- - | -
- Include {lastMonth[date.getMonth()]} Playlist - | -- - | -
- Type - | -- - | -
- - | -
{ this.state.error_text }
; - - return this.state.error ? error : table; - } - -} - -function ReferenceEntry(props) { - return ( - - ); -} - -function ListBlock(props) { - return ( - -