added get authed network function to reduce token refreshing, added play endpoint

This commit is contained in:
aj 2019-09-27 10:32:42 +01:00
parent 215c839210
commit d0adcc679b
6 changed files with 143 additions and 32 deletions

View File

@ -13,6 +13,10 @@ from werkzeug.security import check_password_hash, generate_password_hash
from spotify.tasks.run_user_playlist import run_user_playlist as run_user_playlist
from spotify.tasks.play_user_playlist import play_user_playlist as play_user_playlist
from spotframework.model.track import SpotifyTrack
from spotframework.model.uri import Uri
from spotframework.model.service import Context
from spotframework.player.player import Player
import spotify.db.database as database
@ -110,13 +114,90 @@ def cloud_task(func):
return cloud_task_wrapper
@blueprint.route('/play', methods=['POST'])
@login_or_basic_auth
def play(username=None):
user_ref = database.get_user_doc_ref(username)
user_dict = user_ref.get().to_dict()
if user_dict.get('spotify_linked', None):
request_json = request.get_json()
if 'uri' in request_json:
try:
uri = Uri(request_json['uri'])
if uri.object_type in [Uri.ObjectType.album, Uri.ObjectType.artist, Uri.ObjectType.playlist]:
context = Context(uri)
net = database.get_authed_network(username)
player = Player(net)
device = None
if 'device_name' in request_json:
devices = net.get_available_devices()
device = next((i for i in devices if i.name == request_json['device_name']), None)
player.play(context=context, device=device)
logger.info(f'played {uri}')
return jsonify({'message': 'played', 'status': 'success'}), 200
else:
return jsonify({'error': "uri not context compatible"}), 400
except ValueError:
return jsonify({'error': "malformed uri provided"}), 400
elif 'playlist_name' in request_json:
net = database.get_authed_network(username)
playlists = net.get_playlists()
if playlists is not None:
playlist_to_play = next((i for i in playlists if i.name == request_json['playlist_name']), None)
if playlist_to_play is not None:
player = Player(net)
device = None
if 'device_name' in request_json:
devices = net.get_available_devices()
device = next((i for i in devices if i.name == request_json['device_name']), None)
player.play(context=Context(playlist_to_play.uri), device=device)
logger.info(f'played {request_json["playlist_name"]}')
return jsonify({'message': 'played', 'status': 'success'}), 200
else:
return jsonify({'error': f"playlist {request_json['playlist_name']} not found"}), 404
else:
return jsonify({'error': "playlists not returned"}), 400
elif 'tracks' in request_json:
try:
uris = [Uri(i) for i in request_json['tracks']]
uris = [SpotifyTrack.get_uri_shell(i) for i in uris if i.object_type == Uri.ObjectType.track]
if len(uris) > 0:
net = database.get_authed_network(username)
player = Player(net)
device = None
if 'device_name' in request_json:
devices = net.get_available_devices()
device = next((i for i in devices if i.name == request_json['device_name']), None)
player.play(tracks=uris, device=device)
logger.info(f'played tracks')
return jsonify({'message': 'played', 'status': 'success'}), 200
else:
return jsonify({'error': "no track uris provided"}), 400
except ValueError:
return jsonify({'error': "uris failed to parse"}), 400
else:
return jsonify({'error': "no uris provided"}), 400
else:
return jsonify({'error': "spotify not linked"}), 400
@blueprint.route('/playlists', methods=['GET'])
@login_or_basic_auth
def get_playlists(username=None):
pulled_user = database.get_user_doc_ref(username)
user_ref = database.get_user_doc_ref(username)
playlists = pulled_user.collection(u'playlists')
playlists = user_ref.collection(u'playlists')
playlist_docs = [i.to_dict() for i in playlists.stream()]

View File

@ -2,8 +2,7 @@ from google.cloud import firestore
import logging
from spotframework.net.user import NetworkUser
from spotframework.net.network import Network
import spotify.db.database as database
db = firestore.Client()
@ -18,13 +17,7 @@ def create_playlist(username, name):
if len(users) == 1:
user_dict = users[0].to_dict()
spotify_keys = db.document('key/spotify').get().to_dict()
net = Network(NetworkUser(spotify_keys['clientid'],
spotify_keys['clientsecret'],
user_dict['refresh_token'],
user_dict['access_token']))
net = database.get_authed_network(username)
playlist = net.create_playlist(net.user.username, name)

View File

