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.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 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 import spotify.db.database as database
@ -110,13 +114,90 @@ def cloud_task(func):
return cloud_task_wrapper 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']) @blueprint.route('/playlists', methods=['GET'])
@login_or_basic_auth @login_or_basic_auth
def get_playlists(username=None): 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()] playlist_docs = [i.to_dict() for i in playlists.stream()]

View File

@ -2,8 +2,7 @@ from google.cloud import firestore
import logging import logging
from spotframework.net.user import NetworkUser import spotify.db.database as database
from spotframework.net.network import Network
db = firestore.Client() db = firestore.Client()
@ -18,13 +17,7 @@ def create_playlist(username, name):
if len(users) == 1: if len(users) == 1:
user_dict = users[0].to_dict() net = database.get_authed_network(username)
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']))
playlist = net.create_playlist(net.user.username, name) playlist = net.create_playlist(net.user.username, name)

View File

@ -1,13 +1,59 @@
from google.cloud import firestore from google.cloud import firestore
import logging import logging
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 spotify.db.user import DatabaseUser
db = firestore.Client() db = firestore.Client()
logger = logging.getLogger(__name__) 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): def check_user_password(username, password):
user = get_user_doc_ref(user=username) user = get_user_doc_ref(user=username)
@ -93,7 +139,7 @@ def get_user_playlist_ref_by_user_ref(user_ref: firestore.DocumentReference,
else: else:
logger.error(f'{username} multiple response playlists found for {playlist}') logger.error(f'{username} multiple response playlists found for {playlist}')
return query return query[0]
else: else:
logger.error(f'{username} no playlist found for {playlist}') 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.sort import SortReleaseDate
from spotframework.engine.processor.deduplicate import DeduplicateByID 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 from spotframework.player.player import Player
import spotify.db.database as database import spotify.db.database as database
from spotify.db.part_generator import PartGenerator from spotify.db.part_generator import PartGenerator
@ -37,8 +35,6 @@ def play_user_playlist(username,
if len(users) == 1: if len(users) == 1:
user_dict = users[0].to_dict()
if parts is None and playlists is None: if parts is None and playlists is None:
logger.critical(f'no playlists to use for creation ({username})') logger.critical(f'no playlists to use for creation ({username})')
return None return None
@ -53,12 +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
spotify_keys = db.document('key/spotify').get().to_dict() net = database.get_authed_network(username)
net = Network(NetworkUser(spotify_keys['clientid'],
spotify_keys['clientsecret'],
user_dict['refresh_token'],
user_dict['access_token']))
device = None device = None
if device_name: if device_name:

View File

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