2019-10-07 12:21:26 +01:00
|
|
|
from flask import Blueprint, jsonify, request
|
|
|
|
import logging
|
2019-10-19 20:35:37 +01:00
|
|
|
import json
|
|
|
|
import os
|
2019-10-20 01:27:59 +01:00
|
|
|
import datetime
|
2019-10-07 12:21:26 +01:00
|
|
|
|
2019-10-20 01:27:59 +01:00
|
|
|
from music.api.decorators import admin_required, login_or_basic_auth, lastfm_username_required, spotify_link_required, cloud_task, gae_cron
|
2019-10-19 17:14:11 +01:00
|
|
|
import music.db.database as database
|
2019-10-20 17:28:05 +01:00
|
|
|
from music.tasks.refresh_lastfm_stats import refresh_lastfm_track_stats, \
|
|
|
|
refresh_lastfm_album_stats, \
|
|
|
|
refresh_lastfm_artist_stats
|
2019-10-07 12:21:26 +01:00
|
|
|
|
|
|
|
from spotfm.maths.counter import Counter
|
|
|
|
from spotframework.model.uri import Uri
|
|
|
|
|
2019-10-20 01:27:59 +01:00
|
|
|
from google.cloud import firestore
|
2019-10-19 20:35:37 +01:00
|
|
|
from google.cloud import tasks_v2
|
2019-10-20 01:27:59 +01:00
|
|
|
from google.protobuf import timestamp_pb2
|
2019-10-19 20:35:37 +01:00
|
|
|
|
2019-10-07 12:21:26 +01:00
|
|
|
blueprint = Blueprint('spotfm-api', __name__)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-10-20 01:27:59 +01:00
|
|
|
db = firestore.Client()
|
2019-10-19 20:35:37 +01:00
|
|
|
tasker = tasks_v2.CloudTasksClient()
|
|
|
|
task_path = tasker.queue_path('sarsooxyz', 'europe-west2', 'spotify-executions')
|
|
|
|
|
2019-10-07 12:21:26 +01:00
|
|
|
|
|
|
|
@blueprint.route('/count', methods=['GET'])
|
|
|
|
@login_or_basic_auth
|
|
|
|
@spotify_link_required
|
|
|
|
@lastfm_username_required
|
|
|
|
def count(username=None):
|
|
|
|
|
|
|
|
uri = request.args.get('uri', None)
|
|
|
|
playlist_name = request.args.get('playlist_name', None)
|
|
|
|
|
|
|
|
if uri is None and playlist_name is None:
|
|
|
|
return jsonify({'error': 'no input provided'}), 401
|
|
|
|
|
|
|
|
if uri:
|
|
|
|
try:
|
|
|
|
uri = Uri(uri)
|
|
|
|
except ValueError:
|
|
|
|
return jsonify({'error': 'malformed uri provided'}), 401
|
|
|
|
|
|
|
|
spotnet = database.get_authed_spotify_network(username)
|
|
|
|
fmnet = database.get_authed_lastfm_network(username)
|
|
|
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
|
|
|
|
|
|
|
if uri:
|
|
|
|
uri_count = counter.count(uri=uri)
|
|
|
|
return jsonify({
|
|
|
|
"uri": str(uri),
|
|
|
|
"count": uri_count,
|
|
|
|
'uri_type': str(uri.object_type),
|
|
|
|
'last.fm_username': fmnet.username
|
|
|
|
}), 200
|
|
|
|
elif playlist_name:
|
|
|
|
|
|
|
|
playlists = spotnet.get_playlists()
|
|
|
|
playlist = next((i for i in playlists if i.name == playlist_name), None)
|
|
|
|
|
|
|
|
if playlist is not None:
|
|
|
|
playlist_count = counter.count_playlist(playlist=playlist)
|
|
|
|
return jsonify({
|
|
|
|
"count": playlist_count,
|
|
|
|
'playlist_name': playlist_name,
|
|
|
|
'last.fm_username': fmnet.username
|
|
|
|
}), 200
|
|
|
|
else:
|
|
|
|
return jsonify({'error': f'playlist {playlist_name} not found'}), 404
|
2019-10-19 20:35:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/playlist/refresh', methods=['GET'])
|
|
|
|
@login_or_basic_auth
|
|
|
|
@spotify_link_required
|
|
|
|
@lastfm_username_required
|
|
|
|
def playlist_refresh(username=None):
|
|
|
|
|
|
|
|
playlist_name = request.args.get('name', None)
|
|
|
|
|
|
|
|
if playlist_name:
|
|
|
|
|
|
|
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
|
|
|
create_refresh_playlist_task(username, playlist_name)
|
|
|
|
else:
|
2019-10-20 17:28:05 +01:00
|
|
|
refresh_lastfm_track_stats(username, playlist_name)
|
|
|
|
refresh_lastfm_album_stats(username, playlist_name)
|
|
|
|
refresh_lastfm_artist_stats(username, playlist_name)
|
2019-10-19 20:35:37 +01:00
|
|
|
|
|
|
|
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
else:
|
|
|
|
logger.warning('no playlist requested')
|
|
|
|
return jsonify({"error": 'no name requested'}), 400
|
|
|
|
|
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
@blueprint.route('/playlist/refresh/task/track', methods=['POST'])
|
2019-10-19 20:35:37 +01:00
|
|
|
@cloud_task
|
2019-10-20 17:28:05 +01:00
|
|
|
def run_playlist_track_task():
|
2019-10-19 20:35:37 +01:00
|
|
|
|
|
|
|
payload = request.get_data(as_text=True)
|
|
|
|
if payload:
|
|
|
|
payload = json.loads(payload)
|
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
logger.info(f'refreshing tracks {payload["username"]} / {payload["name"]}')
|
2019-10-19 20:35:37 +01:00
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
refresh_lastfm_track_stats(payload['username'], payload['name'])
|
|
|
|
|
|
|
|
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/playlist/refresh/task/album', methods=['POST'])
|
|
|
|
@cloud_task
|
|
|
|
def run_playlist_album_task():
|
|
|
|
|
|
|
|
payload = request.get_data(as_text=True)
|
|
|
|
if payload:
|
|
|
|
payload = json.loads(payload)
|
|
|
|
|
|
|
|
logger.info(f'refreshing albums {payload["username"]} / {payload["name"]}')
|
|
|
|
|
|
|
|
refresh_lastfm_album_stats(payload['username'], payload['name'])
|
|
|
|
|
|
|
|
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/playlist/refresh/task/artist', methods=['POST'])
|
|
|
|
@cloud_task
|
|
|
|
def run_playlist_artist_task():
|
|
|
|
|
|
|
|
payload = request.get_data(as_text=True)
|
|
|
|
if payload:
|
|
|
|
payload = json.loads(payload)
|
|
|
|
|
|
|
|
logger.info(f'refreshing artists {payload["username"]} / {payload["name"]}')
|
|
|
|
|
|
|
|
refresh_lastfm_artist_stats(payload['username'], payload['name'])
|
2019-10-19 20:35:37 +01:00
|
|
|
|
|
|
|
return jsonify({'message': 'executed playlist', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
2019-10-20 01:27:59 +01:00
|
|
|
@blueprint.route('/playlist/refresh/users', methods=['GET'])
|
|
|
|
@login_or_basic_auth
|
|
|
|
@admin_required
|
|
|
|
def run_users(username=None):
|
|
|
|
execute_all_users()
|
|
|
|
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_users()
|
|
|
|
return jsonify({'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/playlist/refresh/user', methods=['GET'])
|
|
|
|
@login_or_basic_auth
|
|
|
|
def run_user(username=None):
|
|
|
|
|
2019-10-27 19:05:50 +00:00
|
|
|
db_user = database.get_user(username)
|
|
|
|
if db_user.type == db_user.Type.admin:
|
2019-10-20 01:27:59 +01:00
|
|
|
user_name = request.args.get('username', username)
|
|
|
|
else:
|
|
|
|
user_name = username
|
|
|
|
|
|
|
|
execute_user(user_name)
|
|
|
|
|
|
|
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/playlist/refresh/user/task', methods=['POST'])
|
|
|
|
@cloud_task
|
|
|
|
def run_user_task():
|
|
|
|
|
|
|
|
payload = request.get_data(as_text=True)
|
|
|
|
if payload:
|
|
|
|
execute_user(payload)
|
|
|
|
return jsonify({'message': 'executed user', 'status': 'success'}), 200
|
|
|
|
|
|
|
|
|
|
|
|
def execute_all_users():
|
|
|
|
|
|
|
|
seconds_delay = 0
|
|
|
|
logger.info('running')
|
|
|
|
|
2019-10-23 14:44:17 +01:00
|
|
|
for iter_user in database.get_users():
|
2019-10-20 01:27:59 +01:00
|
|
|
|
2019-10-23 14:44:17 +01:00
|
|
|
if iter_user.spotify_linked and iter_user.lastfm_username and \
|
|
|
|
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
2019-10-23 14:44:17 +01:00
|
|
|
create_refresh_user_task(username=iter_user.username, delay=seconds_delay)
|
2019-10-20 01:27:59 +01:00
|
|
|
else:
|
2019-10-23 14:44:17 +01:00
|
|
|
execute_user(username=iter_user.username)
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
seconds_delay += 2400
|
|
|
|
|
|
|
|
else:
|
2019-10-23 14:44:17 +01:00
|
|
|
logger.debug(f'skipping {iter_user.username}')
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
def execute_user(username):
|
|
|
|
|
2019-10-23 14:44:17 +01:00
|
|
|
playlists = database.get_user_playlists(username)
|
|
|
|
user = database.get_user(username)
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
seconds_delay = 0
|
|
|
|
logger.info(f'running {username}')
|
|
|
|
|
2019-10-23 14:44:17 +01:00
|
|
|
if user.lastfm_username and len(user.lastfm_username) > 0:
|
|
|
|
for playlist in playlists:
|
|
|
|
if playlist.uri:
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
2019-10-23 14:44:17 +01:00
|
|
|
create_refresh_playlist_task(username, playlist.name, seconds_delay)
|
2019-10-20 01:27:59 +01:00
|
|
|
else:
|
2019-10-23 14:44:17 +01:00
|
|
|
refresh_lastfm_track_stats(username, playlist.name)
|
2019-10-20 01:27:59 +01:00
|
|
|
|
|
|
|
seconds_delay += 1200
|
|
|
|
else:
|
|
|
|
logger.error('no last.fm username')
|
|
|
|
|
|
|
|
|
|
|
|
def create_refresh_user_task(username, delay=0):
|
|
|
|
|
|
|
|
task = {
|
|
|
|
'app_engine_http_request': { # Specify the type of request.
|
|
|
|
'http_method': 'POST',
|
|
|
|
'relative_uri': '/api/spotfm/playlist/refresh/user/task',
|
|
|
|
'body': username.encode()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if delay > 0:
|
|
|
|
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay)
|
|
|
|
|
|
|
|
timestamp = timestamp_pb2.Timestamp()
|
|
|
|
timestamp.FromDatetime(d)
|
|
|
|
|
|
|
|
task['schedule_time'] = timestamp
|
|
|
|
|
|
|
|
tasker.create_task(task_path, task)
|
|
|
|
|
|
|
|
|
|
|
|
def create_refresh_playlist_task(username, playlist_name, delay=0):
|
2019-10-19 20:35:37 +01:00
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
track_task = {
|
2019-10-19 20:35:37 +01:00
|
|
|
'app_engine_http_request': { # Specify the type of request.
|
|
|
|
'http_method': 'POST',
|
2019-10-20 17:28:05 +01:00
|
|
|
'relative_uri': '/api/spotfm/playlist/refresh/task/track',
|
2019-10-19 20:35:37 +01:00
|
|
|
'body': json.dumps({
|
|
|
|
'username': username,
|
|
|
|
'name': playlist_name
|
|
|
|
}).encode()
|
|
|
|
}
|
|
|
|
}
|
2019-10-20 01:27:59 +01:00
|
|
|
if delay > 0:
|
|
|
|
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay)
|
|
|
|
timestamp = timestamp_pb2.Timestamp()
|
|
|
|
timestamp.FromDatetime(d)
|
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
track_task['schedule_time'] = timestamp
|
2019-10-20 01:27:59 +01:00
|
|
|
|
2019-10-20 17:28:05 +01:00
|
|
|
album_task = {
|
|
|
|
'app_engine_http_request': { # Specify the type of request.
|
|
|
|
'http_method': 'POST',
|
|
|
|
'relative_uri': '/api/spotfm/playlist/refresh/task/album',
|
|
|
|
'body': json.dumps({
|
|
|
|
'username': username,
|
|
|
|
'name': playlist_name
|
|
|
|
}).encode()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay + 180)
|
|
|
|
timestamp = timestamp_pb2.Timestamp()
|
|
|
|
timestamp.FromDatetime(d)
|
|
|
|
|
|
|
|
album_task['schedule_time'] = timestamp
|
|
|
|
|
|
|
|
artist_task = {
|
|
|
|
'app_engine_http_request': { # Specify the type of request.
|
|
|
|
'http_method': 'POST',
|
|
|
|
'relative_uri': '/api/spotfm/playlist/refresh/task/artist',
|
|
|
|
'body': json.dumps({
|
|
|
|
'username': username,
|
|
|
|
'name': playlist_name
|
|
|
|
}).encode()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay + 360)
|
|
|
|
timestamp = timestamp_pb2.Timestamp()
|
|
|
|
timestamp.FromDatetime(d)
|
|
|
|
|
|
|
|
artist_task['schedule_time'] = timestamp
|
|
|
|
|
|
|
|
tasker.create_task(task_path, track_task)
|
|
|
|
tasker.create_task(task_path, album_task)
|
|
|
|
tasker.create_task(task_path, artist_task)
|