added fmframework, added count and daily scrobbles endpoint
This commit is contained in:
parent
694add7f8a
commit
d6bff0f9bd
@ -7,6 +7,8 @@ logger = logging.getLogger(__name__)
|
|||||||
logger.setLevel('DEBUG')
|
logger.setLevel('DEBUG')
|
||||||
|
|
||||||
spotframework_logger = logging.getLogger('spotframework')
|
spotframework_logger = logging.getLogger('spotframework')
|
||||||
|
fmframework_logger = logging.getLogger('fmframework')
|
||||||
|
spotfm_logger = logging.getLogger('spotfm')
|
||||||
|
|
||||||
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
||||||
from google.cloud import logging as glogging
|
from google.cloud import logging as glogging
|
||||||
@ -21,6 +23,8 @@ if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD':
|
|||||||
|
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
spotframework_logger.addHandler(handler)
|
spotframework_logger.addHandler(handler)
|
||||||
|
fmframework_logger.addHandler(handler)
|
||||||
|
spotfm_logger.addHandler(handler)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s'
|
log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s'
|
||||||
@ -31,3 +35,5 @@ else:
|
|||||||
|
|
||||||
logger.addHandler(stream_handler)
|
logger.addHandler(stream_handler)
|
||||||
spotframework_logger.addHandler(stream_handler)
|
spotframework_logger.addHandler(stream_handler)
|
||||||
|
fmframework_logger.addHandler(stream_handler)
|
||||||
|
spotfm_logger.addHandler(stream_handler)
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
from .api import blueprint as api_blueprint
|
from .api import blueprint as api_blueprint
|
||||||
from .player import blueprint as player_blueprint
|
from .player import blueprint as player_blueprint
|
||||||
|
from .fm import blueprint as fm_blueprint
|
||||||
|
from .spotfm import blueprint as spotfm_blueprint
|
||||||
|
@ -85,6 +85,24 @@ def spotify_link_required(func):
|
|||||||
return spotify_link_required_wrapper
|
return spotify_link_required_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def lastfm_username_required(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def lastfm_username_required_wrapper(*args, **kwargs):
|
||||||
|
user_dict = database.get_user_doc_ref(kwargs.get('username')).get().to_dict()
|
||||||
|
|
||||||
|
if user_dict:
|
||||||
|
if user_dict.get('lastfm_username'):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.warning(f'no last.fm username for {user_dict["username"]}')
|
||||||
|
return jsonify({'status': 'error', 'message': 'no last.fm username'}), 401
|
||||||
|
else:
|
||||||
|
logger.warning('user not logged in')
|
||||||
|
return jsonify({'error': 'not logged in'}), 401
|
||||||
|
|
||||||
|
return lastfm_username_required_wrapper
|
||||||
|
|
||||||
|
|
||||||
def gae_cron(func):
|
def gae_cron(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def gae_cron_wrapper(*args, **kwargs):
|
def gae_cron_wrapper(*args, **kwargs):
|
||||||
|
29
spotify/api/fm.py
Normal file
29
spotify/api/fm.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from flask import Blueprint, jsonify
|
||||||
|
from datetime import date
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from google.cloud import firestore
|
||||||
|
|
||||||
|
from spotify.api.decorators import login_or_basic_auth, lastfm_username_required
|
||||||
|
|
||||||
|
import spotify.db.database as database
|
||||||
|
|
||||||
|
blueprint = Blueprint('fm-api', __name__)
|
||||||
|
db = firestore.Client()
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/today', methods=['GET'])
|
||||||
|
@login_or_basic_auth
|
||||||
|
@lastfm_username_required
|
||||||
|
def daily_scrobbles(username=None):
|
||||||
|
|
||||||
|
net = database.get_authed_lastfm_network(username)
|
||||||
|
|
||||||
|
total = net.get_scrobble_count_from_date(input_date=date.today())
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'username': net.username,
|
||||||
|
'scrobbles_today': total
|
||||||
|
}), 200
|
@ -30,7 +30,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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
|
|
||||||
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))
|
||||||
@ -42,7 +42,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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
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)
|
||||||
@ -63,7 +63,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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
|
|
||||||
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))
|
||||||
@ -82,7 +82,7 @@ def play(username=None):
|
|||||||
@login_or_basic_auth
|
@login_or_basic_auth
|
||||||
@spotify_link_required
|
@spotify_link_required
|
||||||
def next_track(username=None):
|
def next_track(username=None):
|
||||||
net = database.get_authed_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.next()
|
player.next()
|
||||||
@ -97,7 +97,7 @@ def shuffle(username=None):
|
|||||||
|
|
||||||
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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.shuffle(state=request_json['state'])
|
player.shuffle(state=request_json['state'])
|
||||||
@ -117,7 +117,7 @@ def volume(username=None):
|
|||||||
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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
player = Player(net)
|
player = Player(net)
|
||||||
|
|
||||||
player.volume(value=request_json['volume'])
|
player.volume(value=request_json['volume'])
|
||||||
|
62
spotify/api/spotfm.py
Normal file
62
spotify/api/spotfm.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
from flask import Blueprint, jsonify, request
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from google.cloud import firestore
|
||||||
|
|
||||||
|
from spotify.api.decorators import login_or_basic_auth, lastfm_username_required, spotify_link_required
|
||||||
|
|
||||||
|
import spotify.db.database as database
|
||||||
|
|
||||||
|
from spotfm.maths.counter import Counter
|
||||||
|
from spotframework.model.uri import Uri
|
||||||
|
|
||||||
|
blueprint = Blueprint('spotfm-api', __name__)
|
||||||
|
db = firestore.Client()
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/count', methods=['GET'])
|
||||||
|
@login_or_basic_auth
|
||||||
|
@spotify_link_required
|
||||||
|
@lastfm_username_required
|
||||||
|
def count(username=None):
|
||||||
|
|
||||||
|
uri = request.args.get('uri', None)
|
||||||
|
playlist_name = request.args.get('playlist_name', None)
|
||||||
|
|
||||||
|
if uri is None and playlist_name is None:
|
||||||
|
return jsonify({'error': 'no input provided'}), 401
|
||||||
|
|
||||||
|
if uri:
|
||||||
|
try:
|
||||||
|
uri = Uri(uri)
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({'error': 'malformed uri provided'}), 401
|
||||||
|
|
||||||
|
spotnet = database.get_authed_spotify_network(username)
|
||||||
|
fmnet = database.get_authed_lastfm_network(username)
|
||||||
|
counter = Counter(fmnet=fmnet, spotnet=spotnet)
|
||||||
|
|
||||||
|
if uri:
|
||||||
|
uri_count = counter.count(uri=uri)
|
||||||
|
return jsonify({
|
||||||
|
"uri": str(uri),
|
||||||
|
"count": uri_count,
|
||||||
|
'uri_type': str(uri.object_type),
|
||||||
|
'last.fm_username': fmnet.username
|
||||||
|
}), 200
|
||||||
|
elif playlist_name:
|
||||||
|
|
||||||
|
playlists = spotnet.get_playlists()
|
||||||
|
playlist = next((i for i in playlists if i.name == playlist_name), None)
|
||||||
|
|
||||||
|
if playlist is not None:
|
||||||
|
playlist_count = counter.count_playlist(playlist=playlist)
|
||||||
|
return jsonify({
|
||||||
|
"count": playlist_count,
|
||||||
|
'playlist_name': playlist_name,
|
||||||
|
'last.fm_username': fmnet.username
|
||||||
|
}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({'error': f'playlist {playlist_name} not found'}), 404
|
@ -4,7 +4,8 @@ from datetime import timedelta, datetime, timezone
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from werkzeug.security import check_password_hash
|
from werkzeug.security import check_password_hash
|
||||||
|
|
||||||
from spotframework.net.network import Network
|
from spotframework.net.network import Network as SpotifyNetwork
|
||||||
|
from fmframework.net.network import Network as FmNetwork
|
||||||
from spotify.db.user import DatabaseUser
|
from spotify.db.user import DatabaseUser
|
||||||
|
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
@ -27,7 +28,7 @@ def refresh_token_database_callback(user):
|
|||||||
logger.error('user has no attached id')
|
logger.error('user has no attached id')
|
||||||
|
|
||||||
|
|
||||||
def get_authed_network(username):
|
def get_authed_spotify_network(username):
|
||||||
|
|
||||||
user = get_user_doc_ref(username)
|
user = get_user_doc_ref(username)
|
||||||
if user:
|
if user:
|
||||||
@ -48,13 +49,29 @@ def get_authed_network(username):
|
|||||||
user_obj.refresh_access_token()
|
user_obj.refresh_access_token()
|
||||||
|
|
||||||
user_obj.refresh_info()
|
user_obj.refresh_info()
|
||||||
return Network(user_obj)
|
return SpotifyNetwork(user_obj)
|
||||||
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 {username} not found')
|
||||||
|
|
||||||
|
|
||||||
|
def get_authed_lastfm_network(username):
|
||||||
|
|
||||||
|
user = get_user_doc_ref(username)
|
||||||
|
if user:
|
||||||
|
user_dict = user.get().to_dict()
|
||||||
|
|
||||||
|
if user_dict.get('lastfm_username', None):
|
||||||
|
fm_keys = db.document('key/fm').get().to_dict()
|
||||||
|
|
||||||
|
return FmNetwork(username=user_dict['lastfm_username'], api_key=fm_keys['clientid'])
|
||||||
|
else:
|
||||||
|
logger.error(f'{username} has no last.fm username')
|
||||||
|
else:
|
||||||
|
logger.error(f'user {username} not found')
|
||||||
|
|
||||||
|
|
||||||
def check_user_password(username, password):
|
def check_user_password(username, password):
|
||||||
|
|
||||||
user = get_user_doc_ref(user=username)
|
user = get_user_doc_ref(user=username)
|
||||||
|
@ -4,7 +4,7 @@ from google.cloud import firestore
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from spotify.auth import auth_blueprint
|
from spotify.auth import auth_blueprint
|
||||||
from spotify.api import api_blueprint, player_blueprint
|
from spotify.api import api_blueprint, player_blueprint, fm_blueprint, spotfm_blueprint
|
||||||
|
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
|
|
||||||
@ -13,6 +13,8 @@ app.secret_key = db.collection(u'spotify').document(u'config').get().to_dict()['
|
|||||||
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
||||||
app.register_blueprint(api_blueprint, url_prefix='/api')
|
app.register_blueprint(api_blueprint, url_prefix='/api')
|
||||||
app.register_blueprint(player_blueprint, url_prefix='/api/player')
|
app.register_blueprint(player_blueprint, url_prefix='/api/player')
|
||||||
|
app.register_blueprint(fm_blueprint, url_prefix='/api/fm')
|
||||||
|
app.register_blueprint(spotfm_blueprint, url_prefix='/api/spotfm')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
@ -17,7 +17,7 @@ def create_playlist(username, name):
|
|||||||
|
|
||||||
if len(users) == 1:
|
if len(users) == 1:
|
||||||
|
|
||||||
net = database.get_authed_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
|
|
||||||
playlist = net.create_playlist(net.user.username, name)
|
playlist = net.create_playlist(net.user.username, name)
|
||||||
|
|
||||||
|
@ -49,7 +49,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_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
|
|
||||||
device = None
|
device = None
|
||||||
if device_name:
|
if device_name:
|
||||||
|
@ -42,7 +42,7 @@ def run_user_playlist(username, playlist_name):
|
|||||||
logger.critical(f'no playlists to use for creation ({username}/{playlist_name})')
|
logger.critical(f'no playlists to use for creation ({username}/{playlist_name})')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
net = database.get_authed_network(username)
|
net = database.get_authed_spotify_network(username)
|
||||||
|
|
||||||
engine = PlaylistEngine(net)
|
engine = PlaylistEngine(net)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user