@ -1,13 +1,59 @@
from google.cloud import firestore
import logging
from datetime import timedelta, datetime, timezone
from typing import List, Optional
from werkzeug.security import check_password_hash
from spotframework.net.network import Network
from spotify.db.user import DatabaseUser
db = firestore.Client()
logger = logging.getLogger(__name__)
def refresh_token_database_callback(user):
if isinstance(user, DatabaseUser):
user_ref = get_user_doc_ref(user.user_id)
user_ref.update({
'access_token': user.accesstoken,
'last_refreshed': user.last_refreshed,
'token_expiry': user.token_expiry
})
logger.debug(f'{user.user_id} database entry updated')
else:
logger.error('user has no attached id')
def get_authed_network(username):
user = get_user_doc_ref(username)
if user:
user_dict = user.get().to_dict()
if user_dict.get('spotify_linked', None):
spotify_keys = db.document('key/spotify').get().to_dict()
user_obj = DatabaseUser(client_id=spotify_keys['clientid'],
client_secret=spotify_keys['clientsecret'],
refresh_token=user_dict['refresh_token'],
user_id=username,
access_token=user_dict['access_token'])
user_obj.on_refresh.append(refresh_token_database_callback)
if user_dict['last_refreshed'] + timedelta(seconds=user_dict['token_expiry'] - 1) \
< datetime.now(timezone.utc):
user_obj.refresh_token()
user_obj.refresh_info()
return Network(user_obj)
else:
logger.error('user spotify not linked')
else:
logger.error(f'user {username} not found')
def check_user_password(username, password):
user = get_user_doc_ref(user=username)
@ -93,7 +139,7 @@ def get_user_playlist_ref_by_user_ref(user_ref: firestore.DocumentReference,
else:
logger.error(f'{username} multiple response playlists found for {playlist}')
return query
return query[0]
else:
logger.error(f'{username} no playlist found for {playlist}')

9
spotify/db/user.py Normal file
View File

@ -0,0 +1,9 @@
from spotframework.net.user import NetworkUser
class DatabaseUser(NetworkUser):
def __init__(self, client_id, client_secret, refresh_token, user_id, access_token=None):
super().__init__(client_id=client_id, client_secret=client_secret,
refresh_token=refresh_token, access_token=access_token)
self.user_id = user_id

View File

@ -8,8 +8,6 @@ from spotframework.engine.processor.shuffle import Shuffle
from spotframework.engine.processor.sort import SortReleaseDate
from spotframework.engine.processor.deduplicate import DeduplicateByID
from spotframework.net.network import Network
from spotframework.net.user import NetworkUser
from spotframework.player.player import Player
import spotify.db.database as database
from spotify.db.part_generator import PartGenerator
@ -37,8 +35,6 @@ def play_user_playlist(username,
if len(users) == 1:
user_dict = users[0].to_dict()
if parts is None and playlists is None:
logger.critical(f'no playlists to use for creation ({username})')
return None
@ -53,12 +49,7 @@ def play_user_playlist(username,
logger.critical(f'no playlists to use for creation ({username})')
return None
spotify_keys = db.document('key/spotify').get().to_dict()
net = Network(NetworkUser(spotify_keys['clientid'],
spotify_keys['clientsecret'],
user_dict['refresh_token'],
user_dict['access_token']))
net = database.get_authed_network(username)
device = None
if device_name:

View File

@ -10,8 +10,6 @@ from spotframework.engine.processor.deduplicate import DeduplicateByID
from spotframework.model.uri import Uri
from spotframework.net.network import Network
from spotframework.net.user import NetworkUser
import spotify.db.database as database
from spotify.db.part_generator import PartGenerator
@ -28,8 +26,6 @@ def run_user_playlist(username, playlist_name):
if len(users) == 1:
user_dict = users[0].to_dict()
playlist_collection = db.collection(u'spotify_users', u'{}'.format(users[0].id), 'playlists')
playlists = [i for i in playlist_collection.where(u'name', u'==', playlist_name).stream()]
@ -46,12 +42,7 @@ def run_user_playlist(username, playlist_name):
logger.critical(f'no playlists to use for creation ({username}/{playlist_name})')
return None
spotify_keys = db.document('key/spotify').get().to_dict()
net = Network(NetworkUser(spotify_keys['clientid'],
spotify_keys['clientsecret'],
user_dict['refresh_token'],
user_dict['access_token']))
net = database.get_authed_network(username)
engine = PlaylistEngine(net)