migrated to fireo ORM
This commit is contained in:
parent
a47c1556f9
commit
bc1b59c7bd
@ -3,13 +3,11 @@ from flask import Blueprint, jsonify
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from google.cloud import firestore
|
|
||||||
from google.cloud import tasks_v2
|
from google.cloud import tasks_v2
|
||||||
|
|
||||||
from music.api.decorators import login_or_basic_auth, admin_required
|
from music.api.decorators import login_or_basic_auth, admin_required
|
||||||
|
|
||||||
blueprint = Blueprint('admin-api', __name__)
|
blueprint = Blueprint('admin-api', __name__)
|
||||||
db = firestore.Client()
|
|
||||||
|
|
||||||
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')
|
||||||
@ -20,7 +18,7 @@ logger = logging.getLogger(__name__)
|
|||||||
@blueprint.route('/tasks', methods=['GET'])
|
@blueprint.route('/tasks', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@admin_required
|
@admin_required
|
||||||
def get_tasks(username=None):
|
def get_tasks(user=None):
|
||||||
|
|
||||||
tasks = [i for i in tasker.list_tasks(task_path)]
|
tasks = [i for i in tasker.list_tasks(task_path)]
|
||||||
|
|
||||||
|
238
music/api/api.py
238
music/api/api.py
@ -1,58 +1,55 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
|
from google.cloud import firestore
|
||||||
|
from werkzeug.security import generate_password_hash
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from google.cloud import firestore
|
|
||||||
|
|
||||||
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 execute_all_user_playlists, execute_user_playlists, create_run_user_playlist_task, \
|
||||||
create_play_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.tasks.play_user_playlist import play_user_playlist as play_user_playlist
|
||||||
|
|
||||||
|
from music.model.user import User
|
||||||
|
from music.model.playlist import Playlist
|
||||||
|
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
|
|
||||||
blueprint = Blueprint('api', __name__)
|
blueprint = Blueprint('api', __name__)
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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(username=None):
|
def get_playlists(user=None):
|
||||||
|
assert user is not None
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'playlists': [i.to_dict() for i in database.get_user_playlists(username)]
|
'playlists': [i.to_dict() for i in Playlist.collection.parent(user.key).fetch()]
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
@blueprint.route('/playlist', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def playlist(username=None):
|
def playlist_route(user=None):
|
||||||
|
|
||||||
user_playlists = database.get_user_playlists(username)
|
|
||||||
|
|
||||||
user_ref = database.get_user(username).db_ref
|
|
||||||
playlists = user_ref.collection(u'playlists')
|
|
||||||
|
|
||||||
if request.method == 'GET' or request.method == 'DELETE':
|
if request.method == 'GET' or request.method == 'DELETE':
|
||||||
playlist_name = request.args.get('name', None)
|
playlist_name = request.args.get('name', None)
|
||||||
|
|
||||||
if playlist_name:
|
if playlist_name:
|
||||||
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
queried_playlist = next((i for i in user_playlists if i.name == playlist_name), None)
|
if playlist is None:
|
||||||
|
return jsonify({'error': f'playlist {playlist_name} not found'}), 404
|
||||||
if queried_playlist is None:
|
|
||||||
return jsonify({'error': 'no playlist found'}), 404
|
|
||||||
|
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return jsonify(queried_playlist.to_dict()), 200
|
return jsonify(playlist.to_dict()), 200
|
||||||
|
|
||||||
elif request.method == 'DELETE':
|
elif request.method == 'DELETE':
|
||||||
database.delete_playlist(username=username, name=playlist_name)
|
Playlist.collection.parent(user.key).delete(key=playlist.key)
|
||||||
return jsonify({"message": 'playlist deleted', "status": "success"}), 200
|
return jsonify({"message": 'playlist deleted', "status": "success"}), 200
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -75,9 +72,9 @@ def playlist(username=None):
|
|||||||
if request_json['playlist_references'] != -1:
|
if request_json['playlist_references'] != -1:
|
||||||
for i in request_json['playlist_references']:
|
for i in request_json['playlist_references']:
|
||||||
|
|
||||||
updating_playlist = database.get_playlist(username=username, name=i)
|
updating_playlist = Playlist.collection.parent(user.key).filter('name', '==', i).get()
|
||||||
if updating_playlist is not None:
|
if updating_playlist is not None:
|
||||||
playlist_references.append(updating_playlist.db_ref)
|
playlist_references.append(db.document(updating_playlist.key))
|
||||||
else:
|
else:
|
||||||
return jsonify({"message": f'managed playlist {i} not found', "status": "error"}), 400
|
return jsonify({"message": f'managed playlist {i} not found', "status": "error"}), 400
|
||||||
|
|
||||||
@ -100,168 +97,143 @@ def playlist(username=None):
|
|||||||
playlist_chart_range = request_json.get('chart_range', None)
|
playlist_chart_range = request_json.get('chart_range', None)
|
||||||
playlist_chart_limit = request_json.get('chart_limit', None)
|
playlist_chart_limit = request_json.get('chart_limit', None)
|
||||||
|
|
||||||
queried_playlist = [i for i in playlists.where(u'name', u'==', playlist_name).stream()]
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
if request.method == 'PUT':
|
if request.method == 'PUT':
|
||||||
|
|
||||||
if len(queried_playlist) != 0:
|
if playlist is not None:
|
||||||
return jsonify({'error': 'playlist already exists'}), 400
|
return jsonify({'error': 'playlist already exists'}), 400
|
||||||
|
|
||||||
from music.tasks.create_playlist import create_playlist as create_playlist
|
from music.tasks.create_playlist import create_playlist as create_playlist
|
||||||
|
|
||||||
to_add = {
|
new_db_playlist = Playlist(parent=user.key)
|
||||||
'name': playlist_name,
|
|
||||||
'parts': playlist_parts if playlist_parts is not None else [],
|
|
||||||
'playlist_references': playlist_references if playlist_references is not None else [],
|
|
||||||
'include_library_tracks': playlist_library_tracks if playlist_library_tracks is not None else False,
|
|
||||||
'include_recommendations': playlist_recommendation if playlist_recommendation is not None else False,
|
|
||||||
'recommendation_sample': playlist_recommendation_sample if playlist_recommendation_sample is not None else 10,
|
|
||||||
'uri': None,
|
|
||||||
'shuffle': playlist_shuffle if playlist_shuffle is not None else False,
|
|
||||||
'type': playlist_type if playlist_type is not None else 'default',
|
|
||||||
'last_updated': datetime.utcnow(),
|
|
||||||
|
|
||||||
'lastfm_stat_count': 0,
|
new_db_playlist.name = playlist_name
|
||||||
'lastfm_stat_album_count': 0,
|
new_db_playlist.parts = playlist_parts
|
||||||
'lastfm_stat_artist_count': 0,
|
new_db_playlist.playlist_references = playlist_references
|
||||||
|
|
||||||
'lastfm_stat_percent': 0,
|
new_db_playlist.include_library_tracks = playlist_library_tracks
|
||||||
'lastfm_stat_album_percent': 0,
|
new_db_playlist.include_recommendations = playlist_recommendation
|
||||||
'lastfm_stat_artist_percent': 0,
|
new_db_playlist.recommendation_sample = playlist_recommendation_sample
|
||||||
|
|
||||||
'lastfm_stat_last_refresh': datetime.utcnow(),
|
new_db_playlist.shuffle = playlist_shuffle
|
||||||
}
|
|
||||||
|
|
||||||
if user_ref.get().to_dict()['spotify_linked']:
|
new_db_playlist.type = playlist_type
|
||||||
new_playlist = create_playlist(username, playlist_name)
|
new_db_playlist.last_updated = datetime.utcnow()
|
||||||
to_add['uri'] = str(new_playlist.uri) if new_playlist is not None else None
|
new_db_playlist.lastfm_stat_last_refresh = datetime.utcnow()
|
||||||
|
|
||||||
if playlist_type == 'recents':
|
new_db_playlist.day_boundary = playlist_day_boundary
|
||||||
to_add['day_boundary'] = playlist_day_boundary if playlist_day_boundary is not None else 21
|
new_db_playlist.add_this_month = playlist_add_this_month
|
||||||
to_add['add_this_month'] = playlist_add_this_month if playlist_add_this_month is not None else False
|
new_db_playlist.add_last_month = playlist_add_last_month
|
||||||
to_add['add_last_month'] = playlist_add_last_month if playlist_add_last_month is not None else False
|
|
||||||
|
|
||||||
if playlist_type == 'fmchart':
|
new_db_playlist.chart_range = playlist_chart_range
|
||||||
to_add['chart_range'] = playlist_chart_range
|
new_db_playlist.chart_limit = playlist_chart_limit
|
||||||
to_add['chart_limit'] = playlist_chart_limit if playlist_chart_limit is not None else 50
|
|
||||||
|
|
||||||
playlists.document().set(to_add)
|
if user.spotify_linked:
|
||||||
logger.info(f'added {username} / {playlist_name}')
|
new_playlist = create_playlist(user, playlist_name)
|
||||||
|
new_db_playlist.uri = str(new_playlist.uri)
|
||||||
|
|
||||||
|
new_db_playlist.save()
|
||||||
|
logger.info(f'added {user.username} / {playlist_name}')
|
||||||
|
|
||||||
return jsonify({"message": 'playlist added', "status": "success"}), 201
|
return jsonify({"message": 'playlist added', "status": "success"}), 201
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
if len(queried_playlist) == 0:
|
if playlist is None:
|
||||||
return jsonify({'error': "playlist doesn't exist"}), 400
|
return jsonify({'error': "playlist doesn't exist"}), 400
|
||||||
|
|
||||||
if len(queried_playlist) > 1:
|
updating_playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
return jsonify({'error': "multiple playlists exist"}), 500
|
|
||||||
|
|
||||||
updating_playlist = database.get_playlist(username=username, name=playlist_name)
|
|
||||||
|
|
||||||
dic = {}
|
|
||||||
|
|
||||||
if playlist_parts is not None:
|
if playlist_parts is not None:
|
||||||
if playlist_parts == -1:
|
if playlist_parts == -1:
|
||||||
dic['parts'] = []
|
updating_playlist.parts = []
|
||||||
else:
|
else:
|
||||||
dic['parts'] = playlist_parts
|
updating_playlist.parts = playlist_parts
|
||||||
|
|
||||||
if playlist_references is not None:
|
if playlist_references is not None:
|
||||||
if playlist_references == -1:
|
if playlist_references == -1:
|
||||||
dic['playlist_references'] = []
|
updating_playlist.playlist_references = []
|
||||||
else:
|
else:
|
||||||
dic['playlist_references'] = playlist_references
|
updating_playlist.playlist_references = playlist_references
|
||||||
|
|
||||||
if playlist_uri is not None:
|
if playlist_uri is not None:
|
||||||
dic['uri'] = playlist_uri
|
updating_playlist.uri = playlist_uri
|
||||||
|
|
||||||
if playlist_shuffle is not None:
|
if playlist_shuffle is not None:
|
||||||
dic['shuffle'] = playlist_shuffle
|
updating_playlist.shuffle = playlist_shuffle
|
||||||
|
|
||||||
if playlist_day_boundary is not None:
|
if playlist_day_boundary is not None:
|
||||||
dic['day_boundary'] = playlist_day_boundary
|
updating_playlist.day_boundary = playlist_day_boundary
|
||||||
|
|
||||||
if playlist_add_this_month is not None:
|
if playlist_add_this_month is not None:
|
||||||
dic['add_this_month'] = playlist_add_this_month
|
updating_playlist.add_this_month = playlist_add_this_month
|
||||||
|
|
||||||
if playlist_add_last_month is not None:
|
if playlist_add_last_month is not None:
|
||||||
dic['add_last_month'] = playlist_add_last_month
|
updating_playlist.add_last_month = playlist_add_last_month
|
||||||
|
|
||||||
if playlist_library_tracks is not None:
|
if playlist_library_tracks is not None:
|
||||||
dic['include_library_tracks'] = playlist_library_tracks
|
updating_playlist.include_library_tracks = playlist_library_tracks
|
||||||
|
|
||||||
if playlist_recommendation is not None:
|
if playlist_recommendation is not None:
|
||||||
dic['include_recommendations'] = playlist_recommendation
|
updating_playlist.include_recommendations = playlist_recommendation
|
||||||
|
|
||||||
if playlist_recommendation_sample is not None:
|
if playlist_recommendation_sample is not None:
|
||||||
dic['recommendation_sample'] = playlist_recommendation_sample
|
updating_playlist.recommendation_sample = playlist_recommendation_sample
|
||||||
|
|
||||||
if playlist_chart_range is not None:
|
if playlist_chart_range is not None:
|
||||||
dic['chart_range'] = playlist_chart_range
|
updating_playlist.chart_range = playlist_chart_range
|
||||||
|
|
||||||
if playlist_chart_limit is not None:
|
if playlist_chart_limit is not None:
|
||||||
dic['chart_limit'] = playlist_chart_limit
|
updating_playlist.chart_limit = playlist_chart_limit
|
||||||
|
|
||||||
if playlist_type is not None:
|
if playlist_type is not None:
|
||||||
dic['type'] = playlist_type
|
# TODO check acceptable value
|
||||||
|
updating_playlist.type = playlist_type
|
||||||
|
|
||||||
if playlist_type == 'fmchart':
|
updating_playlist.update()
|
||||||
dic['chart_range'] = 'YEAR'
|
logger.info(f'updated {user.username} / {playlist_name}')
|
||||||
dic['chart_limit'] = 50
|
|
||||||
|
|
||||||
if len(dic) == 0:
|
|
||||||
logger.warning(f'no changes to make for {username} / {playlist_name}')
|
|
||||||
return jsonify({"message": 'no changes to make', "status": "error"}), 400
|
|
||||||
|
|
||||||
updating_playlist.update_database(dic)
|
|
||||||
logger.info(f'updated {username} / {playlist_name}')
|
|
||||||
|
|
||||||
return jsonify({"message": 'playlist updated', "status": "success"}), 200
|
return jsonify({"message": 'playlist updated', "status": "success"}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/user', methods=['GET', 'POST'])
|
@blueprint.route('/user', methods=['GET', 'POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def user(username=None):
|
def user_route(user=None):
|
||||||
|
assert user is not None
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
|
return jsonify(user.to_dict()), 200
|
||||||
|
|
||||||
database_user = database.get_user(username)
|
else: # POST
|
||||||
return jsonify(database_user.to_dict()), 200
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
db_user = database.get_user(username)
|
|
||||||
|
|
||||||
if db_user.user_type != db_user.Type.admin:
|
|
||||||
return jsonify({'status': 'error', 'message': 'unauthorized'}), 401
|
|
||||||
|
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
if 'username' in request_json:
|
if 'username' in request_json:
|
||||||
username = request_json['username']
|
if request_json['username'].strip().lower() != user.username:
|
||||||
|
if user.type != "admin":
|
||||||
|
return jsonify({'status': 'error', 'message': 'unauthorized'}), 401
|
||||||
|
|
||||||
actionable_user = database.get_user(username)
|
user = User.collection.filter('username', '==', request_json['username'].strip().lower()).get()
|
||||||
|
|
||||||
if 'locked' in request_json:
|
if 'locked' in request_json:
|
||||||
logger.info(f'updating lock {username} / {request_json["locked"]}')
|
if user.type == "admin":
|
||||||
actionable_user.locked = request_json['locked']
|
logger.info(f'updating lock {user.username} / {request_json["locked"]}')
|
||||||
|
user.locked = request_json['locked']
|
||||||
|
|
||||||
if 'spotify_linked' in request_json:
|
if 'spotify_linked' in request_json:
|
||||||
logger.info(f'deauthing {username}')
|
logger.info(f'deauthing {user.username}')
|
||||||
if request_json['spotify_linked'] is False:
|
if request_json['spotify_linked'] is False:
|
||||||
actionable_user.update_database({
|
user.access_token = None
|
||||||
'access_token': None,
|
user.refresh_token = None
|
||||||
'refresh_token': None,
|
user.spotify_linked = False
|
||||||
'spotify_linked': False
|
|
||||||
})
|
|
||||||
|
|
||||||
if 'lastfm_username' in request_json:
|
if 'lastfm_username' in request_json:
|
||||||
logger.info(f'updating lastfm username {username} -> {request_json["lastfm_username"]}')
|
logger.info(f'updating lastfm username {user.username} -> {request_json["lastfm_username"]}')
|
||||||
actionable_user.lastfm_username = request_json['lastfm_username']
|
user.lastfm_username = request_json['lastfm_username']
|
||||||
|
|
||||||
logger.info(f'updated {username}')
|
user.update()
|
||||||
|
|
||||||
|
logger.info(f'updated {user.username}')
|
||||||
|
|
||||||
return jsonify({'message': 'account updated', 'status': 'succeeded'}), 200
|
return jsonify({'message': 'account updated', 'status': 'succeeded'}), 200
|
||||||
|
|
||||||
@ -269,16 +241,15 @@ def user(username=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(username=None):
|
def users(user=None):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'accounts': [i.to_dict() for i in database.get_users()]
|
'accounts': [i.to_dict() for i in User.collection.fetch()]
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/user/password', methods=['POST'])
|
@blueprint.route('/user/password', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def change_password(username=None):
|
def change_password(user=None):
|
||||||
|
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
if 'new_password' in request_json and 'current_password' in request_json:
|
if 'new_password' in request_json and 'current_password' in request_json:
|
||||||
@ -289,14 +260,14 @@ def change_password(username=None):
|
|||||||
if len(request_json['new_password']) > 30:
|
if len(request_json['new_password']) > 30:
|
||||||
return jsonify({"error": 'password too long'}), 400
|
return jsonify({"error": 'password too long'}), 400
|
||||||
|
|
||||||
db_user = database.get_user(username)
|
if user.check_password(request_json['current_password']):
|
||||||
if db_user.check_password(request_json['current_password']):
|
user.password = generate_password_hash(request_json['new_password'])
|
||||||
db_user.password = request_json['new_password']
|
user.update()
|
||||||
logger.info(f'password udpated {username}')
|
logger.info(f'password udpated {user.username}')
|
||||||
|
|
||||||
return jsonify({"message": 'password changed', "status": "success"}), 200
|
return jsonify({"message": 'password changed', "status": "success"}), 200
|
||||||
else:
|
else:
|
||||||
logger.warning(f"incorrect password {username}")
|
logger.warning(f"incorrect password {user.username}")
|
||||||
return jsonify({'error': 'wrong password provided'}), 401
|
return jsonify({'error': 'wrong password provided'}), 401
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -305,7 +276,7 @@ def change_password(username=None):
|
|||||||
|
|
||||||
@blueprint.route('/playlist/play', methods=['POST'])
|
@blueprint.route('/playlist/play', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def play_playlist(username=None):
|
def play_playlist(user=None):
|
||||||
|
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
@ -321,12 +292,12 @@ def play_playlist(username=None):
|
|||||||
|
|
||||||
request_device_name = request_json.get('device_name', None)
|
request_device_name = request_json.get('device_name', None)
|
||||||
|
|
||||||
logger.info(f'playing {username}')
|
logger.info(f'playing {user.username}')
|
||||||
|
|
||||||
if (request_parts and len(request_parts) > 0) or (request_playlists and len(request_playlists) > 0):
|
if (request_parts and len(request_parts) > 0) or (request_playlists and len(request_playlists) > 0):
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
create_play_user_playlist_task(username,
|
create_play_user_playlist_task(user.username,
|
||||||
parts=request_parts,
|
parts=request_parts,
|
||||||
playlist_type=request_playlist_type,
|
playlist_type=request_playlist_type,
|
||||||
playlists=request_playlists,
|
playlists=request_playlists,
|
||||||
@ -338,7 +309,7 @@ def play_playlist(username=None):
|
|||||||
add_last_month=request_add_last_month,
|
add_last_month=request_add_last_month,
|
||||||
device_name=request_device_name)
|
device_name=request_device_name)
|
||||||
else:
|
else:
|
||||||
play_user_playlist(username,
|
play_user_playlist(user.username,
|
||||||
parts=request_parts,
|
parts=request_parts,
|
||||||
playlist_type=request_playlist_type,
|
playlist_type=request_playlist_type,
|
||||||
playlists=request_playlists,
|
playlists=request_playlists,
|
||||||
@ -352,7 +323,7 @@ def play_playlist(username=None):
|
|||||||
|
|
||||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
||||||
else:
|
else:
|
||||||
logger.error(f'no playlists/parts {username}')
|
logger.error(f'no playlists/parts {user.username}')
|
||||||
return jsonify({'error': 'insufficient playlist sources'}), 400
|
return jsonify({'error': 'insufficient playlist sources'}), 400
|
||||||
|
|
||||||
|
|
||||||
@ -381,16 +352,16 @@ def play_playlist_task():
|
|||||||
|
|
||||||
@blueprint.route('/playlist/run', methods=['GET'])
|
@blueprint.route('/playlist/run', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def run_playlist(username=None):
|
def run_playlist(user=None):
|
||||||
|
|
||||||
playlist_name = request.args.get('name', None)
|
playlist_name = request.args.get('name', 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(username, playlist_name)
|
create_run_user_playlist_task(user.username, playlist_name)
|
||||||
else:
|
else:
|
||||||
run_user_playlist(username, playlist_name)
|
run_user_playlist(user.username, playlist_name)
|
||||||
|
|
||||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
||||||
|
|
||||||
@ -416,13 +387,12 @@ def run_playlist_task():
|
|||||||
|
|
||||||
@blueprint.route('/playlist/run/user', methods=['GET'])
|
@blueprint.route('/playlist/run/user', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def run_user(username=None):
|
def run_user(user=None):
|
||||||
|
|
||||||
db_user = database.get_user(username)
|
if user.type == 'admin':
|
||||||
if db_user.user_type == db_user.Type.admin:
|
user_name = request.args.get('username', user.username)
|
||||||
user_name = request.args.get('username', username)
|
|
||||||
else:
|
else:
|
||||||
user_name = username
|
user_name = user.username
|
||||||
|
|
||||||
execute_user_playlists(user_name)
|
execute_user_playlists(user_name)
|
||||||
|
|
||||||
@ -442,7 +412,7 @@ def run_user_task():
|
|||||||
@blueprint.route('/playlist/run/users', methods=['GET'])
|
@blueprint.route('/playlist/run/users', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@admin_required
|
@admin_required
|
||||||
def run_users(username=None):
|
def run_users(user=None):
|
||||||
|
|
||||||
execute_all_user_playlists()
|
execute_all_user_playlists()
|
||||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||||
@ -458,18 +428,18 @@ def run_users_cron():
|
|||||||
|
|
||||||
@blueprint.route('/playlist/image', methods=['GET'])
|
@blueprint.route('/playlist/image', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def image(username=None):
|
def image(user=None):
|
||||||
|
|
||||||
name = request.args.get('name', None)
|
name = request.args.get('name', None)
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
return jsonify({'error': "no name provided"}), 400
|
return jsonify({'error': "no name provided"}), 400
|
||||||
|
|
||||||
_playlist = database.get_playlist(name=name, username=username)
|
_playlist = Playlist.collection.parent(user.key).filter('name', '==', name).get()
|
||||||
if _playlist is None:
|
if _playlist is None:
|
||||||
return jsonify({'error': "playlist not found"}), 404
|
return jsonify({'error': "playlist not found"}), 404
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(username=username)
|
net = database.get_authed_spotify_network(user)
|
||||||
|
|
||||||
spotify_playlist = net.get_playlist(uri_string=_playlist.uri)
|
spotify_playlist = net.get_playlist(uri_string=_playlist.uri)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
|
|
||||||
from flask import session, request, jsonify
|
from flask import session, request, jsonify
|
||||||
|
|
||||||
from music.db import database as database
|
from music.model.user import User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -18,17 +18,24 @@ def is_logged_in():
|
|||||||
def is_basic_authed():
|
def is_basic_authed():
|
||||||
if request.authorization:
|
if request.authorization:
|
||||||
if request.authorization.get('username', None) and request.authorization.get('password', None):
|
if request.authorization.get('username', None) and request.authorization.get('password', None):
|
||||||
if database.get_user(request.authorization.username).check_password(request.authorization.password):
|
user = User.collection.filter('username', '==', request.authorization.username.strip().lower()).get()
|
||||||
return True
|
if user is None:
|
||||||
|
return False, None
|
||||||
|
|
||||||
return False
|
if user.check_password(request.authorization.password):
|
||||||
|
return True, user
|
||||||
|
else:
|
||||||
|
return False, user
|
||||||
|
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
def login_required(func):
|
def login_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def login_required_wrapper(*args, **kwargs):
|
def login_required_wrapper(*args, **kwargs):
|
||||||
if is_logged_in():
|
if is_logged_in():
|
||||||
return func(username=session['username'], *args, **kwargs)
|
user = User.collection.filter('username', '==', session['username'].strip().lower()).get()
|
||||||
|
return func(user=user, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
logger.warning('user not logged in')
|
logger.warning('user not logged in')
|
||||||
return jsonify({'error': 'not logged in'}), 401
|
return jsonify({'error': 'not logged in'}), 401
|
||||||
@ -39,12 +46,15 @@ def login_or_basic_auth(func):
|
|||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def login_or_basic_auth_wrapper(*args, **kwargs):
|
def login_or_basic_auth_wrapper(*args, **kwargs):
|
||||||
if is_logged_in():
|
if is_logged_in():
|
||||||
return func(username=session['username'], *args, **kwargs)
|
user = User.collection.filter('username', '==', session['username'].strip().lower()).get()
|
||||||
elif is_basic_authed():
|
return func(user=user, *args, **kwargs)
|
||||||
return func(username=request.authorization.username, *args, **kwargs)
|
|
||||||
else:
|
else:
|
||||||
logger.warning('user not logged in')
|
check, user = is_basic_authed()
|
||||||
return jsonify({'error': 'not logged in'}), 401
|
if check:
|
||||||
|
return func(user=user, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.warning('user not logged in')
|
||||||
|
return jsonify({'error': 'not logged in'}), 401
|
||||||
|
|
||||||
return login_or_basic_auth_wrapper
|
return login_or_basic_auth_wrapper
|
||||||
|
|
||||||
@ -52,10 +62,10 @@ def login_or_basic_auth(func):
|
|||||||
def admin_required(func):
|
def admin_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def admin_required_wrapper(*args, **kwargs):
|
def admin_required_wrapper(*args, **kwargs):
|
||||||
db_user = database.get_user(kwargs.get('username'))
|
db_user = kwargs.get('user')
|
||||||
|
|
||||||
if db_user is not None:
|
if db_user is not None:
|
||||||
if db_user.user_type == db_user.Type.admin:
|
if db_user.type == 'admin':
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
logger.warning(f'{db_user.username} not authorized')
|
logger.warning(f'{db_user.username} not authorized')
|
||||||
@ -70,7 +80,7 @@ def admin_required(func):
|
|||||||
def spotify_link_required(func):
|
def spotify_link_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def spotify_link_required_wrapper(*args, **kwargs):
|
def spotify_link_required_wrapper(*args, **kwargs):
|
||||||
db_user = database.get_user(kwargs.get('username'))
|
db_user = kwargs.get('user')
|
||||||
|
|
||||||
if db_user is not None:
|
if db_user is not None:
|
||||||
if db_user.spotify_linked:
|
if db_user.spotify_linked:
|
||||||
@ -88,7 +98,7 @@ def spotify_link_required(func):
|
|||||||
def lastfm_username_required(func):
|
def lastfm_username_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def lastfm_username_required_wrapper(*args, **kwargs):
|
def lastfm_username_required_wrapper(*args, **kwargs):
|
||||||
db_user = database.get_user(kwargs.get('username'))
|
db_user = kwargs.get('user')
|
||||||
|
|
||||||
if db_user is not None:
|
if db_user is not None:
|
||||||
if db_user.lastfm_username and len(db_user.lastfm_username) > 0:
|
if db_user.lastfm_username and len(db_user.lastfm_username) > 0:
|
||||||
|
@ -13,9 +13,9 @@ logger = logging.getLogger(__name__)
|
|||||||
@blueprint.route('/today', methods=['GET'])
|
@blueprint.route('/today', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@lastfm_username_required
|
@lastfm_username_required
|
||||||
def daily_scrobbles(username=None):
|
def daily_scrobbles(user=None):
|
||||||
|
|
||||||
net = database.get_authed_lastfm_network(username)
|
net = database.get_authed_lastfm_network(user)
|
||||||
|
|
||||||
total = net.get_scrobble_count_from_date(input_date=date.today())
|
total = net.get_scrobble_count_from_date(input_date=date.today())
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
|||||||
@blueprint.route('/play', methods=['POST'])
|
@blueprint.route('/play', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def play(username=None):
|
def play(user=None):
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
if 'uri' in request_json:
|
if 'uri' in request_json:
|
||||||
@ -27,7 +27,7 @@ def play(username=None):
|
|||||||
if uri.object_type in [Uri.ObjectType.album, Uri.ObjectType.artist, Uri.ObjectType.playlist]:
|
if uri.object_type in [Uri.ObjectType.album, Uri.ObjectType.artist, Uri.ObjectType.playlist]:
|
||||||
context = Context(uri)
|
context = Context(uri)
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
|
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
player.play(context=context, device_name=request_json.get('device_name', None))
|
player.play(context=context, device_name=request_json.get('device_name', None))
|
||||||
@ -39,7 +39,7 @@ def play(username=None):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return jsonify({'error': "malformed uri provided"}), 400
|
return jsonify({'error': "malformed uri provided"}), 400
|
||||||
elif 'playlist_name' in request_json:
|
elif 'playlist_name' in request_json:
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
playlists = net.get_playlists()
|
playlists = net.get_playlists()
|
||||||
if playlists is not None:
|
if playlists is not None:
|
||||||
playlist_to_play = next((i for i in playlists if i.name == request_json['playlist_name']), None)
|
playlist_to_play = next((i for i in playlists if i.name == request_json['playlist_name']), None)
|
||||||
@ -60,7 +60,7 @@ def play(username=None):
|
|||||||
uris = [SpotifyTrack.wrap(uri=i) for i in uris if i.object_type == Uri.ObjectType.track]
|
uris = [SpotifyTrack.wrap(uri=i) for i in uris if i.object_type == Uri.ObjectType.track]
|
||||||
|
|
||||||
if len(uris) > 0:
|
if len(uris) > 0:
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
|
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
player.play(tracks=uris, device_name=request_json.get('device_name', None))
|
player.play(tracks=uris, device_name=request_json.get('device_name', None))
|
||||||
@ -78,8 +78,8 @@ def play(username=None):
|
|||||||
@blueprint.route('/next', methods=['POST'])
|
@blueprint.route('/next', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def next_track(username=None):
|
def next_track(user=None):
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.next()
|
player.next()
|
||||||
@ -89,12 +89,12 @@ def next_track(username=None):
|
|||||||
@blueprint.route('/shuffle', methods=['POST'])
|
@blueprint.route('/shuffle', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def shuffle(username=None):
|
def shuffle(user=None):
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
if 'state' in request_json:
|
if 'state' in request_json:
|
||||||
if isinstance(request_json['state'], bool):
|
if isinstance(request_json['state'], bool):
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.shuffle(state=request_json['state'])
|
player.shuffle(state=request_json['state'])
|
||||||
@ -108,13 +108,13 @@ def shuffle(username=None):
|
|||||||
@blueprint.route('/volume', methods=['POST'])
|
@blueprint.route('/volume', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def volume(username=None):
|
def volume(user=None):
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
if 'volume' in request_json:
|
if 'volume' in request_json:
|
||||||
if isinstance(request_json['volume'], int):
|
if isinstance(request_json['volume'], int):
|
||||||
if 0 <= request_json['volume'] <= 100:
|
if 0 <= request_json['volume'] <= 100:
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.volume(value=request_json['volume'])
|
player.volume(value=request_json['volume'])
|
||||||
|
@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
|||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
@lastfm_username_required
|
@lastfm_username_required
|
||||||
def count(username=None):
|
def count(user=None):
|
||||||
|
|
||||||
uri = request.args.get('uri', None)
|
uri = request.args.get('uri', None)
|
||||||
playlist_name = request.args.get('playlist_name', None)
|
playlist_name = request.args.get('playlist_name', None)
|
||||||
@ -35,8 +35,8 @@ def count(username=None):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return jsonify({'error': 'malformed uri provided'}), 401
|
return jsonify({'error': 'malformed uri provided'}), 401
|
||||||
|
|
||||||
spotnet = database.get_authed_spotify_network(username)
|
spotnet = database.get_authed_spotify_network(user)
|
||||||
fmnet = database.get_authed_lastfm_network(username)
|
fmnet = database.get_authed_lastfm_network(user)
|
||||||
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
||||||
|
|
||||||
if uri:
|
if uri:
|
||||||
@ -67,18 +67,18 @@ def count(username=None):
|
|||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
@lastfm_username_required
|
@lastfm_username_required
|
||||||
def playlist_refresh(username=None):
|
def playlist_refresh(user=None):
|
||||||
|
|
||||||
playlist_name = request.args.get('name', None)
|
playlist_name = request.args.get('name', 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(username, playlist_name)
|
create_refresh_playlist_task(user.username, playlist_name)
|
||||||
else:
|
else:
|
||||||
refresh_lastfm_track_stats(username, playlist_name)
|
refresh_lastfm_track_stats(user.username, playlist_name)
|
||||||
refresh_lastfm_album_stats(username, playlist_name)
|
refresh_lastfm_album_stats(user.username, playlist_name)
|
||||||
refresh_lastfm_artist_stats(username, playlist_name)
|
refresh_lastfm_artist_stats(user.username, playlist_name)
|
||||||
|
|
||||||
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
return jsonify({'message': 'execution requested', 'status': 'success'}), 200
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ def run_playlist_artist_task():
|
|||||||
@blueprint.route('/playlist/refresh/users', methods=['GET'])
|
@blueprint.route('/playlist/refresh/users', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@admin_required
|
@admin_required
|
||||||
def run_users(username=None):
|
def run_users(user=None):
|
||||||
execute_all_user_playlist_stats()
|
execute_all_user_playlist_stats()
|
||||||
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
return jsonify({'message': 'executed all users', 'status': 'success'}), 200
|
||||||
|
|
||||||
@ -149,13 +149,12 @@ def run_users_task():
|
|||||||
|
|
||||||
@blueprint.route('/playlist/refresh/user', methods=['GET'])
|
@blueprint.route('/playlist/refresh/user', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def run_user(username=None):
|
def run_user(user=None):
|
||||||
|
|
||||||
db_user = database.get_user(username)
|
if user.type == 'admin':
|
||||||
if db_user.type == db_user.Type.admin:
|
user_name = request.args.get('username', user.username)
|
||||||
user_name = request.args.get('username', username)
|
|
||||||
else:
|
else:
|
||||||
user_name = username
|
user_name = user.username
|
||||||
|
|
||||||
execute_user_playlist_stats(user_name)
|
execute_user_playlist_stats(user_name)
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ logger = logging.getLogger(__name__)
|
|||||||
@blueprint.route('/sort', methods=['POST'])
|
@blueprint.route('/sort', methods=['POST'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def play(username=None):
|
def play(user=None):
|
||||||
request_json = request.get_json()
|
request_json = request.get_json()
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
engine = PlaylistEngine(net)
|
engine = PlaylistEngine(net)
|
||||||
|
|
||||||
reverse = request_json.get('reverse', False)
|
reverse = request_json.get('reverse', False)
|
||||||
|
@ -2,40 +2,41 @@ from flask import Blueprint, jsonify, request
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import music.db.database as database
|
|
||||||
from music.api.decorators import login_or_basic_auth
|
from music.api.decorators import login_or_basic_auth
|
||||||
from music.cloud.function import update_tag
|
from music.cloud.function import update_tag
|
||||||
|
|
||||||
|
from music.model.tag import Tag
|
||||||
|
|
||||||
blueprint = Blueprint('task', __name__)
|
blueprint = Blueprint('task', __name__)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/tag', methods=['GET'])
|
@blueprint.route('/tag', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def tags(username=None):
|
def tags(user=None):
|
||||||
logger.info(f'retrieving tags for {username}')
|
logger.info(f'retrieving tags for {user.username}')
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'tags': [i.to_dict() for i in database.get_user_tags(username)]
|
'tags': [i.to_dict() for i in Tag.collection.parent(user.key).fetch()]
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/tag/<tag_id>', methods=['GET', 'PUT', 'POST', "DELETE"])
|
@blueprint.route('/tag/<tag_id>', methods=['GET', 'PUT', 'POST', "DELETE"])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def tag(tag_id, username=None):
|
def tag_route(tag_id, user=None):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return get_tag(tag_id, username)
|
return get_tag(tag_id, user)
|
||||||
elif request.method == 'PUT':
|
elif request.method == 'PUT':
|
||||||
return put_tag(tag_id, username)
|
return put_tag(tag_id, user)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
return post_tag(tag_id, username)
|
return post_tag(tag_id, user)
|
||||||
elif request.method == 'DELETE':
|
elif request.method == 'DELETE':
|
||||||
return delete_tag(tag_id, username)
|
return delete_tag(tag_id, user)
|
||||||
|
|
||||||
|
|
||||||
def get_tag(tag_id, username):
|
def get_tag(tag_id, user):
|
||||||
logger.info(f'retriving {tag_id} for {username}')
|
logger.info(f'retriving {tag_id} for {user.username}')
|
||||||
|
|
||||||
db_tag = database.get_tag(username=username, tag_id=tag_id)
|
db_tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
|
||||||
if db_tag is not None:
|
if db_tag is not None:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'tag': db_tag.to_dict()
|
'tag': db_tag.to_dict()
|
||||||
@ -44,10 +45,10 @@ def get_tag(tag_id, username):
|
|||||||
return jsonify({"error": 'tag not found'}), 404
|
return jsonify({"error": 'tag not found'}), 404
|
||||||
|
|
||||||
|
|
||||||
def put_tag(tag_id, username):
|
def put_tag(tag_id, user):
|
||||||
logger.info(f'updating {tag_id} for {username}')
|
logger.info(f'updating {tag_id} for {user.username}')
|
||||||
|
|
||||||
db_tag = database.get_tag(username=username, tag_id=tag_id)
|
db_tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
|
||||||
|
|
||||||
if db_tag is None:
|
if db_tag is None:
|
||||||
return jsonify({"error": 'tag not found'}), 404
|
return jsonify({"error": 'tag not found'}), 404
|
||||||
@ -92,34 +93,38 @@ def put_tag(tag_id, username):
|
|||||||
db_tag.artists = artists
|
db_tag.artists = artists
|
||||||
|
|
||||||
if update_required:
|
if update_required:
|
||||||
update_tag(username=username, tag_id=tag_id)
|
update_tag(username=user.username, tag_id=tag_id)
|
||||||
|
|
||||||
|
db_tag.update()
|
||||||
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
return jsonify({"message": 'tag updated', "status": "success"}), 200
|
||||||
|
|
||||||
|
|
||||||
def post_tag(tag_id, username):
|
def post_tag(tag_id, user):
|
||||||
logger.info(f'creating {tag_id} for {username}')
|
logger.info(f'creating {tag_id} for {user.username}')
|
||||||
|
|
||||||
tag_id = tag_id.replace(' ', '_')
|
tag_id = tag_id.replace(' ', '_')
|
||||||
|
|
||||||
database.create_tag(username=username, tag_id=tag_id)
|
tag = Tag(parent=user.key)
|
||||||
|
tag.tag_id = tag_id
|
||||||
|
tag.name = tag_id
|
||||||
|
tag.username = user.username
|
||||||
|
tag.save()
|
||||||
|
|
||||||
return jsonify({"message": 'tag added', "status": "success"}), 201
|
return jsonify({"message": 'tag added', "status": "success"}), 201
|
||||||
|
|
||||||
|
|
||||||
def delete_tag(tag_id, username):
|
def delete_tag(tag_id, user):
|
||||||
logger.info(f'deleting {tag_id} for {username}')
|
logger.info(f'deleting {tag_id} for {user.username}')
|
||||||
|
|
||||||
response = database.delete_tag(username=username, tag_id=tag_id)
|
db_tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
|
||||||
|
Tag.collection.parent(user.key).delete(key=db_tag.key)
|
||||||
|
|
||||||
if response is not None:
|
return jsonify({"message": 'tag deleted', "status": "success"}), 201
|
||||||
return jsonify({"message": 'tag deleted', "status": "success"}), 201
|
|
||||||
else:
|
|
||||||
return jsonify({"error": 'tag not deleted'}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/tag/<tag_id>/update', methods=['GET'])
|
@blueprint.route('/tag/<tag_id>/update', methods=['GET'])
|
||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
def tag_refresh(tag_id, username=None):
|
def tag_refresh(tag_id, user=None):
|
||||||
logger.info(f'updating {tag_id} tag for {username}')
|
logger.info(f'updating {tag_id} tag for {user.username}')
|
||||||
update_tag(username=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
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from flask import Blueprint, session, flash, request, redirect, url_for, render_template
|
from flask import Blueprint, session, flash, request, redirect, url_for, render_template
|
||||||
from google.cloud import firestore
|
from google.cloud import firestore
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
|
from music.model.user import User
|
||||||
|
|
||||||
import urllib
|
import urllib
|
||||||
import datetime
|
import datetime
|
||||||
@ -8,8 +9,6 @@ import logging
|
|||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import music.db.database as database
|
|
||||||
|
|
||||||
blueprint = Blueprint('authapi', __name__)
|
blueprint = Blueprint('authapi', __name__)
|
||||||
|
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
@ -31,8 +30,7 @@ def login():
|
|||||||
flash('malformed request')
|
flash('malformed request')
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
username = username.lower()
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
user = database.get_user(username)
|
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
flash('user not found')
|
flash('user not found')
|
||||||
@ -45,6 +43,7 @@ def login():
|
|||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
user.last_login = datetime.datetime.utcnow()
|
user.last_login = datetime.datetime.utcnow()
|
||||||
|
user.update()
|
||||||
|
|
||||||
logger.info(f'success {username}')
|
logger.info(f'success {username}')
|
||||||
session['username'] = username
|
session['username'] = username
|
||||||
@ -91,12 +90,17 @@ def register():
|
|||||||
flash('password mismatch')
|
flash('password mismatch')
|
||||||
return redirect('authapi.register')
|
return redirect('authapi.register')
|
||||||
|
|
||||||
if username in [i.to_dict()['username'] for i in
|
if username in [i.username for i in
|
||||||
db.collection(u'spotify_users').where(u'username', u'==', username).stream()]:
|
User.collection.fetch()]:
|
||||||
flash('username already registered')
|
flash('username already registered')
|
||||||
return redirect('authapi.register')
|
return redirect('authapi.register')
|
||||||
|
|
||||||
database.create_user(username=username, password=password)
|
user = User()
|
||||||
|
user.username = username
|
||||||
|
user.password = generate_password_hash(password)
|
||||||
|
user.last_login = datetime.utcnow()
|
||||||
|
|
||||||
|
user.save()
|
||||||
|
|
||||||
logger.info(f'new user {username}')
|
logger.info(f'new user {username}')
|
||||||
session['username'] = username
|
session['username'] = username
|
||||||
@ -150,15 +154,15 @@ def token():
|
|||||||
|
|
||||||
resp = req.json()
|
resp = req.json()
|
||||||
|
|
||||||
user = database.get_user(session['username'])
|
user = User.collection.filter('username', '==', session['username'].strip().lower()).get()
|
||||||
|
|
||||||
user.update_database({
|
user.access_token = resp['access_token']
|
||||||
'access_token': resp['access_token'],
|
user.refresh_token = resp['refresh_token']
|
||||||
'refresh_token': resp['refresh_token'],
|
user.last_refreshed = datetime.datetime.now(datetime.timezone.utc)
|
||||||
'last_refreshed': datetime.datetime.now(datetime.timezone.utc),
|
user.token_expiry = resp['expires_in']
|
||||||
'token_expiry': resp['expires_in'],
|
user.spotify_linked = True
|
||||||
'spotify_linked': True
|
|
||||||
})
|
user.update()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
flash('http error on token request')
|
flash('http error on token request')
|
||||||
@ -174,15 +178,15 @@ def deauth():
|
|||||||
|
|
||||||
if 'username' in session:
|
if 'username' in session:
|
||||||
|
|
||||||
user = database.get_user(session['username'])
|
user = User.collection.filter('username', '==', session['username'].strip().lower()).get()
|
||||||
|
|
||||||
user.update_database({
|
user.access_token = None
|
||||||
'access_token': None,
|
user.refresh_token = None
|
||||||
'refresh_token': None,
|
user.last_refreshed = datetime.datetime.now(datetime.timezone.utc)
|
||||||
'last_refreshed': datetime.datetime.now(datetime.timezone.utc),
|
user.token_expiry = None
|
||||||
'token_expiry': None,
|
user.spotify_linked = False
|
||||||
'spotify_linked': False
|
|
||||||
})
|
user.update()
|
||||||
|
|
||||||
return redirect('/app/settings/spotify')
|
return redirect('/app/settings/spotify')
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ 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.playlist import Playlist
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
@ -21,7 +24,7 @@ def execute_all_user_playlists():
|
|||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
logger.info('running')
|
logger.info('running')
|
||||||
|
|
||||||
for iter_user in database.get_users():
|
for iter_user in User.collection.fetch():
|
||||||
|
|
||||||
if iter_user.spotify_linked and not iter_user.locked:
|
if iter_user.spotify_linked and not iter_user.locked:
|
||||||
|
|
||||||
@ -45,14 +48,18 @@ def execute_all_user_playlists():
|
|||||||
|
|
||||||
|
|
||||||
def execute_user_playlists(username):
|
def execute_user_playlists(username):
|
||||||
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
|
|
||||||
playlists = database.get_user_playlists(username)
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
playlists = Playlist.collection.parent(user.key).fetch()
|
||||||
|
|
||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
logger.info(f'running {username}')
|
logger.info(f'running {username}')
|
||||||
|
|
||||||
for iterate_playlist in playlists:
|
for iterate_playlist in playlists:
|
||||||
if iterate_playlist.uri:
|
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)
|
create_run_user_playlist_task(username, iterate_playlist.name, seconds_delay)
|
||||||
@ -139,7 +146,7 @@ def execute_all_user_playlist_stats():
|
|||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
logger.info('running')
|
logger.info('running')
|
||||||
|
|
||||||
for iter_user in database.get_users():
|
for iter_user in User.collection.fetch():
|
||||||
|
|
||||||
if iter_user.spotify_linked and iter_user.lastfm_username and \
|
if iter_user.spotify_linked and iter_user.lastfm_username and \
|
||||||
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
len(iter_user.lastfm_username) > 0 and not iter_user.locked:
|
||||||
@ -157,15 +164,18 @@ def execute_all_user_playlist_stats():
|
|||||||
|
|
||||||
def execute_user_playlist_stats(username):
|
def execute_user_playlist_stats(username):
|
||||||
|
|
||||||
playlists = database.get_user_playlists(username)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
user = database.get_user(username)
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
playlists = Playlist.collection.parent(user.key).fetch()
|
||||||
|
|
||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
logger.info(f'running {username}')
|
logger.info(f'running {username}')
|
||||||
|
|
||||||
if user.lastfm_username and len(user.lastfm_username) > 0:
|
if user.lastfm_username and len(user.lastfm_username) > 0:
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
if playlist.uri:
|
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)
|
create_refresh_playlist_task(username, playlist.name, seconds_delay)
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
from google.cloud import firestore
|
from google.cloud import firestore
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta, datetime, timezone
|
from datetime import timedelta, datetime, timezone
|
||||||
from typing import List, Optional
|
|
||||||
from werkzeug.security import generate_password_hash
|
|
||||||
|
|
||||||
from spotframework.net.network import Network as SpotifyNetwork
|
from spotframework.net.network import Network as SpotifyNetwork
|
||||||
from fmframework.net.network import Network as FmNetwork
|
from fmframework.net.network import Network as FmNetwork
|
||||||
from music.db.user import DatabaseUser
|
from music.db.user import DatabaseUser
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.playlist import Playlist, RecentsPlaylist, LastFMChartPlaylist, Sort
|
|
||||||
from music.model.tag import Tag
|
|
||||||
|
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
|
|
||||||
@ -18,22 +14,23 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def refresh_token_database_callback(user):
|
def refresh_token_database_callback(user):
|
||||||
if isinstance(user, DatabaseUser):
|
if isinstance(user, DatabaseUser):
|
||||||
user_obj = get_user(user.user_id)
|
user_obj = User.collection.filter('username', '==', user.user_id.strip().lower()).get()
|
||||||
|
if user_obj is None:
|
||||||
|
logger.error(f'user {user} not found')
|
||||||
|
|
||||||
|
user_obj.access_token = user.access_token
|
||||||
|
user_obj.refresh_token = user.refresh_token
|
||||||
|
user_obj.last_refreshed = user.last_refreshed
|
||||||
|
user_obj.token_expiry = user.token_expiry
|
||||||
|
|
||||||
|
user_obj.update()
|
||||||
|
|
||||||
user_obj.update_database({
|
|
||||||
'access_token': user.access_token,
|
|
||||||
'refresh_token': user.refresh_token,
|
|
||||||
'last_refreshed': user.last_refreshed,
|
|
||||||
'token_expiry': user.token_expiry
|
|
||||||
})
|
|
||||||
logger.debug(f'{user.user_id} database entry updated')
|
logger.debug(f'{user.user_id} database entry updated')
|
||||||
else:
|
else:
|
||||||
logger.error('user has no attached id')
|
logger.error('user has no attached id')
|
||||||
|
|
||||||
|
|
||||||
def get_authed_spotify_network(username):
|
def get_authed_spotify_network(user):
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.spotify_linked:
|
if user.spotify_linked:
|
||||||
spotify_keys = db.document('key/spotify').get().to_dict()
|
spotify_keys = db.document('key/spotify').get().to_dict()
|
||||||
@ -41,7 +38,7 @@ def get_authed_spotify_network(username):
|
|||||||
user_obj = DatabaseUser(client_id=spotify_keys['clientid'],
|
user_obj = DatabaseUser(client_id=spotify_keys['clientid'],
|
||||||
client_secret=spotify_keys['clientsecret'],
|
client_secret=spotify_keys['clientsecret'],
|
||||||
refresh_token=user.refresh_token,
|
refresh_token=user.refresh_token,
|
||||||
user_id=username,
|
user_id=user.username,
|
||||||
access_token=user.access_token)
|
access_token=user.access_token)
|
||||||
user_obj.on_refresh.append(refresh_token_database_callback)
|
user_obj.on_refresh.append(refresh_token_database_callback)
|
||||||
|
|
||||||
@ -54,403 +51,15 @@ def get_authed_spotify_network(username):
|
|||||||
else:
|
else:
|
||||||
logger.error('user spotify not linked')
|
logger.error('user spotify not linked')
|
||||||
else:
|
else:
|
||||||
logger.error(f'user {username} not found')
|
logger.error(f'user {user.username} not found')
|
||||||
|
|
||||||
|
|
||||||
def get_authed_lastfm_network(username):
|
def get_authed_lastfm_network(user):
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
if user:
|
if user:
|
||||||
if user.lastfm_username:
|
if user.lastfm_username:
|
||||||
fm_keys = db.document('key/fm').get().to_dict()
|
fm_keys = db.document('key/fm').get().to_dict()
|
||||||
return FmNetwork(username=user.lastfm_username, api_key=fm_keys['clientid'])
|
return FmNetwork(username=user.lastfm_username, api_key=fm_keys['clientid'])
|
||||||
else:
|
else:
|
||||||
logger.error(f'{username} has no last.fm username')
|
logger.error(f'{user.username} has no last.fm username')
|
||||||
else:
|
else:
|
||||||
logger.error(f'user {username} not found')
|
logger.error(f'user {user.username} not found')
|
||||||
|
|
||||||
|
|
||||||
def get_users() -> List[User]:
|
|
||||||
logger.info('retrieving users')
|
|
||||||
return [parse_user_reference(user_snapshot=i) for i in db.collection(u'spotify_users').stream()]
|
|
||||||
|
|
||||||
|
|
||||||
def get_user(username: str) -> Optional[User]:
|
|
||||||
logger.info(f'retrieving {username}')
|
|
||||||
|
|
||||||
users = [i for i in db.collection(u'spotify_users').where(u'username', u'==', username).stream()]
|
|
||||||
|
|
||||||
if len(users) == 0:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
return None
|
|
||||||
if len(users) > 1:
|
|
||||||
logger.critical(f"multiple {username}'s found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
return parse_user_reference(user_snapshot=users[0])
|
|
||||||
|
|
||||||
|
|
||||||
def parse_user_reference(user_ref=None, user_snapshot=None) -> User:
|
|
||||||
if user_ref is None and user_snapshot is None:
|
|
||||||
raise ValueError('no user object supplied')
|
|
||||||
|
|
||||||
if user_ref is None:
|
|
||||||
user_ref = user_snapshot.reference
|
|
||||||
|
|
||||||
if user_snapshot is None:
|
|
||||||
user_snapshot = user_ref.get()
|
|
||||||
|
|
||||||
user_dict = user_snapshot.to_dict()
|
|
||||||
|
|
||||||
return User(username=user_dict.get('username'),
|
|
||||||
password=user_dict.get('password'),
|
|
||||||
db_ref=user_ref,
|
|
||||||
email=user_dict.get('email'),
|
|
||||||
user_type=User.Type[user_dict.get('type')],
|
|
||||||
last_login=user_dict.get('last_login'),
|
|
||||||
last_refreshed=user_dict.get('last_refreshed'),
|
|
||||||
locked=user_dict.get('locked'),
|
|
||||||
validated=user_dict.get('validated'),
|
|
||||||
|
|
||||||
spotify_linked=user_dict.get('spotify_linked'),
|
|
||||||
access_token=user_dict.get('access_token'),
|
|
||||||
refresh_token=user_dict.get('refresh_token'),
|
|
||||||
token_expiry=user_dict.get('token_expiry'),
|
|
||||||
lastfm_username=user_dict.get('lastfm_username'))
|
|
||||||
|
|
||||||
|
|
||||||
def update_user(username: str, updates: dict) -> None:
|
|
||||||
logger.debug(f'updating {username}')
|
|
||||||
|
|
||||||
users = [i for i in db.collection(u'spotify_users').where(u'username', u'==', username).stream()]
|
|
||||||
|
|
||||||
if len(users) == 0:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
return None
|
|
||||||
if len(users) > 1:
|
|
||||||
logger.critical(f"multiple {username}'s found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
user = users[0].reference
|
|
||||||
user.update(updates)
|
|
||||||
|
|
||||||
|
|
||||||
def create_user(username: str, password: str):
|
|
||||||
db.collection(u'spotify_users').add({
|
|
||||||
'access_token': None,
|
|
||||||
'email': None,
|
|
||||||
'last_login': datetime.utcnow(),
|
|
||||||
'last_refreshed': None,
|
|
||||||
'locked': False,
|
|
||||||
'password': generate_password_hash(password),
|
|
||||||
'refresh_token': None,
|
|
||||||
'spotify_linked': False,
|
|
||||||
'type': 'user',
|
|
||||||
'username': username,
|
|
||||||
'validated': True
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_playlists(username: str) -> List[Playlist]:
|
|
||||||
logger.info(f'getting playlists for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
if user:
|
|
||||||
playlist_refs = [i for i in user.db_ref.collection(u'playlists').stream()]
|
|
||||||
|
|
||||||
return [parse_playlist_reference(username=username, playlist_snapshot=i) for i in playlist_refs]
|
|
||||||
else:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
|
|
||||||
|
|
||||||
def get_playlist(username: str = None, name: str = None) -> Optional[Playlist]:
|
|
||||||
logger.info(f'retrieving {name} for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
if user:
|
|
||||||
|
|
||||||
playlists = [i for i in user.db_ref.collection(u'playlists').where(u'name', u'==', name).stream()]
|
|
||||||
|
|
||||||
if len(playlists) == 0:
|
|
||||||
logger.error(f'playlist {name} for {user} not found')
|
|
||||||
return None
|
|
||||||
if len(playlists) > 1:
|
|
||||||
logger.critical(f"multiple {name}'s for {user} found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
return parse_playlist_reference(username=username, playlist_snapshot=playlists[0])
|
|
||||||
else:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
|
|
||||||
|
|
||||||
def parse_playlist_reference(username, playlist_ref=None, playlist_snapshot=None) -> Playlist:
|
|
||||||
if playlist_ref is None and playlist_snapshot is None:
|
|
||||||
raise ValueError('no playlist object supplied')
|
|
||||||
|
|
||||||
if playlist_ref is None:
|
|
||||||
playlist_ref = playlist_snapshot.reference
|
|
||||||
|
|
||||||
if playlist_snapshot is None:
|
|
||||||
playlist_snapshot = playlist_ref.get()
|
|
||||||
|
|
||||||
playlist_dict = playlist_snapshot.to_dict()
|
|
||||||
|
|
||||||
if playlist_dict.get('type') == 'default':
|
|
||||||
return Playlist(uri=playlist_dict.get('uri'),
|
|
||||||
name=playlist_dict.get('name'),
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=playlist_ref,
|
|
||||||
|
|
||||||
include_recommendations=playlist_dict.get('include_recommendations', False),
|
|
||||||
recommendation_sample=playlist_dict.get('recommendation_sample', 0),
|
|
||||||
include_library_tracks=playlist_dict.get('include_library_tracks', False),
|
|
||||||
|
|
||||||
parts=playlist_dict.get('parts'),
|
|
||||||
playlist_references=playlist_dict.get('playlist_references'),
|
|
||||||
shuffle=playlist_dict.get('shuffle'),
|
|
||||||
|
|
||||||
sort=Sort[playlist_dict.get('sort', 'release_date')],
|
|
||||||
|
|
||||||
description_overwrite=playlist_dict.get('description_overwrite'),
|
|
||||||
description_suffix=playlist_dict.get('description_suffix'),
|
|
||||||
|
|
||||||
last_updated=playlist_dict.get('last_updated'),
|
|
||||||
|
|
||||||
lastfm_stat_count=playlist_dict.get('lastfm_stat_count', 0),
|
|
||||||
lastfm_stat_album_count=playlist_dict.get('lastfm_stat_album_count', 0),
|
|
||||||
lastfm_stat_artist_count=playlist_dict.get('lastfm_stat_artist_count', 0),
|
|
||||||
|
|
||||||
lastfm_stat_percent=playlist_dict.get('lastfm_stat_percent', 0),
|
|
||||||
lastfm_stat_album_percent=playlist_dict.get('lastfm_stat_album_percent', 0),
|
|
||||||
lastfm_stat_artist_percent=playlist_dict.get('lastfm_stat_artist_percent', 0),
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh=playlist_dict.get('lastfm_stat_last_refresh'))
|
|
||||||
|
|
||||||
elif playlist_dict.get('type') == 'recents':
|
|
||||||
return RecentsPlaylist(uri=playlist_dict.get('uri'),
|
|
||||||
name=playlist_dict.get('name'),
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=playlist_ref,
|
|
||||||
|
|
||||||
include_recommendations=playlist_dict.get('include_recommendations', False),
|
|
||||||
recommendation_sample=playlist_dict.get('recommendation_sample', 0),
|
|
||||||
include_library_tracks=playlist_dict.get('include_library_tracks', False),
|
|
||||||
|
|
||||||
parts=playlist_dict.get('parts'),
|
|
||||||
playlist_references=playlist_dict.get('playlist_references'),
|
|
||||||
shuffle=playlist_dict.get('shuffle'),
|
|
||||||
|
|
||||||
sort=Sort[playlist_dict.get('sort', 'release_date')],
|
|
||||||
|
|
||||||
description_overwrite=playlist_dict.get('description_overwrite'),
|
|
||||||
description_suffix=playlist_dict.get('description_suffix'),
|
|
||||||
|
|
||||||
last_updated=playlist_dict.get('last_updated'),
|
|
||||||
|
|
||||||
lastfm_stat_count=playlist_dict.get('lastfm_stat_count', 0),
|
|
||||||
lastfm_stat_album_count=playlist_dict.get('lastfm_stat_album_count', 0),
|
|
||||||
lastfm_stat_artist_count=playlist_dict.get('lastfm_stat_artist_count', 0),
|
|
||||||
|
|
||||||
lastfm_stat_percent=playlist_dict.get('lastfm_stat_percent', 0),
|
|
||||||
lastfm_stat_album_percent=playlist_dict.get('lastfm_stat_album_percent', 0),
|
|
||||||
lastfm_stat_artist_percent=playlist_dict.get('lastfm_stat_artist_percent', 0),
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh=playlist_dict.get('lastfm_stat_last_refresh'),
|
|
||||||
|
|
||||||
add_last_month=playlist_dict.get('add_last_month'),
|
|
||||||
add_this_month=playlist_dict.get('add_this_month'),
|
|
||||||
day_boundary=playlist_dict.get('day_boundary'))
|
|
||||||
|
|
||||||
elif playlist_dict.get('type') == 'fmchart':
|
|
||||||
return LastFMChartPlaylist(uri=playlist_dict.get('uri'),
|
|
||||||
name=playlist_dict.get('name'),
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=playlist_ref,
|
|
||||||
|
|
||||||
include_recommendations=playlist_dict.get('include_recommendations', False),
|
|
||||||
recommendation_sample=playlist_dict.get('recommendation_sample', 0),
|
|
||||||
include_library_tracks=playlist_dict.get('include_library_tracks', False),
|
|
||||||
|
|
||||||
parts=playlist_dict.get('parts'),
|
|
||||||
playlist_references=playlist_dict.get('playlist_references'),
|
|
||||||
shuffle=playlist_dict.get('shuffle'),
|
|
||||||
|
|
||||||
sort=Sort[playlist_dict.get('sort', 'release_date')],
|
|
||||||
|
|
||||||
description_overwrite=playlist_dict.get('description_overwrite'),
|
|
||||||
description_suffix=playlist_dict.get('description_suffix'),
|
|
||||||
|
|
||||||
last_updated=playlist_dict.get('last_updated'),
|
|
||||||
|
|
||||||
lastfm_stat_count=playlist_dict.get('lastfm_stat_count', 0),
|
|
||||||
lastfm_stat_album_count=playlist_dict.get('lastfm_stat_album_count', 0),
|
|
||||||
lastfm_stat_artist_count=playlist_dict.get('lastfm_stat_artist_count', 0),
|
|
||||||
|
|
||||||
lastfm_stat_percent=playlist_dict.get('lastfm_stat_percent', 0),
|
|
||||||
lastfm_stat_album_percent=playlist_dict.get('lastfm_stat_album_percent', 0),
|
|
||||||
lastfm_stat_artist_percent=playlist_dict.get('lastfm_stat_artist_percent', 0),
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh=playlist_dict.get('lastfm_stat_last_refresh'),
|
|
||||||
|
|
||||||
chart_limit=playlist_dict.get('chart_limit'),
|
|
||||||
chart_range=FmNetwork.Range[playlist_dict.get('chart_range')])
|
|
||||||
|
|
||||||
|
|
||||||
def update_playlist(username: str, name: str, updates: dict) -> None:
|
|
||||||
if len(updates) > 0:
|
|
||||||
logger.debug(f'updating {name} for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
playlists = [i for i in user.db_ref.collection(u'playlists').where(u'name', u'==', name).stream()]
|
|
||||||
|
|
||||||
if len(playlists) == 0:
|
|
||||||
logger.error(f'playlist {name} for {username} not found')
|
|
||||||
return None
|
|
||||||
if len(playlists) > 1:
|
|
||||||
logger.critical(f"multiple {name}'s for {username} found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
playlist = playlists[0].reference
|
|
||||||
playlist.update(updates)
|
|
||||||
else:
|
|
||||||
logger.debug(f'nothing to update for {name} for {username}')
|
|
||||||
|
|
||||||
|
|
||||||
def delete_playlist(username: str, name: str) -> None:
|
|
||||||
logger.info(f'deleting {name} for {username}')
|
|
||||||
|
|
||||||
playlist = get_playlist(username=username, name=name)
|
|
||||||
|
|
||||||
if playlist:
|
|
||||||
playlist.db_ref.delete()
|
|
||||||
else:
|
|
||||||
logger.error(f'playlist {name} not found for {username}')
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_tags(username: str) -> List[Tag]:
|
|
||||||
logger.info(f'getting tags for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
if user:
|
|
||||||
tag_refs = [i for i in user.db_ref.collection(u'tags').stream()]
|
|
||||||
|
|
||||||
return [parse_tag_reference(username=username, tag_snapshot=i) for i in tag_refs]
|
|
||||||
else:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
|
|
||||||
|
|
||||||
def get_tag(username: str = None, tag_id: str = None) -> Optional[Tag]:
|
|
||||||
logger.info(f'retrieving {tag_id} for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
if user:
|
|
||||||
|
|
||||||
tags = [i for i in user.db_ref.collection(u'tags').where(u'tag_id', u'==', tag_id).stream()]
|
|
||||||
|
|
||||||
if len(tags) == 0:
|
|
||||||
logger.error(f'tag {tag_id} for {user} not found')
|
|
||||||
return None
|
|
||||||
if len(tags) > 1:
|
|
||||||
logger.critical(f"multiple {tag_id}'s for {user} found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
return parse_tag_reference(username=username, tag_snapshot=tags[0])
|
|
||||||
else:
|
|
||||||
logger.error(f'user {username} not found')
|
|
||||||
|
|
||||||
|
|
||||||
def parse_tag_reference(username, tag_ref=None, tag_snapshot=None) -> Tag:
|
|
||||||
if tag_ref is None and tag_snapshot is None:
|
|
||||||
raise ValueError('no tag object supplied')
|
|
||||||
|
|
||||||
if tag_ref is None:
|
|
||||||
tag_ref = tag_snapshot.reference
|
|
||||||
|
|
||||||
if tag_snapshot is None:
|
|
||||||
tag_snapshot = tag_ref.get()
|
|
||||||
|
|
||||||
tag_dict = tag_snapshot.to_dict()
|
|
||||||
|
|
||||||
return Tag(tag_id=tag_dict['tag_id'],
|
|
||||||
name=tag_dict.get('name', 'n/a'),
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=tag_ref,
|
|
||||||
|
|
||||||
tracks=tag_dict.get('tracks', []),
|
|
||||||
albums=tag_dict.get('albums', []),
|
|
||||||
artists=tag_dict.get('artists', []),
|
|
||||||
|
|
||||||
count=tag_dict.get('count', 0),
|
|
||||||
proportion=tag_dict.get('proportion', 0.0),
|
|
||||||
total_user_scrobbles=tag_dict.get('total_user_scrobbles', 0),
|
|
||||||
|
|
||||||
last_updated=tag_dict.get('last_updated'))
|
|
||||||
|
|
||||||
|
|
||||||
def update_tag(username: str, tag_id: str, updates: dict) -> None:
|
|
||||||
if len(updates) > 0:
|
|
||||||
logger.debug(f'updating {tag_id} for {username}')
|
|
||||||
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
tags = [i for i in user.db_ref.collection(u'tags').where(u'tag_id', u'==', tag_id).stream()]
|
|
||||||
|
|
||||||
if len(tags) == 0:
|
|
||||||
logger.error(f'tag {tag_id} for {username} not found')
|
|
||||||
return None
|
|
||||||
if len(tags) > 1:
|
|
||||||
logger.critical(f"multiple {tag_id}'s for {username} found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
tag = tags[0].reference
|
|
||||||
tag.update(updates)
|
|
||||||
else:
|
|
||||||
logger.debug(f'nothing to update for {tag_id} for {username}')
|
|
||||||
|
|
||||||
|
|
||||||
def delete_tag(username: str, tag_id: str) -> bool:
|
|
||||||
logger.info(f'deleting {tag_id} for {username}')
|
|
||||||
|
|
||||||
tag = get_tag(username=username, tag_id=tag_id)
|
|
||||||
|
|
||||||
if tag:
|
|
||||||
tag.db_ref.delete()
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.error(f'playlist {tag_id} not found for {username}')
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def create_tag(username: str, tag_id: str):
|
|
||||||
user = get_user(username)
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
logger.error(f'{username} not found')
|
|
||||||
return None
|
|
||||||
|
|
||||||
if tag_id in [i.tag_id for i in get_user_tags(username)]:
|
|
||||||
logger.error(f'{tag_id} already exists for {username}')
|
|
||||||
return None
|
|
||||||
|
|
||||||
user.db_ref.collection(u'tags').add({
|
|
||||||
'tag_id': tag_id,
|
|
||||||
'name': tag_id,
|
|
||||||
|
|
||||||
'tracks': [],
|
|
||||||
'albums': [],
|
|
||||||
'artists': [],
|
|
||||||
|
|
||||||
'count': 0,
|
|
||||||
'proportion': 0.0,
|
|
||||||
'total_user_scrobbles': 0,
|
|
||||||
'last_updated': None
|
|
||||||
})
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
from google.cloud import firestore
|
|
||||||
import music.db.database as database
|
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
|
from music.model.playlist import Playlist
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
db = firestore.Client()
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ class PartGenerator:
|
|||||||
if user:
|
if user:
|
||||||
self.user = user
|
self.user = user
|
||||||
elif username:
|
elif username:
|
||||||
pulled_user = database.get_user(username)
|
pulled_user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
if pulled_user:
|
if pulled_user:
|
||||||
self.user = pulled_user
|
self.user = pulled_user
|
||||||
else:
|
else:
|
||||||
@ -38,13 +36,14 @@ class PartGenerator:
|
|||||||
|
|
||||||
def process_reference_by_name(self, name):
|
def process_reference_by_name(self, name):
|
||||||
|
|
||||||
playlist = database.get_playlist(username=self.user.username, name=name)
|
playlist = Playlist.collection.parent(self.user.key).filter('name', '==', name).get()
|
||||||
|
|
||||||
if playlist is not None:
|
if playlist is not None:
|
||||||
|
|
||||||
if playlist.db_ref.id not in self.queried_playlists:
|
if playlist.id not in self.queried_playlists:
|
||||||
|
|
||||||
self.parts += playlist.parts
|
self.parts += playlist.parts
|
||||||
|
self.queried_playlists.append(playlist.id)
|
||||||
|
|
||||||
for i in playlist.playlist_references:
|
for i in playlist.playlist_references:
|
||||||
if i.id not in self.queried_playlists:
|
if i.id not in self.queried_playlists:
|
||||||
@ -61,6 +60,7 @@ class PartGenerator:
|
|||||||
if ref.id not in self.queried_playlists:
|
if ref.id not in self.queried_playlists:
|
||||||
playlist_reference_object = ref.get().to_dict()
|
playlist_reference_object = ref.get().to_dict()
|
||||||
self.parts += playlist_reference_object['parts']
|
self.parts += playlist_reference_object['parts']
|
||||||
|
self.queried_playlists.append(ref.id)
|
||||||
|
|
||||||
for i in playlist_reference_object['playlist_references']:
|
for i in playlist_reference_object['playlist_references']:
|
||||||
self.process_reference_by_reference(i)
|
self.process_reference_by_reference(i)
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
from typing import List
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import datetime
|
|
||||||
from google.cloud.firestore import DocumentReference
|
|
||||||
|
|
||||||
from fmframework.net.network import Network
|
from fireo.models import Model
|
||||||
|
from fireo.fields import TextField, BooleanField, DateTime, NumberField, ListField
|
||||||
import music.db.database as database
|
|
||||||
|
|
||||||
|
|
||||||
class Sort(Enum):
|
class Sort(Enum):
|
||||||
@ -14,466 +10,52 @@ class Sort(Enum):
|
|||||||
release_date = 3
|
release_date = 3
|
||||||
|
|
||||||
|
|
||||||
class Playlist:
|
class Playlist(Model):
|
||||||
def __init__(self,
|
class Meta:
|
||||||
uri: str,
|
collection_name = 'playlists'
|
||||||
name: str,
|
|
||||||
username: str,
|
|
||||||
|
|
||||||
db_ref: DocumentReference,
|
uri = TextField()
|
||||||
|
name = TextField(required=True)
|
||||||
|
type = TextField(required=True)
|
||||||
|
|
||||||
include_recommendations: bool,
|
include_recommendations = BooleanField(default=False)
|
||||||
recommendation_sample: int,
|
recommendation_sample = NumberField(default=10)
|
||||||
include_library_tracks: bool,
|
include_library_tracks = BooleanField(default=False)
|
||||||
|
|
||||||
parts: List[str],
|
parts = ListField(default=[])
|
||||||
playlist_references: List[DocumentReference],
|
playlist_references = ListField(default=[])
|
||||||
shuffle: bool,
|
shuffle = BooleanField(default=False)
|
||||||
|
|
||||||
sort: Sort = None,
|
sort = TextField(default='release_date')
|
||||||
|
description_overwrite = TextField()
|
||||||
|
description_suffix = TextField()
|
||||||
|
|
||||||
description_overwrite: str = None,
|
last_updated = DateTime()
|
||||||
description_suffix: str = None,
|
|
||||||
|
|
||||||
last_updated: datetime = None,
|
lastfm_stat_count = NumberField(default=0)
|
||||||
|
lastfm_stat_album_count = NumberField(default=0)
|
||||||
|
lastfm_stat_artist_count = NumberField(default=0)
|
||||||
|
|
||||||
lastfm_stat_count: int = None,
|
lastfm_stat_percent = NumberField(default=0)
|
||||||
lastfm_stat_album_count: int = None,
|
lastfm_stat_album_percent = NumberField(default=0)
|
||||||
lastfm_stat_artist_count: int = None,
|
lastfm_stat_artist_percent = NumberField(default=0)
|
||||||
|
|
||||||
lastfm_stat_percent: int = None,
|
lastfm_stat_last_refresh = DateTime()
|
||||||
lastfm_stat_album_percent: int = None,
|
|
||||||
lastfm_stat_artist_percent: int = None,
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh: datetime = None):
|
add_last_month = BooleanField(default=False)
|
||||||
self._uri = uri
|
add_this_month = BooleanField(default=False)
|
||||||
self.name = name
|
day_boundary = NumberField(default=21)
|
||||||
self.username = username
|
|
||||||
|
|
||||||
self.db_ref = db_ref
|
chart_range = TextField(default='1month')
|
||||||
|
chart_limit = NumberField(default=50)
|
||||||
self._include_recommendations = include_recommendations
|
|
||||||
self._recommendation_sample = recommendation_sample
|
|
||||||
self._include_library_tracks = include_library_tracks
|
|
||||||
|
|
||||||
self._parts = parts
|
|
||||||
self._playlist_references = playlist_references
|
|
||||||
self._shuffle = shuffle
|
|
||||||
|
|
||||||
self._sort = sort
|
|
||||||
self._description_overwrite = description_overwrite
|
|
||||||
self._description_suffix = description_suffix
|
|
||||||
|
|
||||||
self._last_updated = last_updated
|
|
||||||
|
|
||||||
self._lastfm_stat_count = lastfm_stat_count
|
|
||||||
self._lastfm_stat_album_count = lastfm_stat_album_count
|
|
||||||
self._lastfm_stat_artist_count = lastfm_stat_artist_count
|
|
||||||
|
|
||||||
self._lastfm_stat_percent = lastfm_stat_percent
|
|
||||||
self._lastfm_stat_album_percent = lastfm_stat_album_percent
|
|
||||||
self._lastfm_stat_artist_percent = lastfm_stat_artist_percent
|
|
||||||
|
|
||||||
self._lastfm_stat_last_refresh = lastfm_stat_last_refresh
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
to_return = super().to_dict()
|
||||||
'uri': self.uri,
|
|
||||||
'name': self.name,
|
|
||||||
'type': 'default',
|
|
||||||
|
|
||||||
'include_recommendations': self.include_recommendations,
|
to_return["playlist_references"] = [i.get().to_dict().get('name') for i in to_return['playlist_references']]
|
||||||
'recommendation_sample': self.recommendation_sample,
|
|
||||||
'include_library_tracks': self.include_library_tracks,
|
|
||||||
|
|
||||||
'parts': self.parts,
|
# remove unnecessary and sensitive fields
|
||||||
'playlist_references': [i.get().to_dict().get('name') for i in self.playlist_references],
|
to_return.pop('id', None)
|
||||||
'shuffle': self.shuffle,
|
to_return.pop('key', None)
|
||||||
|
|
||||||
'sort': self.sort.name,
|
return to_return
|
||||||
'description_overwrite': self.description_overwrite,
|
|
||||||
'description_suffix': self.description_suffix,
|
|
||||||
|
|
||||||
'last_updated': self.last_updated,
|
|
||||||
|
|
||||||
'lastfm_stat_count': self.lastfm_stat_count,
|
|
||||||
'lastfm_stat_album_count': self.lastfm_stat_album_count,
|
|
||||||
'lastfm_stat_artist_count': self.lastfm_stat_artist_count,
|
|
||||||
|
|
||||||
'lastfm_stat_percent': self.lastfm_stat_percent,
|
|
||||||
'lastfm_stat_album_percent': self.lastfm_stat_album_percent,
|
|
||||||
'lastfm_stat_artist_percent': self.lastfm_stat_artist_percent,
|
|
||||||
|
|
||||||
'lastfm_stat_last_refresh': self.lastfm_stat_last_refresh
|
|
||||||
}
|
|
||||||
|
|
||||||
def update_database(self, updates):
|
|
||||||
database.update_playlist(username=self.username, name=self.name, updates=updates)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def uri(self):
|
|
||||||
return self._uri
|
|
||||||
|
|
||||||
@uri.setter
|
|
||||||
def uri(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'uri': value})
|
|
||||||
self._uri = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def include_recommendations(self):
|
|
||||||
return self._include_recommendations
|
|
||||||
|
|
||||||
@include_recommendations.setter
|
|
||||||
def include_recommendations(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'include_recommendations': value})
|
|
||||||
self._include_recommendations = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def recommendation_sample(self):
|
|
||||||
return self._recommendation_sample
|
|
||||||
|
|
||||||
@recommendation_sample.setter
|
|
||||||
def recommendation_sample(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'recommendation_sample': value})
|
|
||||||
self._recommendation_sample = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def include_library_tracks(self):
|
|
||||||
return self._include_library_tracks
|
|
||||||
|
|
||||||
@include_library_tracks.setter
|
|
||||||
def include_library_tracks(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'include_library_tracks': value})
|
|
||||||
self._include_library_tracks = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parts(self):
|
|
||||||
return self._parts
|
|
||||||
|
|
||||||
@parts.setter
|
|
||||||
def parts(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'parts': value})
|
|
||||||
self._parts = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def playlist_references(self):
|
|
||||||
return self._playlist_references
|
|
||||||
|
|
||||||
@playlist_references.setter
|
|
||||||
def playlist_references(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'playlist_references': value})
|
|
||||||
self._playlist_references = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shuffle(self):
|
|
||||||
return self._shuffle
|
|
||||||
|
|
||||||
@shuffle.setter
|
|
||||||
def shuffle(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'shuffle': value})
|
|
||||||
self._shuffle = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sort(self):
|
|
||||||
return self._sort
|
|
||||||
|
|
||||||
@sort.setter
|
|
||||||
def sort(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'sort': value.name})
|
|
||||||
self._sort = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description_overwrite(self):
|
|
||||||
return self._description_overwrite
|
|
||||||
|
|
||||||
@description_overwrite.setter
|
|
||||||
def description_overwrite(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'description_overwrite': value})
|
|
||||||
self._description_overwrite = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description_suffix(self):
|
|
||||||
return self._description_suffix
|
|
||||||
|
|
||||||
@description_suffix.setter
|
|
||||||
def description_suffix(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'description_suffix': value})
|
|
||||||
self._description_suffix = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def last_updated(self):
|
|
||||||
return self._last_updated
|
|
||||||
|
|
||||||
@last_updated.setter
|
|
||||||
def last_updated(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'last_updated': value})
|
|
||||||
self._last_updated = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_count(self):
|
|
||||||
return self._lastfm_stat_count
|
|
||||||
|
|
||||||
@lastfm_stat_count.setter
|
|
||||||
def lastfm_stat_count(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_count': value})
|
|
||||||
self._lastfm_stat_count = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_album_count(self):
|
|
||||||
return self._lastfm_stat_album_count
|
|
||||||
|
|
||||||
@lastfm_stat_album_count.setter
|
|
||||||
def lastfm_stat_album_count(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_album_count': value})
|
|
||||||
self._lastfm_stat_album_count = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_artist_count(self):
|
|
||||||
return self._lastfm_stat_artist_count
|
|
||||||
|
|
||||||
@lastfm_stat_artist_count.setter
|
|
||||||
def lastfm_stat_artist_count(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_artist_count': value})
|
|
||||||
self._lastfm_stat_artist_count = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_percent(self):
|
|
||||||
return self._lastfm_stat_percent
|
|
||||||
|
|
||||||
@lastfm_stat_percent.setter
|
|
||||||
def lastfm_stat_percent(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_percent': value})
|
|
||||||
self._lastfm_stat_percent = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_album_percent(self):
|
|
||||||
return self._lastfm_stat_album_percent
|
|
||||||
|
|
||||||
@lastfm_stat_album_percent.setter
|
|
||||||
def lastfm_stat_album_percent(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_album_percent': value})
|
|
||||||
self._lastfm_stat_album_percent = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_artist_percent(self):
|
|
||||||
return self._lastfm_stat_artist_percent
|
|
||||||
|
|
||||||
@lastfm_stat_artist_percent.setter
|
|
||||||
def lastfm_stat_artist_percent(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_artist_percent': value})
|
|
||||||
self._lastfm_stat_artist_percent = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_stat_last_refresh(self):
|
|
||||||
return self._lastfm_stat_last_refresh
|
|
||||||
|
|
||||||
@lastfm_stat_last_refresh.setter
|
|
||||||
def lastfm_stat_last_refresh(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'lastfm_stat_last_refresh': value})
|
|
||||||
self._lastfm_stat_last_refresh = value
|
|
||||||
|
|
||||||
|
|
||||||
class RecentsPlaylist(Playlist):
|
|
||||||
def __init__(self,
|
|
||||||
uri: str,
|
|
||||||
name: str,
|
|
||||||
username: str,
|
|
||||||
|
|
||||||
db_ref: DocumentReference,
|
|
||||||
|
|
||||||
include_recommendations: bool,
|
|
||||||
recommendation_sample: int,
|
|
||||||
include_library_tracks: bool,
|
|
||||||
|
|
||||||
parts: List[str],
|
|
||||||
playlist_references: List[DocumentReference],
|
|
||||||
shuffle: bool,
|
|
||||||
|
|
||||||
sort: Sort = None,
|
|
||||||
|
|
||||||
description_overwrite: str = None,
|
|
||||||
description_suffix: str = None,
|
|
||||||
|
|
||||||
last_updated: datetime = None,
|
|
||||||
|
|
||||||
lastfm_stat_count: int = None,
|
|
||||||
lastfm_stat_album_count: int = None,
|
|
||||||
lastfm_stat_artist_count: int = None,
|
|
||||||
|
|
||||||
lastfm_stat_percent: int = None,
|
|
||||||
lastfm_stat_album_percent: int = None,
|
|
||||||
lastfm_stat_artist_percent: int = None,
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh: datetime = None,
|
|
||||||
|
|
||||||
add_last_month: bool = False,
|
|
||||||
add_this_month: bool = False,
|
|
||||||
day_boundary: int = 7):
|
|
||||||
super().__init__(uri=uri,
|
|
||||||
name=name,
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=db_ref,
|
|
||||||
|
|
||||||
include_recommendations=include_recommendations,
|
|
||||||
recommendation_sample=recommendation_sample,
|
|
||||||
include_library_tracks=include_library_tracks,
|
|
||||||
|
|
||||||
parts=parts,
|
|
||||||
playlist_references=playlist_references,
|
|
||||||
shuffle=shuffle,
|
|
||||||
|
|
||||||
sort=sort,
|
|
||||||
|
|
||||||
description_overwrite=description_overwrite,
|
|
||||||
description_suffix=description_suffix,
|
|
||||||
|
|
||||||
last_updated=last_updated,
|
|
||||||
|
|
||||||
lastfm_stat_count=lastfm_stat_count,
|
|
||||||
lastfm_stat_album_count=lastfm_stat_album_count,
|
|
||||||
lastfm_stat_artist_count=lastfm_stat_artist_count,
|
|
||||||
|
|
||||||
lastfm_stat_percent=lastfm_stat_percent,
|
|
||||||
lastfm_stat_album_percent=lastfm_stat_album_percent,
|
|
||||||
lastfm_stat_artist_percent=lastfm_stat_artist_percent,
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh=lastfm_stat_last_refresh)
|
|
||||||
self._add_last_month = add_last_month
|
|
||||||
self._add_this_month = add_this_month
|
|
||||||
self._day_boundary = day_boundary
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
response = super().to_dict()
|
|
||||||
response.update({
|
|
||||||
'add_last_month': self.add_last_month,
|
|
||||||
'add_this_month': self.add_this_month,
|
|
||||||
'day_boundary': self.day_boundary,
|
|
||||||
'type': 'recents'
|
|
||||||
})
|
|
||||||
return response
|
|
||||||
|
|
||||||
@property
|
|
||||||
def add_last_month(self):
|
|
||||||
return self._add_last_month
|
|
||||||
|
|
||||||
@add_last_month.setter
|
|
||||||
def add_last_month(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'add_last_month': value})
|
|
||||||
self._add_last_month = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def add_this_month(self):
|
|
||||||
return self._add_this_month
|
|
||||||
|
|
||||||
@add_this_month.setter
|
|
||||||
def add_this_month(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'add_this_month': value})
|
|
||||||
self._add_this_month = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def day_boundary(self):
|
|
||||||
return self._day_boundary
|
|
||||||
|
|
||||||
@day_boundary.setter
|
|
||||||
def day_boundary(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'day_boundary': value})
|
|
||||||
self._day_boundary = value
|
|
||||||
|
|
||||||
|
|
||||||
class LastFMChartPlaylist(Playlist):
|
|
||||||
def __init__(self,
|
|
||||||
uri: str,
|
|
||||||
name: str,
|
|
||||||
username: str,
|
|
||||||
|
|
||||||
chart_range: Network.Range,
|
|
||||||
|
|
||||||
db_ref: DocumentReference,
|
|
||||||
|
|
||||||
|
|
||||||
include_recommendations: bool,
|
|
||||||
recommendation_sample: int,
|
|
||||||
include_library_tracks: bool,
|
|
||||||
|
|
||||||
parts: List[str],
|
|
||||||
playlist_references: List[DocumentReference],
|
|
||||||
shuffle: bool,
|
|
||||||
|
|
||||||
chart_limit: int = 50,
|
|
||||||
|
|
||||||
sort: Sort = None,
|
|
||||||
|
|
||||||
description_overwrite: str = None,
|
|
||||||
description_suffix: str = None,
|
|
||||||
|
|
||||||
last_updated: datetime = None,
|
|
||||||
|
|
||||||
lastfm_stat_count: int = None,
|
|
||||||
lastfm_stat_album_count: int = None,
|
|
||||||
lastfm_stat_artist_count: int = None,
|
|
||||||
|
|
||||||
lastfm_stat_percent: int = None,
|
|
||||||
lastfm_stat_album_percent: int = None,
|
|
||||||
lastfm_stat_artist_percent: int = None,
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh: datetime = None):
|
|
||||||
super().__init__(uri=uri,
|
|
||||||
name=name,
|
|
||||||
username=username,
|
|
||||||
|
|
||||||
db_ref=db_ref,
|
|
||||||
|
|
||||||
include_recommendations=include_recommendations,
|
|
||||||
recommendation_sample=recommendation_sample,
|
|
||||||
include_library_tracks=include_library_tracks,
|
|
||||||
|
|
||||||
parts=parts,
|
|
||||||
playlist_references=playlist_references,
|
|
||||||
shuffle=shuffle,
|
|
||||||
|
|
||||||
sort=sort,
|
|
||||||
|
|
||||||
description_overwrite=description_overwrite,
|
|
||||||
description_suffix=description_suffix,
|
|
||||||
|
|
||||||
last_updated=last_updated,
|
|
||||||
|
|
||||||
lastfm_stat_count=lastfm_stat_count,
|
|
||||||
lastfm_stat_album_count=lastfm_stat_album_count,
|
|
||||||
lastfm_stat_artist_count=lastfm_stat_artist_count,
|
|
||||||
|
|
||||||
lastfm_stat_percent=lastfm_stat_percent,
|
|
||||||
lastfm_stat_album_percent=lastfm_stat_album_percent,
|
|
||||||
lastfm_stat_artist_percent=lastfm_stat_artist_percent,
|
|
||||||
|
|
||||||
lastfm_stat_last_refresh=lastfm_stat_last_refresh)
|
|
||||||
self._chart_range = chart_range
|
|
||||||
self._chart_limit = chart_limit
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
response = super().to_dict()
|
|
||||||
response.update({
|
|
||||||
'chart_limit': self.chart_limit,
|
|
||||||
'chart_range': self.chart_range.name,
|
|
||||||
'type': 'fmchart'
|
|
||||||
})
|
|
||||||
return response
|
|
||||||
|
|
||||||
@property
|
|
||||||
def chart_range(self):
|
|
||||||
return self._chart_range
|
|
||||||
|
|
||||||
@chart_range.setter
|
|
||||||
def chart_range(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'chart_range': value.name})
|
|
||||||
self._chart_range = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def chart_limit(self):
|
|
||||||
return self._chart_limit
|
|
||||||
|
|
||||||
@chart_limit.setter
|
|
||||||
def chart_limit(self, value):
|
|
||||||
database.update_playlist(self.username, self.name, {'chart_limit': value})
|
|
||||||
self._chart_limit = value
|
|
||||||
|
@ -1,129 +1,30 @@
|
|||||||
from datetime import datetime
|
from fireo.models import Model
|
||||||
import music.db.database as db
|
from fireo.fields import TextField, DateTime, NumberField, ListField
|
||||||
|
|
||||||
|
|
||||||
class Tag:
|
class Tag(Model):
|
||||||
|
class Meta:
|
||||||
|
collection_name = 'tags'
|
||||||
|
|
||||||
def __init__(self,
|
tag_id = TextField(required=True)
|
||||||
tag_id: str,
|
name = TextField(required=True)
|
||||||
name: str,
|
username = TextField(required=True)
|
||||||
username: str,
|
|
||||||
|
|
||||||
db_ref,
|
tracks = ListField(default=[])
|
||||||
|
albums = ListField(default=[])
|
||||||
|
artists = ListField(default=[])
|
||||||
|
|
||||||
tracks,
|
count = NumberField(default=0)
|
||||||
albums,
|
proportion = NumberField(default=0)
|
||||||
artists,
|
total_user_scrobbles = NumberField(default=0)
|
||||||
|
|
||||||
count: int,
|
last_updated = DateTime()
|
||||||
proportion: float,
|
|
||||||
total_user_scrobbles: int,
|
|
||||||
|
|
||||||
last_updated: datetime):
|
|
||||||
self.tag_id = tag_id
|
|
||||||
self._name = name
|
|
||||||
self.username = username
|
|
||||||
|
|
||||||
self.db_ref = db_ref
|
|
||||||
|
|
||||||
self._tracks = tracks
|
|
||||||
self._albums = albums
|
|
||||||
self._artists = artists
|
|
||||||
|
|
||||||
self._count = count
|
|
||||||
self._proportion = proportion
|
|
||||||
self._total_user_scrobbles = total_user_scrobbles
|
|
||||||
|
|
||||||
self._last_updated = last_updated
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
to_return = super().to_dict()
|
||||||
'tag_id': self.tag_id,
|
|
||||||
'name': self.name,
|
|
||||||
'username': self.username,
|
|
||||||
|
|
||||||
'tracks': self.tracks,
|
# remove unnecessary and sensitive fields
|
||||||
'albums': self.albums,
|
to_return.pop('id', None)
|
||||||
'artists': self.artists,
|
to_return.pop('key', None)
|
||||||
|
|
||||||
'count': self.count,
|
return to_return
|
||||||
'proportion': self.proportion,
|
|
||||||
'total_user_scrobbles': self.total_user_scrobbles,
|
|
||||||
|
|
||||||
'last_updated': self.last_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
def update_database(self, updates):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates=updates)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'name': value})
|
|
||||||
self._name = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tracks(self):
|
|
||||||
return self._tracks
|
|
||||||
|
|
||||||
@tracks.setter
|
|
||||||
def tracks(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'tracks': value})
|
|
||||||
self._tracks = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def albums(self):
|
|
||||||
return self._albums
|
|
||||||
|
|
||||||
@albums.setter
|
|
||||||
def albums(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'albums': value})
|
|
||||||
self._albums = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def artists(self):
|
|
||||||
return self._artists
|
|
||||||
|
|
||||||
@artists.setter
|
|
||||||
def artists(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'artists': value})
|
|
||||||
self._artists = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def count(self):
|
|
||||||
return self._count
|
|
||||||
|
|
||||||
@count.setter
|
|
||||||
def count(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'count': value})
|
|
||||||
self._count = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def proportion(self):
|
|
||||||
return self._proportion
|
|
||||||
|
|
||||||
@proportion.setter
|
|
||||||
def proportion(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'proportion': value})
|
|
||||||
self._proportion = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_user_scrobbles(self):
|
|
||||||
return self._total_user_scrobbles
|
|
||||||
|
|
||||||
@total_user_scrobbles.setter
|
|
||||||
def total_user_scrobbles(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'total_user_scrobbles': value})
|
|
||||||
self._total_user_scrobbles = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def last_updated(self):
|
|
||||||
return self._last_updated
|
|
||||||
|
|
||||||
@last_updated.setter
|
|
||||||
def last_updated(self, value):
|
|
||||||
db.update_tag(username=self.username, tag_id=self.tag_id, updates={'last_updated': value})
|
|
||||||
self._last_updated = value
|
|
||||||
|
@ -1,172 +1,42 @@
|
|||||||
from datetime import datetime
|
from fireo.models import Model
|
||||||
from enum import Enum
|
from fireo.fields import TextField, BooleanField, DateTime, NumberField
|
||||||
|
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import check_password_hash
|
||||||
|
|
||||||
import music.db.database as database
|
|
||||||
|
|
||||||
|
|
||||||
class User:
|
class User(Model):
|
||||||
class Type(Enum):
|
class Meta:
|
||||||
user = 1
|
collection_name = 'spotify_users'
|
||||||
admin = 2
|
|
||||||
|
|
||||||
def __init__(self,
|
username = TextField(required=True)
|
||||||
username: str,
|
password = TextField(required=True)
|
||||||
password: str,
|
email = TextField()
|
||||||
db_ref,
|
type = TextField(default="user")
|
||||||
email: str,
|
|
||||||
user_type: Type,
|
|
||||||
last_login: datetime,
|
|
||||||
last_refreshed: datetime,
|
|
||||||
locked: bool,
|
|
||||||
validated: bool,
|
|
||||||
|
|
||||||
spotify_linked: bool,
|
last_login = DateTime()
|
||||||
access_token: str,
|
last_refreshed = DateTime()
|
||||||
refresh_token: str,
|
locked = BooleanField(default=False, required=True)
|
||||||
token_expiry: int,
|
validated = BooleanField(default=True, required=True)
|
||||||
|
|
||||||
lastfm_username: str = None):
|
spotify_linked = BooleanField(default=False, required=True)
|
||||||
self.username = username
|
access_token = TextField()
|
||||||
self._password = password
|
refresh_token = TextField()
|
||||||
self.db_ref = db_ref
|
token_expiry = NumberField()
|
||||||
self._email = email
|
|
||||||
self._type = user_type
|
|
||||||
|
|
||||||
self._last_login = last_login
|
lastfm_username = TextField()
|
||||||
self._last_refreshed = last_refreshed
|
|
||||||
self._locked = locked
|
|
||||||
self._validated = validated
|
|
||||||
|
|
||||||
self._spotify_linked = spotify_linked
|
|
||||||
self._access_token = access_token
|
|
||||||
self._refresh_token = refresh_token
|
|
||||||
self._token_expiry = token_expiry
|
|
||||||
|
|
||||||
self._lastfm_username = lastfm_username
|
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
return check_password_hash(self.password, password)
|
return check_password_hash(self.password, password)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
to_return = super().to_dict()
|
||||||
'username': self.username,
|
|
||||||
'email': self.email,
|
|
||||||
'type': self.user_type.name,
|
|
||||||
'last_login': self.last_login,
|
|
||||||
'spotify_linked': self.spotify_linked,
|
|
||||||
'lastfm_username': self.lastfm_username
|
|
||||||
}
|
|
||||||
|
|
||||||
def update_database(self, updates):
|
# remove unnecessary and sensitive fields
|
||||||
database.update_user(username=self.username, updates=updates)
|
to_return.pop('password', None)
|
||||||
|
to_return.pop('access_token', None)
|
||||||
|
to_return.pop('refresh_token', None)
|
||||||
|
to_return.pop('token_expiry', None)
|
||||||
|
to_return.pop('id', None)
|
||||||
|
to_return.pop('key', None)
|
||||||
|
|
||||||
@property
|
return to_return
|
||||||
def password(self):
|
|
||||||
return self._password
|
|
||||||
|
|
||||||
@password.setter
|
|
||||||
def password(self, value):
|
|
||||||
pw_hash = generate_password_hash(value)
|
|
||||||
database.update_user(self.username, {'password': pw_hash})
|
|
||||||
self._password = pw_hash
|
|
||||||
|
|
||||||
@property
|
|
||||||
def email(self):
|
|
||||||
return self._email
|
|
||||||
|
|
||||||
@email.setter
|
|
||||||
def email(self, value):
|
|
||||||
database.update_user(self.username, {'email': value})
|
|
||||||
self._email = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def user_type(self):
|
|
||||||
return self._type
|
|
||||||
|
|
||||||
@user_type.setter
|
|
||||||
def user_type(self, value):
|
|
||||||
database.update_user(self.username, {'type': value})
|
|
||||||
self._type = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def last_login(self):
|
|
||||||
return self._last_login
|
|
||||||
|
|
||||||
@last_login.setter
|
|
||||||
def last_login(self, value):
|
|
||||||
database.update_user(self.username, {'last_login': value})
|
|
||||||
self._last_login = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def last_refreshed(self):
|
|
||||||
return self._last_refreshed
|
|
||||||
|
|
||||||
@last_refreshed.setter
|
|
||||||
def last_refreshed(self, value):
|
|
||||||
database.update_user(self.username, {'last_refreshed': value})
|
|
||||||
self._last_refreshed = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def locked(self):
|
|
||||||
return self._locked
|
|
||||||
|
|
||||||
@locked.setter
|
|
||||||
def locked(self, value):
|
|
||||||
database.update_user(self.username, {'locked': value})
|
|
||||||
self._locked = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def validated(self):
|
|
||||||
return self._validated
|
|
||||||
|
|
||||||
@validated.setter
|
|
||||||
def validated(self, value):
|
|
||||||
database.update_user(self.username, {'validated': value})
|
|
||||||
self._validated = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def spotify_linked(self):
|
|
||||||
return self._spotify_linked
|
|
||||||
|
|
||||||
@spotify_linked.setter
|
|
||||||
def spotify_linked(self, value):
|
|
||||||
database.update_user(self.username, {'spotify_linked': value})
|
|
||||||
self._spotify_linked = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def access_token(self):
|
|
||||||
return self._access_token
|
|
||||||
|
|
||||||
@access_token.setter
|
|
||||||
def access_token(self, value):
|
|
||||||
database.update_user(self.username, {'access_token': value})
|
|
||||||
self._access_token = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def refresh_token(self):
|
|
||||||
return self._refresh_token
|
|
||||||
|
|
||||||
@refresh_token.setter
|
|
||||||
def refresh_token(self, value):
|
|
||||||
database.update_user(self.username, {'refresh_token': value})
|
|
||||||
self._refresh_token = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def token_expiry(self):
|
|
||||||
return self._token_expiry
|
|
||||||
|
|
||||||
@token_expiry.setter
|
|
||||||
def token_expiry(self, value):
|
|
||||||
database.update_user(self.username, {'refresh_token': value})
|
|
||||||
self._token_expiry = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lastfm_username(self):
|
|
||||||
return self._lastfm_username
|
|
||||||
|
|
||||||
@lastfm_username.setter
|
|
||||||
def lastfm_username(self, value):
|
|
||||||
database.update_user(self.username, {'lastfm_username': value})
|
|
||||||
self._lastfm_username = value
|
|
||||||
|
@ -9,21 +9,19 @@ db = firestore.Client()
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_playlist(username, name):
|
def create_playlist(user, name):
|
||||||
logger.info(f'creating spotify playlist for {username} / {name}')
|
logger.info(f'creating spotify playlist for {user.username} / {name}')
|
||||||
|
|
||||||
user = database.get_user(username)
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
|
|
||||||
playlist = net.create_playlist(net.user.username, name)
|
playlist = net.create_playlist(net.user.username, name)
|
||||||
|
|
||||||
if playlist is not None:
|
if playlist is not None:
|
||||||
return playlist
|
return playlist
|
||||||
else:
|
else:
|
||||||
logger.error(f'no response received {username} / {name}')
|
logger.error(f'no response received {user.username} / {name}')
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error(f'{username} not found')
|
logger.error(f'{user.username} not provided')
|
||||||
return
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
from google.cloud import firestore
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -11,8 +9,7 @@ from spotframework.engine.processor.deduplicate import DeduplicateByID
|
|||||||
from spotframework.player.player import Player
|
from spotframework.player.player import Player
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
from music.db.part_generator import PartGenerator
|
from music.db.part_generator import PartGenerator
|
||||||
|
from music.model.user import User
|
||||||
db = firestore.Client()
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -29,7 +26,11 @@ def play_user_playlist(username,
|
|||||||
add_last_month=False,
|
add_last_month=False,
|
||||||
device_name=None):
|
device_name=None):
|
||||||
|
|
||||||
user = database.get_user(username)
|
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}')
|
logger.info(f'playing for {username}')
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ def play_user_playlist(username,
|
|||||||
logger.critical(f'no playlists to use for creation ({username})')
|
logger.critical(f'no playlists to use for creation ({username})')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
|
|
||||||
device = None
|
device = None
|
||||||
if device_name:
|
if device_name:
|
||||||
|
@ -4,6 +4,8 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
|
from music.model.user import User
|
||||||
|
from music.model.playlist import Playlist
|
||||||
|
|
||||||
from spotfm.maths.counter import Counter
|
from spotfm.maths.counter import Counter
|
||||||
from spotframework.model.uri import Uri
|
from spotframework.model.uri import Uri
|
||||||
@ -17,11 +19,15 @@ def refresh_lastfm_track_stats(username, playlist_name):
|
|||||||
|
|
||||||
logger.info(f'refreshing {playlist_name} stats for {username}')
|
logger.info(f'refreshing {playlist_name} stats for {username}')
|
||||||
|
|
||||||
fmnet = database.get_authed_lastfm_network(username=username)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
spotnet = database.get_authed_spotify_network(username=username)
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
fmnet = database.get_authed_lastfm_network(user)
|
||||||
|
spotnet = database.get_authed_spotify_network(user)
|
||||||
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
||||||
|
|
||||||
playlist = database.get_playlist(username=username, name=playlist_name)
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
logger.critical(f'playlist {playlist_name} for {username} not found')
|
logger.critical(f'playlist {playlist_name} for {username} not found')
|
||||||
@ -40,23 +46,26 @@ def refresh_lastfm_track_stats(username, playlist_name):
|
|||||||
else:
|
else:
|
||||||
percent = 0
|
percent = 0
|
||||||
|
|
||||||
playlist.update_database({
|
playlist.lastfm_stat_count = track_count
|
||||||
'lastfm_stat_count': track_count,
|
playlist.lastfm_stat_percent = percent
|
||||||
'lastfm_stat_percent': percent,
|
playlist.lastfm_stat_last_refresh = datetime.utcnow()
|
||||||
|
|
||||||
'lastfm_stat_last_refresh': datetime.utcnow()
|
playlist.update()
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_lastfm_album_stats(username, playlist_name):
|
def refresh_lastfm_album_stats(username, playlist_name):
|
||||||
|
|
||||||
logger.info(f'refreshing {playlist_name} stats for {username}')
|
logger.info(f'refreshing {playlist_name} stats for {username}')
|
||||||
|
|
||||||
fmnet = database.get_authed_lastfm_network(username=username)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
spotnet = database.get_authed_spotify_network(username=username)
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
fmnet = database.get_authed_lastfm_network(user)
|
||||||
|
spotnet = database.get_authed_spotify_network(user)
|
||||||
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
||||||
|
|
||||||
playlist = database.get_playlist(username=username, name=playlist_name)
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
logger.critical(f'playlist {playlist_name} for {username} not found')
|
logger.critical(f'playlist {playlist_name} for {username} not found')
|
||||||
@ -75,23 +84,26 @@ def refresh_lastfm_album_stats(username, playlist_name):
|
|||||||
else:
|
else:
|
||||||
album_percent = 0
|
album_percent = 0
|
||||||
|
|
||||||
playlist.update_database({
|
playlist.lastfm_stat_album_count = album_count
|
||||||
'lastfm_stat_album_count': album_count,
|
playlist.lastfm_stat_album_percent = album_percent
|
||||||
'lastfm_stat_album_percent': album_percent,
|
playlist.lastfm_stat_last_refresh = datetime.utcnow()
|
||||||
|
|
||||||
'lastfm_stat_last_refresh': datetime.utcnow()
|
playlist.update()
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_lastfm_artist_stats(username, playlist_name):
|
def refresh_lastfm_artist_stats(username, playlist_name):
|
||||||
|
|
||||||
logger.info(f'refreshing {playlist_name} stats for {username}')
|
logger.info(f'refreshing {playlist_name} stats for {username}')
|
||||||
|
|
||||||
fmnet = database.get_authed_lastfm_network(username=username)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
spotnet = database.get_authed_spotify_network(username=username)
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
fmnet = database.get_authed_lastfm_network(user)
|
||||||
|
spotnet = database.get_authed_spotify_network(user)
|
||||||
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
||||||
|
|
||||||
playlist = database.get_playlist(username=username, name=playlist_name)
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
logger.critical(f'playlist {playlist_name} for {username} not found')
|
logger.critical(f'playlist {playlist_name} for {username} not found')
|
||||||
@ -110,9 +122,8 @@ def refresh_lastfm_artist_stats(username, playlist_name):
|
|||||||
else:
|
else:
|
||||||
artist_percent = 0
|
artist_percent = 0
|
||||||
|
|
||||||
playlist.update_database({
|
playlist.lastfm_stat_artist_count = artist_count
|
||||||
'lastfm_stat_artist_count': artist_count,
|
playlist.lastfm_stat_artist_percent = artist_percent
|
||||||
'lastfm_stat_artist_percent': artist_percent,
|
playlist.lastfm_stat_last_refresh = datetime.utcnow()
|
||||||
|
|
||||||
'lastfm_stat_last_refresh': datetime.utcnow()
|
playlist.update()
|
||||||
})
|
|
||||||
|
@ -14,7 +14,8 @@ from spotfm.engine.chart_source import ChartSource
|
|||||||
|
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
from music.db.part_generator import PartGenerator
|
from music.db.part_generator import PartGenerator
|
||||||
from music.model.playlist import RecentsPlaylist, LastFMChartPlaylist
|
from music.model.user import User
|
||||||
|
from music.model.playlist import Playlist
|
||||||
|
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
|
|
||||||
@ -23,7 +24,9 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def run_user_playlist(username, playlist_name):
|
def run_user_playlist(username, playlist_name):
|
||||||
"""Generate and upadate a user's playlist"""
|
"""Generate and upadate a user's playlist"""
|
||||||
user = database.get_user(username)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
logger.info(f'running {username} / {playlist_name}')
|
logger.info(f'running {username} / {playlist_name}')
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ def run_user_playlist(username, playlist_name):
|
|||||||
logger.critical(f'{username} not found')
|
logger.critical(f'{username} not found')
|
||||||
return
|
return
|
||||||
|
|
||||||
playlist = database.get_playlist(username=username, name=playlist_name)
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||||
|
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
logger.critical(f'playlist not found ({username}/{playlist_name})')
|
logger.critical(f'playlist not found ({username}/{playlist_name})')
|
||||||
@ -44,7 +47,7 @@ def run_user_playlist(username, playlist_name):
|
|||||||
|
|
||||||
# END CHECKS
|
# END CHECKS
|
||||||
|
|
||||||
net = database.get_authed_spotify_network(username)
|
net = database.get_authed_spotify_network(user)
|
||||||
engine = PlaylistEngine(net)
|
engine = PlaylistEngine(net)
|
||||||
part_generator = PartGenerator(user=user)
|
part_generator = PartGenerator(user=user)
|
||||||
|
|
||||||
@ -63,11 +66,11 @@ def run_user_playlist(username, playlist_name):
|
|||||||
params.append(LibraryTrackSource.Params())
|
params.append(LibraryTrackSource.Params())
|
||||||
# END OPTIONS
|
# END OPTIONS
|
||||||
|
|
||||||
if isinstance(playlist, LastFMChartPlaylist):
|
if playlist.type == 'fmchart':
|
||||||
if user.lastfm_username is None:
|
if user.lastfm_username is None:
|
||||||
logger.error(f'{username} has no associated last.fm username, chart source skipped')
|
logger.error(f'{username} has no associated last.fm username, chart source skipped')
|
||||||
else:
|
else:
|
||||||
engine.sources.append(ChartSource(spotnet=net, fmnet=database.get_authed_lastfm_network(user.username)))
|
engine.sources.append(ChartSource(spotnet=net, fmnet=database.get_authed_lastfm_network(user)))
|
||||||
params.append(ChartSource.Params(chart_range=playlist.chart_range, limit=playlist.chart_limit))
|
params.append(ChartSource.Params(chart_range=playlist.chart_range, limit=playlist.chart_limit))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -78,7 +81,7 @@ def run_user_playlist(username, playlist_name):
|
|||||||
processors.append(SortReleaseDate(reverse=True))
|
processors.append(SortReleaseDate(reverse=True))
|
||||||
|
|
||||||
# GENERATE TRACKS
|
# GENERATE TRACKS
|
||||||
if isinstance(playlist, RecentsPlaylist):
|
if playlist.type == 'recents':
|
||||||
boundary_date = datetime.datetime.now(datetime.timezone.utc) - \
|
boundary_date = datetime.datetime.now(datetime.timezone.utc) - \
|
||||||
datetime.timedelta(days=int(playlist.day_boundary))
|
datetime.timedelta(days=int(playlist.day_boundary))
|
||||||
tracks = engine.get_recent_playlist(params=params,
|
tracks = engine.get_recent_playlist(params=params,
|
||||||
@ -101,3 +104,4 @@ def run_user_playlist(username, playlist_name):
|
|||||||
overwrite=overwrite,
|
overwrite=overwrite,
|
||||||
suffix=suffix)
|
suffix=suffix)
|
||||||
playlist.last_updated = datetime.datetime.utcnow()
|
playlist.last_updated = datetime.datetime.utcnow()
|
||||||
|
playlist.update()
|
||||||
|
@ -2,6 +2,8 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
|
from music.model.user import User
|
||||||
|
from music.model.tag import Tag
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -9,19 +11,20 @@ logger = logging.getLogger(__name__)
|
|||||||
def update_tag(username, tag_id):
|
def update_tag(username, tag_id):
|
||||||
logger.info(f'updating {username} / {tag_id}')
|
logger.info(f'updating {username} / {tag_id}')
|
||||||
|
|
||||||
tag = database.get_tag(username=username, tag_id=tag_id)
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
|
if user is None:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
|
||||||
|
|
||||||
if tag is None:
|
if tag is None:
|
||||||
logger.error(f'{tag_id} for {username} not found')
|
logger.error(f'{tag_id} for {username} not found')
|
||||||
return
|
return
|
||||||
|
|
||||||
user = database.get_user(username)
|
|
||||||
|
|
||||||
if user.lastfm_username is None or len(user.lastfm_username) == 0:
|
if user.lastfm_username is None or len(user.lastfm_username) == 0:
|
||||||
logger.error(f'{username} has no last.fm username')
|
logger.error(f'{username} has no last.fm username')
|
||||||
return
|
return
|
||||||
|
|
||||||
net = database.get_authed_lastfm_network(username=username)
|
net = database.get_authed_lastfm_network(user)
|
||||||
|
|
||||||
tag_count = 0
|
tag_count = 0
|
||||||
user_scrobbles = net.get_user_scrobble_count()
|
user_scrobbles = net.get_user_scrobble_count()
|
||||||
@ -60,13 +63,13 @@ def update_tag(username, tag_id):
|
|||||||
|
|
||||||
tracks.append(track)
|
tracks.append(track)
|
||||||
|
|
||||||
tag.update_database({
|
tag.tracks = tracks
|
||||||
'tracks': tracks,
|
tag.albums = albums
|
||||||
'albums': albums,
|
tag.artists = artists
|
||||||
'artists': artists,
|
|
||||||
|
|
||||||
'total_user_scrobbles': user_scrobbles,
|
tag.total_user_scrobbles = user_scrobbles
|
||||||
'count': tag_count,
|
tag.count = tag_count
|
||||||
'proportion': (tag_count / user_scrobbles) * 100,
|
tag.proportion = (tag_count / user_scrobbles) * 100
|
||||||
'last_updated': datetime.utcnow()
|
tag.last_updated = datetime.utcnow()
|
||||||
})
|
|
||||||
|
tag.update()
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import music.db.database as database
|
from music.model.user import User
|
||||||
|
from music.model.playlist import Playlist
|
||||||
|
|
||||||
playlists = database.get_user_playlists('andy')
|
user = User.collection.filter('username', '==', 'andy').get()
|
||||||
|
|
||||||
name = input('enter playlist name: ')
|
name = input('enter playlist name: ')
|
||||||
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', name).get()
|
||||||
playlist = next((i for i in playlists if i.name == name), None)
|
|
||||||
|
|
||||||
if playlist is not None:
|
if playlist is not None:
|
||||||
new_name = input('enter new name: ')
|
new_name = input('enter new name: ')
|
||||||
playlist.update_database({'name': new_name})
|
playlist.name = new_name
|
||||||
|
playlist.update()
|
||||||
else:
|
else:
|
||||||
print('playlist not found')
|
print('playlist not found')
|
||||||
|
@ -1,37 +1,39 @@
|
|||||||
astroid==2.3.3
|
astroid==2.4.0
|
||||||
cachetools==4.0.0
|
cachetools==4.1.0
|
||||||
certifi==2019.11.28
|
certifi==2020.4.5.1
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
click==7.1.1
|
click==7.1.2
|
||||||
Flask==1.1.1
|
fireo==1.2.4
|
||||||
google-api-core==1.16.0
|
Flask==1.1.2
|
||||||
google-auth==1.12.0
|
google-api-core==1.17.0
|
||||||
|
google-auth==1.14.1
|
||||||
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.1
|
google-cloud-pubsub==1.4.3
|
||||||
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.27.2
|
grpcio==1.28.1
|
||||||
idna==2.9
|
idna==2.9
|
||||||
isort==4.3.21
|
isort==4.3.21
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
Jinja2==2.11.1
|
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.2
|
numpy==1.18.3
|
||||||
opencv-python==4.2.0.32
|
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.4.4
|
pylint==2.5.0
|
||||||
pytz==2019.3
|
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
|
||||||
urllib3==1.25.8
|
toml==0.10.0
|
||||||
Werkzeug==1.0.0
|
urllib3==1.25.9
|
||||||
|
Werkzeug==1.0.1
|
||||||
wrapt==1.12.1
|
wrapt==1.12.1
|
||||||
|
Loading…
Reference in New Issue
Block a user