From df18eba73c2b194ee5a700a21065d7d01f8af70c Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 3 Feb 2020 16:14:04 +1300 Subject: [PATCH] Running black --- mopidy_iris/__init__.py | 75 +--- mopidy_iris/core.py | 826 ++++++++++++++++++---------------------- mopidy_iris/frontend.py | 6 +- mopidy_iris/handlers.py | 193 +++++----- mopidy_iris/mem.py | 2 +- mopidy_iris/system.py | 50 +-- 6 files changed, 529 insertions(+), 623 deletions(-) diff --git a/mopidy_iris/__init__.py b/mopidy_iris/__init__.py index e8102b2e..3d537a8b 100755 --- a/mopidy_iris/__init__.py +++ b/mopidy_iris/__init__.py @@ -3,7 +3,7 @@ import logging, json, pathlib import pkg_resources from mopidy import config, ext -__version__ = '3.44.0' +__version__ = "3.44.0" logger = logging.getLogger(__name__) @@ -13,10 +13,10 @@ logger = logging.getLogger(__name__) # # Loads config and gets the party started. Initiates any additional frontends, etc. ## -class Extension( ext.Extension ): +class Extension(ext.Extension): - dist_name = 'Mopidy-Iris' - ext_name = 'iris' + dist_name = "Mopidy-Iris" + ext_name = "iris" version = __version__ def get_default_config(self): @@ -24,25 +24,24 @@ class Extension( ext.Extension ): def get_config_schema(self): schema = config.ConfigSchema(self.ext_name) - schema['enabled'] = config.Boolean() - schema['country'] = config.String() - schema['locale'] = config.String() - schema['spotify_authorization_url'] = config.String() - schema['lastfm_authorization_url'] = config.String() - schema['genius_authorization_url'] = config.String() - schema['data_dir'] = config.String() + schema["enabled"] = config.Boolean() + schema["country"] = config.String() + schema["locale"] = config.String() + schema["spotify_authorization_url"] = config.String() + schema["lastfm_authorization_url"] = config.String() + schema["genius_authorization_url"] = config.String() + schema["data_dir"] = config.String() return schema def setup(self, registry): from .frontend import IrisFrontend + # Add web extension - registry.add('http:app', { - 'name': self.ext_name, - 'factory': iris_factory - }) + registry.add("http:app", {"name": self.ext_name, "factory": iris_factory}) # Add our frontend - registry.add('frontend', IrisFrontend) + registry.add("frontend", IrisFrontend) + ## # Frontend factory @@ -51,44 +50,12 @@ def iris_factory(config, core): from tornado.web import StaticFileHandler from .handlers import HttpHandler, ReactRouterHandler, WebsocketHandler - path = pathlib.Path(__file__).parent / 'static' + path = pathlib.Path(__file__).parent / "static" return [ - ( - r'/http/([^/]*)', - HttpHandler, - { - 'core': core, - 'config': config - } - ), - ( - r'/ws/?', - WebsocketHandler, - { - 'core': core, - 'config': config - } - ), - ( - r'/assets/(.*)', - StaticFileHandler, - { - 'path': path / 'assets' - } - ), - ( - r'/((.*)(?:css|js|json|map)$)', - StaticFileHandler, - { - 'path': path - } - ), - ( - r'/(.*)', - ReactRouterHandler, - { - 'path': path / 'index.html' - } - ), + (r"/http/([^/]*)", HttpHandler, {"core": core, "config": config}), + (r"/ws/?", WebsocketHandler, {"core": core, "config": config}), + (r"/assets/(.*)", StaticFileHandler, {"path": path / "assets"}), + (r"/((.*)(?:css|js|json|map)$)", StaticFileHandler, {"path": path}), + (r"/(.*)", ReactRouterHandler, {"path": path / "index.html"}), ] diff --git a/mopidy_iris/core.py b/mopidy_iris/core.py index 4ec543ee..9ef2f673 100755 --- a/mopidy_iris/core.py +++ b/mopidy_iris/core.py @@ -14,12 +14,13 @@ from pathlib import Path from . import Extension from .system import IrisSystemThread -if sys.platform == 'win32': +if sys.platform == "win32": import ctypes # import logger logger = logging.getLogger(__name__) + class IrisCore(pykka.ThreadingActor): version = "" spotify_token = False @@ -32,7 +33,7 @@ class IrisCore(pykka.ThreadingActor): "seed_artists": [], "seed_genres": [], "seed_tracks": [], - "results": [] + "results": [], } ioloop = None @@ -49,16 +50,16 @@ class IrisCore(pykka.ThreadingActor): # Mopidy server is starting ## def start(self): - logger.info('Starting Iris '+Extension.version) + logger.info("Starting Iris " + Extension.version) # Load our commands from file - self.commands = self.load_from_file('commands') + self.commands = self.load_from_file("commands") ## # Mopidy is shutting down ## def stop(self): - logger.info('Stopping Iris') + logger.info("Stopping Iris") ## # Load a dict from disk @@ -67,10 +68,10 @@ class IrisCore(pykka.ThreadingActor): # @return Dict ## def load_from_file(self, name): - file_path = Path(self.config['iris']['data_dir']) / ('%s.pkl' % name) + file_path = Path(self.config["iris"]["data_dir"]) / ("%s.pkl" % name) try: - with file_path.open('rb') as f: + with file_path.open("rb") as f: content = pickle.load(f) f.close() return content @@ -85,10 +86,10 @@ class IrisCore(pykka.ThreadingActor): # @return void ## def save_to_file(self, dict, name): - file_path = Path(self.config['iris']['data_dir']) / ('%s.pkl' % name) + file_path = Path(self.config["iris"]["data_dir"]) / ("%s.pkl" % name) try: - with file_path.open('wb') as f: + with file_path.open("wb") as f: pickle.dump(dict, f, pickle.HIGHEST_PROTOCOL) pickle.close() except Exception: @@ -100,7 +101,7 @@ class IrisCore(pykka.ThreadingActor): # @return String ## def load_version(self): - file_path = pathlib.Path(__file__).parent.parent / 'IRIS_VERSION' + file_path = pathlib.Path(__file__).parent.parent / "IRIS_VERSION" try: return file_path.read_text() except Exception: @@ -113,7 +114,7 @@ class IrisCore(pykka.ThreadingActor): # @return string ## def generateGuid(self): - return ''.join(random.choices(string.ascii_uppercase + string.digits, k=12)) + return "".join(random.choices(string.ascii_uppercase + string.digits, k=12)) ## # Digest a protocol header into it's id/name parts @@ -127,7 +128,7 @@ class IrisCore(pykka.ThreadingActor): if isinstance(protocol, basestring): # make sure we strip any spaces (IE gives "element,element", proper browsers give "element, element") - protocol = [i.strip() for i in protocol.split(',')] + protocol = [i.strip() for i in protocol.split(",")] # if we've been given a valid array try: @@ -140,7 +141,7 @@ class IrisCore(pykka.ThreadingActor): except: client_id = self.generateGuid() connection_id = self.generateGuid() - username = 'Anonymous' + username = "Anonymous" generated = True # construct our protocol object, and return @@ -148,87 +149,77 @@ class IrisCore(pykka.ThreadingActor): "client_id": client_id, "connection_id": connection_id, "username": username, - "generated": generated + "generated": generated, } def send_message(self, *args, **kwargs): - callback = kwargs.get('callback', None) - data = kwargs.get('data', None) + callback = kwargs.get("callback", None) + data = kwargs.get("data", None) logger.debug(data) # Catch invalid recipient - if data['recipient'] not in self.connections: - error = 'Connection "'+data['recipient']+'" not found' + if data["recipient"] not in self.connections: + error = 'Connection "' + data["recipient"] + '" not found' logger.error(error) - error = { - 'message': error - } - if (callback): + error = {"message": error} + if callback: callback(False, error) else: return error # Sending of an error - if 'error' in data: - message = { - 'jsonrpc': '2.0', - 'error': data['error'] - } + if "error" in data: + message = {"jsonrpc": "2.0", "error": data["error"]} # Sending of a regular message else: message = { - 'jsonrpc': '2.0', - 'method': data['method'] if 'method' in data else None + "jsonrpc": "2.0", + "method": data["method"] if "method" in data else None, } - if 'id' in data: - message['id'] = data['id'] - if 'params' in data: - message['params'] = data['params'] - if 'result' in data: - message['result'] = data['result'] + if "id" in data: + message["id"] = data["id"] + if "params" in data: + message["params"] = data["params"] + if "result" in data: + message["result"] = data["result"] # Dispatch the message try: - self.connections[data['recipient']]['connection'].write_message(json_encode(message)) + self.connections[data["recipient"]]["connection"].write_message( + json_encode(message) + ) - response = { - 'message': 'Sent message to '+data['recipient'] - } - if (callback): + response = {"message": "Sent message to " + data["recipient"]} + if callback: callback(response) else: return response except: - error = 'Failed to send message to '+ data['recipient'] + error = "Failed to send message to " + data["recipient"] logger.error(error) - error = { - 'message': error - } - if (callback): + error = {"message": error} + if callback: callback(False, error) else: return error def broadcast(self, *args, **kwargs): - callback = kwargs.get('callback', None) - data = kwargs.get('data', None) + callback = kwargs.get("callback", None) + data = kwargs.get("data", None) logger.debug(data) - if 'error' in data: - message = { - 'jsonrpc': '2.0', - 'error': data['error'] - } + if "error" in data: + message = {"jsonrpc": "2.0", "error": data["error"]} else: message = { - 'jsonrpc': '2.0', - 'method': data['method'] if 'method' in data else None, - 'params': data['params'] if 'params' in data else None + "jsonrpc": "2.0", + "method": data["method"] if "method" in data else None, + "params": data["params"] if "params" in data else None, } for connection in self.connections.values(): @@ -236,22 +227,21 @@ class IrisCore(pykka.ThreadingActor): send_to_this_connection = True # Don't send the broadcast to the origin, naturally - if 'connection_id' in data: - if connection['connection_id'] == data["connection_id"]: + if "connection_id" in data: + if connection["connection_id"] == data["connection_id"]: send_to_this_connection = False if send_to_this_connection: - connection['connection'].write_message(json_encode(message)) + connection["connection"].write_message(json_encode(message)) response = { - 'message': 'Broadcast to '+str(len(self.connections))+' connections' + "message": "Broadcast to " + str(len(self.connections)) + " connections" } - if (callback): + if callback: callback(response) else: return response - ## # Connections # @@ -261,70 +251,61 @@ class IrisCore(pykka.ThreadingActor): ## def get_connections(self, *args, **kwargs): - callback = kwargs.get('callback', None) + callback = kwargs.get("callback", None) connections = [] for connection in self.connections.values(): - connections.append(connection['client']) + connections.append(connection["client"]) - response = { - 'connections': connections - } - if (callback): + response = {"connections": connections} + if callback: callback(response) else: return response def add_connection(self, *args, **kwargs): - connection = kwargs.get('connection', None) - client = kwargs.get('client', None) + connection = kwargs.get("connection", None) + client = kwargs.get("client", None) logger.debug("Connection added") logger.debug(connection) - self.connections[client['connection_id']] = { - 'client': client, - 'connection_id': client['connection_id'], - 'connection': connection + self.connections[client["connection_id"]] = { + "client": client, + "connection_id": client["connection_id"], + "connection": connection, } - self.broadcast(data={ - 'method': 'connection_added', - 'params': { - 'connection': client - } - }) + self.broadcast( + data={"method": "connection_added", "params": {"connection": client}} + ) def update_connection(self, *args, **kwargs): - callback = kwargs.get('callback', None) - data = kwargs.get('data', {}) - connection_id = data['connection_id'] + callback = kwargs.get("callback", None) + data = kwargs.get("data", {}) + connection_id = data["connection_id"] if connection_id in self.connections: - self.connections[connection_id]['client']['username'] = data['username'] - self.connections[connection_id]['client']['client_id'] = data['client_id'] - self.broadcast(data={ - 'method': "connection_changed", - 'params': { - 'connection': self.connections[connection_id]['client'] + self.connections[connection_id]["client"]["username"] = data["username"] + self.connections[connection_id]["client"]["client_id"] = data["client_id"] + self.broadcast( + data={ + "method": "connection_changed", + "params": {"connection": self.connections[connection_id]["client"]}, } - }) - response = { - 'connection': self.connections[connection_id]['client'] - } - if (callback): + ) + response = {"connection": self.connections[connection_id]["client"]} + if callback: callback(response) else: return response else: - error = 'Connection "'+data['connection_id']+'" not found' + error = 'Connection "' + data["connection_id"] + '" not found' logger.error(error) - error = { - 'message': error - } - if (callback): + error = {"message": error} + if callback: callback(False, error) else: return error @@ -332,54 +313,46 @@ class IrisCore(pykka.ThreadingActor): def remove_connection(self, connection_id): if connection_id in self.connections: try: - client = self.connections[connection_id]['client'] + client = self.connections[connection_id]["client"] del self.connections[connection_id] - self.broadcast(data={ - 'method': "connection_removed", - 'params': { - 'connection': client + self.broadcast( + data={ + "method": "connection_removed", + "params": {"connection": client}, } - }) + ) except: - logger.error('Failed to close connection to '+ connection_id) + logger.error("Failed to close connection to " + connection_id) def set_username(self, *args, **kwargs): - callback = kwargs.get('callback', None) - data = kwargs.get('data', {}) - connection_id = data['connection_id'] + callback = kwargs.get("callback", None) + data = kwargs.get("data", {}) + connection_id = data["connection_id"] if connection_id in self.connections: - self.connections[connection_id]['client']['username'] = data['username'] - self.broadcast(data={ - 'method': "connection_changed", - 'params': { - 'connection': self.connections[connection_id]['client'] + self.connections[connection_id]["client"]["username"] = data["username"] + self.broadcast( + data={ + "method": "connection_changed", + "params": {"connection": self.connections[connection_id]["client"]}, } - }) - response = { - 'connection_id': connection_id, - 'username': data['username'] - } - if (callback): + ) + response = {"connection_id": connection_id, "username": data["username"]} + if callback: callback(response) else: return response else: - error = 'Connection "'+data['connection_id']+'" not found' + error = 'Connection "' + data["connection_id"] + '" not found' logger.error(error) - error = { - 'message': error - } - if (callback): + error = {"message": error} + if callback: callback(False, error) else: return error - - - ## # System controls # @@ -387,190 +360,154 @@ class IrisCore(pykka.ThreadingActor): ## def get_config(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) # handle config setups where there is no username/password # Iris won't work properly anyway, but at least we won't get server errors - if 'spotify' in self.config and 'username' in self.config['spotify']: - spotify_username = self.config['spotify']['username'] + if "spotify" in self.config and "username" in self.config["spotify"]: + spotify_username = self.config["spotify"]["username"] else: spotify_username = False response = { - 'config': { + "config": { "is_root": self.is_root(), "spotify_username": spotify_username, - "country": self.config['iris']['country'], - "locale": self.config['iris']['locale'], - "spotify_authorization_url": self.config['iris']['spotify_authorization_url'], - "lastfm_authorization_url": self.config['iris']['lastfm_authorization_url'], - "genius_authorization_url": self.config['iris']['genius_authorization_url'] + "country": self.config["iris"]["country"], + "locale": self.config["iris"]["locale"], + "spotify_authorization_url": self.config["iris"][ + "spotify_authorization_url" + ], + "lastfm_authorization_url": self.config["iris"][ + "lastfm_authorization_url" + ], + "genius_authorization_url": self.config["iris"][ + "genius_authorization_url" + ], } } - if (callback): + if callback: callback(response) else: return response - async def get_version(self, *args, **kwargs): - callback = kwargs.get('callback', False) - url = 'https://pypi.python.org/pypi/Mopidy-Iris/json' + callback = kwargs.get("callback", False) + url = "https://pypi.python.org/pypi/Mopidy-Iris/json" http_client = AsyncHTTPClient() try: http_response = await http_client.fetch(url) response_body = json.loads(http_response.body) - latest_version = response_body['info']['version'] + latest_version = response_body["info"]["version"] current_version = self.load_version() # compare our versions, and convert result to boolean - upgrade_available = parse_version( latest_version ) > parse_version( current_version ) - upgrade_available = ( upgrade_available == 1 ) + upgrade_available = parse_version(latest_version) > parse_version( + current_version + ) + upgrade_available = upgrade_available == 1 except (urllib.request.HTTPError, urllib.request.URLError) as e: - latest_version = '0.0.0' + latest_version = "0.0.0" upgrade_available = False response = { - 'version': { - 'current': current_version, - 'latest': latest_version, - 'is_root': self.is_root(), - 'upgrade_available': upgrade_available + "version": { + "current": current_version, + "latest": latest_version, + "is_root": self.is_root(), + "upgrade_available": upgrade_available, } } - if (callback): + if callback: callback(response) else: return response - ## # Restart Mopidy # This requires sudo access to system.sh ## def restart(self, *args, **kwargs): - callback = kwargs.get('callback', False) - ioloop = kwargs.get('ioloop', False) + callback = kwargs.get("callback", False) + ioloop = kwargs.get("ioloop", False) # Trigger the action - IrisSystemThread('restart', ioloop, self.restart_callback).start() + IrisSystemThread("restart", ioloop, self.restart_callback).start() - self.broadcast(data={ - 'method': "restart_started" - }) + self.broadcast(data={"method": "restart_started"}) - response = { - 'message': "Restart started" - } - if (callback): + response = {"message": "Restart started"} + if callback: callback(response) else: return response def restart_callback(self, response, error, update): if error: - self.broadcast(data={ - 'method': "restart_error", - 'params': error - }) + self.broadcast(data={"method": "restart_error", "params": error}) elif update: - self.broadcast(data={ - 'method': "restart_updated", - 'params': update - }) + self.broadcast(data={"method": "restart_updated", "params": update}) else: - self.broadcast(data={ - 'method': "restart_finished", - 'params': response - }) - + self.broadcast(data={"method": "restart_finished", "params": response}) ## # Run an upgrade of Iris ## def upgrade(self, *args, **kwargs): - callback = kwargs.get('callback', False) - ioloop = kwargs.get('ioloop', False) + callback = kwargs.get("callback", False) + ioloop = kwargs.get("ioloop", False) - self.broadcast(data={ - 'method': "upgrade_started" - }) + self.broadcast(data={"method": "upgrade_started"}) # Trigger the action - IrisSystemThread('upgrade', ioloop, self.upgrade_callback).start() + IrisSystemThread("upgrade", ioloop, self.upgrade_callback).start() - response = { - 'message': "Upgrade started" - } + response = {"message": "Upgrade started"} - if (callback): + if callback: callback(response) else: return response def upgrade_callback(self, response, error, update): if error: - self.broadcast(data={ - 'method': "upgrade_error", - 'params': error - }) + self.broadcast(data={"method": "upgrade_error", "params": error}) elif update: - self.broadcast(data={ - 'method': "upgrade_updated", - 'params': update - }) + self.broadcast(data={"method": "upgrade_updated", "params": update}) else: - self.broadcast(data={ - 'method': "upgrade_finished", - 'params': response - }) + self.broadcast(data={"method": "upgrade_finished", "params": response}) self.restart() - ## # Run a mopidy local scan # Essetially an alias to "mopidyctl local scan" ## def local_scan(self, *args, **kwargs): - callback = kwargs.get('callback', False) - ioloop = kwargs.get('ioloop', False) + callback = kwargs.get("callback", False) + ioloop = kwargs.get("ioloop", False) # Trigger the action - IrisSystemThread('local_scan', ioloop, self.local_scan_callback).start() + IrisSystemThread("local_scan", ioloop, self.local_scan_callback).start() - self.broadcast(data={ - 'method': "local_scan_started" - }) + self.broadcast(data={"method": "local_scan_started"}) - response = { - 'message': "Local scan started" - } - if (callback): + response = {"message": "Local scan started"} + if callback: callback(response) else: return response def local_scan_callback(self, response, error, update): if error: - self.broadcast(data={ - 'method': "local_scan_error", - 'params': error - }) + self.broadcast(data={"method": "local_scan_error", "params": error}) elif update: - self.broadcast(data={ - 'method': "local_scan_updated", - 'params': update - }) + self.broadcast(data={"method": "local_scan_updated", "params": update}) else: - self.broadcast(data={ - 'method': "local_scan_finished", - 'params': response - }) - + self.broadcast(data={"method": "local_scan_finished", "params": response}) ## # Spotify Radio @@ -581,22 +518,20 @@ class IrisCore(pykka.ThreadingActor): ## def get_radio(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) - response = { - 'radio': self.radio - } - if (callback): + response = {"radio": self.radio} + if callback: callback(response) else: return response async def change_radio(self, *args, **kwargs): - callback = kwargs.get('callback', False) - data = kwargs.get('data', {}) + callback = kwargs.get("callback", False) + data = kwargs.get("data", {}) # We're starting a new radio (or forced restart) - if data['reset'] or not self.radio['enabled']: + if data["reset"] or not self.radio["enabled"]: starting = True self.initial_consume = self.core.tracklist.get_consume().get() else: @@ -604,11 +539,11 @@ class IrisCore(pykka.ThreadingActor): # fetch more tracks from Mopidy-Spotify self.radio = { - 'seed_artists': data['seed_artists'], - 'seed_genres': data['seed_genres'], - 'seed_tracks': data['seed_tracks'], - 'enabled': 1, - 'results': [] + "seed_artists": data["seed_artists"], + "seed_genres": data["seed_genres"], + "seed_tracks": data["seed_tracks"], + "enabled": 1, + "results": [], } uris = await self.load_more_tracks() @@ -620,41 +555,35 @@ class IrisCore(pykka.ThreadingActor): self.core.tracklist.set_consume(True) # We only want to play the first batch - added = self.core.tracklist.add(uris = uris[0:3]) + added = self.core.tracklist.add(uris=uris[0:3]) - if (not added.get()): + if not added.get(): logger.error("No recommendations added to queue") - self.radio['enabled'] = 0; + self.radio["enabled"] = 0 error = { - 'message': 'No recommendations added to queue', - 'radio': self.radio + "message": "No recommendations added to queue", + "radio": self.radio, } - if (callback): + if callback: callback(False, error) else: return error # Save results (minus first batch) for later use - self.radio['results'] = uris[3:] + self.radio["results"] = uris[3:] self.add_radio_metadata(added) if starting: self.core.playback.play() - self.broadcast(data={ - 'method': "radio_started", - 'params': { - 'radio': self.radio - } - }) + self.broadcast( + data={"method": "radio_started", "params": {"radio": self.radio}} + ) else: - self.broadcast(data={ - 'method': "radio_changed", - 'params': { - 'radio': self.radio - } - }) + self.broadcast( + data={"method": "radio_changed", "params": {"radio": self.radio}} + ) self.get_radio(callback=callback) return @@ -662,130 +591,145 @@ class IrisCore(pykka.ThreadingActor): # Failed fetching/adding tracks, so no-go else: logger.error("No recommendations returned by Spotify") - self.radio['enabled'] = 0 + self.radio["enabled"] = 0 error = { - 'code': 32500, - 'message': 'Could not start radio', - 'data': { - 'radio': self.radio - } + "code": 32500, + "message": "Could not start radio", + "data": {"radio": self.radio}, } - if (callback): + if callback: callback(False, error) else: return error - def stop_radio(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) self.radio = { "enabled": 0, "seed_artists": [], "seed_genres": [], "seed_tracks": [], - "results": [] + "results": [], } # restore initial consume state self.core.tracklist.set_consume(self.initial_consume) self.core.playback.stop() - self.broadcast(data={ - 'method': "radio_stopped", - 'params': { - 'radio': self.radio - } - }) + self.broadcast( + data={"method": "radio_stopped", "params": {"radio": self.radio}} + ) - response = { - 'message': 'Stopped radio' - } - if (callback): + response = {"message": "Stopped radio"} + if callback: callback(response) else: return response - async def load_more_tracks(self, *args, **kwargs): try: await self.get_spotify_token() spotify_token = self.spotify_token - access_token = spotify_token['access_token'] + access_token = spotify_token["access_token"] except: - error = 'IrisFrontend: access_token missing or invalid' + error = "IrisFrontend: access_token missing or invalid" logger.error(error) return False - url = 'https://api.spotify.com/v1/recommendations/' - url = url+'?seed_artists='+(",".join(self.radio['seed_artists'])).replace('spotify:artist:','') - url = url+'&seed_genres='+(",".join(self.radio['seed_genres'])).replace('spotify:genre:','') - url = url+'&seed_tracks='+(",".join(self.radio['seed_tracks'])).replace('spotify:track:','') - url = url+'&limit=50' + url = "https://api.spotify.com/v1/recommendations/" + url = ( + url + + "?seed_artists=" + + (",".join(self.radio["seed_artists"])).replace("spotify:artist:", "") + ) + url = ( + url + + "&seed_genres=" + + (",".join(self.radio["seed_genres"])).replace("spotify:genre:", "") + ) + url = ( + url + + "&seed_tracks=" + + (",".join(self.radio["seed_tracks"])).replace("spotify:track:", "") + ) + url = url + "&limit=50" http_client = AsyncHTTPClient() try: http_response = await http_client.fetch( - url, - 'POST', - headers={'Authorization': 'Bearer '+access_token} + url, "POST", headers={"Authorization": "Bearer " + access_token} ) response_body = json.loads(http_response.body) uris = [] - for track in response_body['tracks']: - uris.append( track['uri'] ) + for track in response_body["tracks"]: + uris.append(track["uri"]) return uris except (urllib.error.HTTPError, urllib.error.URLError) as e: error = json.loads(e.read()) - error = {'message': 'Could not fetch Spotify recommendations: '+error['error_description']} - logger.error('Could not fetch Spotify recommendations: '+error['error_description']) + error = { + "message": "Could not fetch Spotify recommendations: " + + error["error_description"] + } + logger.error( + "Could not fetch Spotify recommendations: " + error["error_description"] + ) logger.debug(error) return False - def check_for_radio_update(self): tracklistLength = self.core.tracklist.get_length().get() - if (tracklistLength < 3 and self.radio['enabled'] == 1): + if tracklistLength < 3 and self.radio["enabled"] == 1: # Grab our loaded tracks - uris = self.radio['results'] + uris = self.radio["results"] # We've run out of pre-fetched tracks, so we need to get more recommendations - if (len(uris) < 3): + if len(uris) < 3: uris = self.load_more_tracks() # Remove the next batch, and update our results - self.radio['results'] = uris[3:] + self.radio["results"] = uris[3:] # Only add the next set of uris uris = uris[0:3] - added = self.core.tracklist.add(uris = uris) + added = self.core.tracklist.add(uris=uris) self.add_radio_metadata(added) - def add_radio_metadata(self, added): - seeds = '' - if len(self.radio['seed_artists']) > 0: - seeds = seeds+(','.join(self.radio['seed_artists'])).replace('spotify:artist:','spotify_artist_') - if len(self.radio['seed_tracks']) > 0: - if seeds != '': seeds = seeds+',' - seeds = seeds+(','.join(self.radio['seed_tracks'])).replace('spotify:track:','spotify_track_') - if len(self.radio['seed_genres']) > 0: - if seeds != '': seeds = seeds+',' - seeds = seeds+(','.join(self.radio['seed_genres'])).replace('spotify:genre:','spotify_genre_') + seeds = "" + if len(self.radio["seed_artists"]) > 0: + seeds = seeds + (",".join(self.radio["seed_artists"])).replace( + "spotify:artist:", "spotify_artist_" + ) + if len(self.radio["seed_tracks"]) > 0: + if seeds != "": + seeds = seeds + "," + seeds = seeds + (",".join(self.radio["seed_tracks"])).replace( + "spotify:track:", "spotify_track_" + ) + if len(self.radio["seed_genres"]) > 0: + if seeds != "": + seeds = seeds + "," + seeds = seeds + (",".join(self.radio["seed_genres"])).replace( + "spotify:genre:", "spotify_genre_" + ) - metadata = {'tlids': [], 'added_by': 'Radio', 'added_from': 'iris:radio:'+seeds} + metadata = { + "tlids": [], + "added_by": "Radio", + "added_from": "iris:radio:" + seeds, + } for added_tltrack in added.get(): - metadata['tlids'].append(added_tltrack.tlid) + metadata["tlids"].append(added_tltrack.tlid) self.add_queue_metadata(data=metadata) - ## # Additional queue metadata # @@ -794,57 +738,53 @@ class IrisCore(pykka.ThreadingActor): ## def get_queue_metadata(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) - response = { - 'queue_metadata': self.queue_metadata - } - if (callback): + response = {"queue_metadata": self.queue_metadata} + if callback: callback(response) else: return response def add_queue_metadata(self, *args, **kwargs): - callback = kwargs.get('callback', False) - data = kwargs.get('data', {}) + callback = kwargs.get("callback", False) + data = kwargs.get("data", {}) - for tlid in data['tlids']: + for tlid in data["tlids"]: item = { - 'tlid': tlid, - 'added_from': data['added_from'] if 'added_from' in data else None, - 'added_by': data['added_by'] if 'added_by' in data else None + "tlid": tlid, + "added_from": data["added_from"] if "added_from" in data else None, + "added_by": data["added_by"] if "added_by" in data else None, } - self.queue_metadata['tlid_'+str(tlid)] = item + self.queue_metadata["tlid_" + str(tlid)] = item - self.broadcast(data={ - 'method': 'queue_metadata_changed', - 'params': { - 'queue_metadata': self.queue_metadata + self.broadcast( + data={ + "method": "queue_metadata_changed", + "params": {"queue_metadata": self.queue_metadata}, } - }) + ) - response = { - 'message': 'Added queue metadata' - } - if (callback): + response = {"message": "Added queue metadata"} + if callback: callback(response) else: return response def clean_queue_metadata(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) cleaned_queue_metadata = {} for tltrack in self.core.tracklist.get_tl_tracks().get(): # if we have metadata for this track, push it through to cleaned dictionary - if 'tlid_'+str(tltrack.tlid) in self.queue_metadata: - cleaned_queue_metadata['tlid_'+str(tltrack.tlid)] = self.queue_metadata['tlid_'+str(tltrack.tlid)] + if "tlid_" + str(tltrack.tlid) in self.queue_metadata: + cleaned_queue_metadata[ + "tlid_" + str(tltrack.tlid) + ] = self.queue_metadata["tlid_" + str(tltrack.tlid)] self.queue_metadata = cleaned_queue_metadata - - ## # Commands # @@ -852,69 +792,62 @@ class IrisCore(pykka.ThreadingActor): ## def get_commands(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) - response = { - 'commands': self.commands - } - if (callback): + response = {"commands": self.commands} + if callback: callback(response) else: return response def set_commands(self, *args, **kwargs): - callback = kwargs.get('callback', False) - data = kwargs.get('data', {}) + callback = kwargs.get("callback", False) + data = kwargs.get("data", {}) # Update our temporary variable - self.commands = data['commands'] + self.commands = data["commands"] # Save the new commands to file storage - self.save_to_file(self.commands, 'commands') + self.save_to_file(self.commands, "commands") - self.broadcast(data={ - 'method': 'commands_changed', - 'params': { - 'commands': self.commands - } - }) + self.broadcast( + data={"method": "commands_changed", "params": {"commands": self.commands}} + ) - response = { - 'message': 'Commands saved' - } - if (callback): + response = {"message": "Commands saved"} + if callback: callback(response) else: return response async def run_command(self, *args, **kwargs): - callback = kwargs.get('callback', False) - ioloop = kwargs.get('ioloop', False) - data = kwargs.get('data', {}) + callback = kwargs.get("callback", False) + ioloop = kwargs.get("ioloop", False) + data = kwargs.get("data", {}) error = False - if str(data['id']) not in self.commands: + if str(data["id"]) not in self.commands: error = { - 'message': 'Command failed', - 'description': 'Could not find command by ID "'+str(data['id'])+'"' + "message": "Command failed", + "description": 'Could not find command by ID "' + str(data["id"]) + '"', } else: - command = self.commands[str(data['id'])] + command = self.commands[str(data["id"])] if "method" not in command: error = { - 'message': 'Command failed', - 'description': 'Missing required property "method"' + "message": "Command failed", + "description": 'Missing required property "method"', } if "url" not in command: error = { - 'message': 'Command failed', - 'description': 'Missing required property "url"' + "message": "Command failed", + "description": 'Missing required property "url"', } - logger.debug("Running command "+str(command)) + logger.debug("Running command " + str(command)) if error: - if (callback): + if callback: callback(False, error) return else: @@ -922,31 +855,43 @@ class IrisCore(pykka.ThreadingActor): # Build headers dict if additional headers are given headers = None - if 'additional_headers' in command: - d = command['additional_headers'].split('\n') - lines = list(filter(lambda x: x.find(':') > 0, d)) - fields = [(x.split(':', 1)[0].strip().lower(), x.split(':', 1)[1].strip()) for x in lines] + if "additional_headers" in command: + d = command["additional_headers"].split("\n") + lines = list(filter(lambda x: x.find(":") > 0, d)) + fields = [ + (x.split(":", 1)[0].strip().lower(), x.split(":", 1)[1].strip()) + for x in lines + ] headers = dict(fields) - if (command['method'] == 'POST'): - if 'content-type' in headers and headers['content-type'].lower() != 'application/json': - post_data = command['post_data'] + if command["method"] == "POST": + if ( + "content-type" in headers + and headers["content-type"].lower() != "application/json" + ): + post_data = command["post_data"] else: - post_data = json.dumps(command['post_data']) - request = HTTPRequest(command['url'], connect_timeout=5, method='POST', body=post_data, validate_cert=False, headers=headers) + post_data = json.dumps(command["post_data"]) + request = HTTPRequest( + command["url"], + connect_timeout=5, + method="POST", + body=post_data, + validate_cert=False, + headers=headers, + ) else: - request = HTTPRequest(command['url'], connect_timeout=5, validate_cert=False, headers=headers) + request = HTTPRequest( + command["url"], connect_timeout=5, validate_cert=False, headers=headers + ) # Make the request, and handle any request errors try: http_client = AsyncHTTPClient() command_response = await http_client.fetch(request) except Exception as e: - error = { - 'message': 'Command failed', - 'description': str(e) - } - if (callback): + error = {"message": "Command failed", "description": str(e)} + if callback: callback(False, error) return else: @@ -963,12 +908,9 @@ class IrisCore(pykka.ThreadingActor): command_response_body = "" # Finally, return the result - response = { - 'message': 'Command run', - 'response': command_response_body - } + response = {"message": "Command run", "response": command_response_body} - if (callback): + if callback: callback(response) return else: @@ -983,77 +925,75 @@ class IrisCore(pykka.ThreadingActor): ## async def get_spotify_token(self, *args, **kwargs): - callback = kwargs.get('callback', False) + callback = kwargs.get("callback", False) # Expired, so go get a new one - if (not self.spotify_token or self.spotify_token['expires_at'] <= time.time()): + if not self.spotify_token or self.spotify_token["expires_at"] <= time.time(): await self.refresh_spotify_token() - response = { - 'spotify_token': self.spotify_token - } + response = {"spotify_token": self.spotify_token} - if (callback): + if callback: callback(response) else: return response async def refresh_spotify_token(self, *args, **kwargs): - callback = kwargs.get('callback', None) + callback = kwargs.get("callback", None) # Use client_id and client_secret from config # This was introduced in Mopidy-Spotify 3.1.0 - url = 'https://auth.mopidy.com/spotify/token' + url = "https://auth.mopidy.com/spotify/token" data = { - 'client_id': self.config['spotify']['client_id'], - 'client_secret': self.config['spotify']['client_secret'], - 'grant_type': 'client_credentials' + "client_id": self.config["spotify"]["client_id"], + "client_secret": self.config["spotify"]["client_secret"], + "grant_type": "client_credentials", } try: http_client = tornado.httpclient.AsyncHTTPClient() - request = tornado.httpclient.HTTPRequest(url, method='POST', body=urllib.parse.urlencode(data)) + request = tornado.httpclient.HTTPRequest( + url, method="POST", body=urllib.parse.urlencode(data) + ) response = await self.do_fetch(http_client, request) token = json.loads(response.body) - token['expires_at'] = time.time() + token['expires_in'] + token["expires_at"] = time.time() + token["expires_in"] self.spotify_token = token - self.broadcast(data={ - 'method': 'spotify_token_changed', - 'params': { - 'spotify_token': self.spotify_token + self.broadcast( + data={ + "method": "spotify_token_changed", + "params": {"spotify_token": self.spotify_token}, } - }) + ) - response = { - 'spotify_token': token - } - if (callback): + response = {"spotify_token": token} + if callback: callback(response) else: return response except (urllib.error.HTTPError, urllib.error.URLError) as e: error = json.loads(e.read()) - error = {'message': 'Could not refresh token: '+error['error_description']} + error = { + "message": "Could not refresh token: " + error["error_description"] + } - if (callback): + if callback: callback(False, error) else: return error - ## # Detect if we're running as root ## def is_root(self): - if sys.platform == 'win32': + if sys.platform == "win32": return ctypes.windll.shell32.IsUserAnAdmin() != 0 else: return os.geteuid() == 0 - ## # Spotify authentication # @@ -1063,35 +1003,32 @@ class IrisCore(pykka.ThreadingActor): ## async def get_lyrics(self, *args, **kwargs): - callback = kwargs.get('callback', False) - request = kwargs.get('request', False) + callback = kwargs.get("callback", False) + request = kwargs.get("request", False) error = False url = "" try: - path = request.get_argument('path') - url = 'https://genius.com'+path + path = request.get_argument("path") + url = "https://genius.com" + path except Exception as e: logger.error(e) - error = { - 'message': "Path not valid", - 'description': str(e) - } + error = {"message": "Path not valid", "description": str(e)} try: - connection_id = request.get_argument('connection_id') + connection_id = request.get_argument("connection_id") if connection_id not in self.connections: error = { - 'message': 'Unauthorized request', - 'description': 'Connection '+connection_id+' not connected' + "message": "Unauthorized request", + "description": "Connection " + connection_id + " not connected", } except Exception as e: logger.error(e) error = { - 'message': "Unauthorized request", - 'description': "connection_id missing" + "message": "Unauthorized request", + "description": "connection_id missing", } if error: @@ -1104,47 +1041,38 @@ class IrisCore(pykka.ThreadingActor): except (urllib.error.HTTPError, urllib.error.URLError) as e: error = json.loads(e.read()) - error = {'message': 'Could not fetch Spotify recommendations: '+error['error_description']} - logger.error('Could not fetch Spotify recommendations: '+error['error_description']) + error = { + "message": "Could not fetch Spotify recommendations: " + + error["error_description"] + } + logger.error( + "Could not fetch Spotify recommendations: " + error["error_description"] + ) logger.debug(error) return error - ## # Simple test method to debug access to system tasks ## def test(self, *args, **kwargs): - callback = kwargs.get('callback', False) - ioloop = kwargs.get('ioloop', False) + callback = kwargs.get("callback", False) + ioloop = kwargs.get("ioloop", False) - self.broadcast(data={ - 'method': "test_started" - }) + self.broadcast(data={"method": "test_started"}) - response = { - 'message': "Running test... please wait" - } + response = {"message": "Running test... please wait"} - if (callback): + if callback: callback(response) else: return response - IrisSystemThread('test', ioloop, self.test_callback).run() + IrisSystemThread("test", ioloop, self.test_callback).run() def test_callback(self, response, error, update): if error: - self.broadcast(data={ - 'method': "test_error", - 'params': error - }) + self.broadcast(data={"method": "test_error", "params": error}) elif error: - self.broadcast(data={ - 'method': "test_updated", - 'params': update - }) + self.broadcast(data={"method": "test_updated", "params": update}) else: - self.broadcast(data={ - 'method': "test_finished", - 'params': response - }) + self.broadcast(data={"method": "test_finished", "params": response}) diff --git a/mopidy_iris/frontend.py b/mopidy_iris/frontend.py index cd235c49..2a71d9ac 100755 --- a/mopidy_iris/frontend.py +++ b/mopidy_iris/frontend.py @@ -6,8 +6,8 @@ from .mem import iris # import logger logger = logging.getLogger(__name__) -class IrisFrontend(pykka.ThreadingActor, CoreListener): +class IrisFrontend(pykka.ThreadingActor, CoreListener): def __init__(self, config, core): super().__init__() @@ -22,7 +22,7 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): iris.stop() def track_playback_ended(self, tl_track, time_position): - iris.ioloop.add_callback( functools.partial(iris.check_for_radio_update) ) + iris.ioloop.add_callback(functools.partial(iris.check_for_radio_update)) def tracklist_changed(self): - iris.ioloop.add_callback( functools.partial(iris.clean_queue_metadata) ) + iris.ioloop.add_callback(functools.partial(iris.clean_queue_metadata)) diff --git a/mopidy_iris/handlers.py b/mopidy_iris/handlers.py index 3779479d..e1b9b272 100755 --- a/mopidy_iris/handlers.py +++ b/mopidy_iris/handlers.py @@ -7,6 +7,7 @@ from .mem import iris logger = logging.getLogger(__name__) + class WebsocketHandler(tornado.websocket.WebSocketHandler): # initiate (not the actual object __init__, but run shortly after) @@ -14,7 +15,7 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler): self.core = core self.config = config self.ioloop = tornado.ioloop.IOLoop.current() - iris.ioloop = self.ioloop # Make available elsewhere in the Frontend + iris.ioloop = self.ioloop # Make available elsewhere in the Frontend def check_origin(self, origin): return True @@ -23,85 +24,104 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler): # Get the client's IP. If it's local, then use it's proxy origin ip = self.request.remote_ip - if (ip == '127.0.0.1' and hasattr(self.request.headers,'X-Forwarded-For')): - ip = self.request.headers['X-Forwarded-For'] + if ip == "127.0.0.1" and hasattr(self.request.headers, "X-Forwarded-For"): + ip = self.request.headers["X-Forwarded-For"] # Construct our initial client object, and add to our list of connections client = { - 'connection_id': iris.generateGuid(), - 'ip': ip, - 'created': datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S') + "connection_id": iris.generateGuid(), + "ip": ip, + "created": datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S"), } - self.connection_id = client['connection_id'] + self.connection_id = client["connection_id"] iris.add_connection(connection=self, client=client) - async def on_message(self, message): - logger.debug("Iris websocket message received: "+message) + logger.debug("Iris websocket message received: " + message) message = json_decode(message) - if 'id' in message: - id = message['id'] + if "id" in message: + id = message["id"] else: id = None - if 'jsonrpc' not in message: - self.handle_result(id=id, error={'id': id, 'code': 32602, 'message': 'Invalid JSON-RPC request (missing property "jsonrpc")'}) + if "jsonrpc" not in message: + self.handle_result( + id=id, + error={ + "id": id, + "code": 32602, + "message": 'Invalid JSON-RPC request (missing property "jsonrpc")', + }, + ) - if 'params' in message: - params = message['params'] + if "params" in message: + params = message["params"] # Handle hard-coded connection_id in messages # Otherwise include the origin connection of this message - if 'connection_id' not in params: - message['params']['connection_id'] = self.connection_id + if "connection_id" not in params: + message["params"]["connection_id"] = self.connection_id else: params = {} # call the method, as specified in payload - if 'method' in message: + if "method" in message: # make sure the method exists - if hasattr(iris, message['method']): + if hasattr(iris, message["method"]): try: # For async methods we need to await, but it must be ommited for syncronous methods - if asyncio.iscoroutinefunction(getattr(iris, message['method'])): - await getattr(iris, message['method'])( + if asyncio.iscoroutinefunction(getattr(iris, message["method"])): + await getattr(iris, message["method"])( ioloop=self.ioloop, data=params, callback=lambda response, error=False: self.handle_result( id=id, - method=message['method'], + method=message["method"], response=response, - error=error - ) + error=error, + ), ) else: - getattr(iris, message['method'])( + getattr(iris, message["method"])( ioloop=self.ioloop, data=params, callback=lambda response, error=False: self.handle_result( id=id, - method=message['method'], + method=message["method"], response=response, - error=error - ) + error=error, + ), ) except Exception as e: logger.error(str(e)) else: - self.handle_result(error={'id': id, 'code': 32601, 'message': 'Method "'+message['method']+'" does not exist'}, id=id) + self.handle_result( + error={ + "id": id, + "code": 32601, + "message": 'Method "' + message["method"] + '" does not exist', + }, + id=id, + ) return else: - self.handle_result(error={'id': id, 'code': 32602, 'message': 'Method key missing from request'}, id=id) + self.handle_result( + error={ + "id": id, + "code": 32602, + "message": "Method key missing from request", + }, + id=id, + ) return - def on_close(self): iris.remove_connection(connection_id=self.connection_id) @@ -110,45 +130,39 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler): # This is just our callback from an Async request ## def handle_result(self, *args, **kwargs): - id = kwargs.get('id', False) - method = kwargs.get('method', None) - response = kwargs.get('response', None) - error = kwargs.get('error', None) - request_response = { - 'id': id, - 'jsonrpc': '2.0', - 'method': method - } + id = kwargs.get("id", False) + method = kwargs.get("method", None) + response = kwargs.get("response", None) + error = kwargs.get("error", None) + request_response = {"id": id, "jsonrpc": "2.0", "method": method} # We've been given an error if error: - error['id'] = id - request_response['error'] = error + error["id"] = id + request_response["error"] = error # We've been handed an AsyncHTTPClient callback. This is the case # when our request calls subsequent external requests (eg Spotify, Genius) elif isinstance(response, tornado.httpclient.HTTPResponse): - request_response['result'] = response.body + request_response["result"] = response.body # Just a regular json object, so not an external request else: - request_response['result'] = response + request_response["result"] = response # Respond to the original request data = request_response - data['recipient'] = self.connection_id + data["recipient"] = self.connection_id iris.send_message(data=data) - - - - class HttpHandler(tornado.web.RequestHandler): - def set_default_headers(self): self.set_header("Access-Control-Allow-Origin", "*") - self.set_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Client-Security-Token, Accept-Encoding") + self.set_header( + "Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept, Authorization, Client-Security-Token, Accept-Encoding", + ) def initialize(self, core, config): self.core = core @@ -175,28 +189,25 @@ class HttpHandler(tornado.web.RequestHandler): ioloop=self.ioloop, request=self, callback=lambda response, error=False: self.handle_result( - id=id, - method=slug, - response=response, - error=error - ) + id=id, method=slug, response=response, error=error + ), ) else: getattr(iris, slug)( ioloop=self.ioloop, request=self, callback=lambda response, error=False: self.handle_result( - id=id, - method=slug, - response=response, - error=error - ) + id=id, method=slug, response=response, error=error + ), ) except Exception as e: logger.error(str(e)) else: - self.handle_result(id=id, error={'code': 32601, 'message': "Method "+slug+" does not exist"}) + self.handle_result( + id=id, + error={"code": 32601, "message": "Method " + slug + " does not exist"}, + ) return async def post(self, slug=None): @@ -204,9 +215,11 @@ class HttpHandler(tornado.web.RequestHandler): id = int(time.time()) try: - params = json.loads(self.request.body.decode('utf-8')) + params = json.loads(self.request.body.decode("utf-8")) except: - self.handle_result(id=id, error={'code': 32700, 'message': "Missing or invalid payload"}) + self.handle_result( + id=id, error={"code": 32700, "message": "Missing or invalid payload"} + ) return # make sure the method exists @@ -217,30 +230,29 @@ class HttpHandler(tornado.web.RequestHandler): data=params, request=self.request, callback=lambda response=False, error=False: self.handle_result( - id=id, - method=slug, - response=response, - error=error - ) + id=id, method=slug, response=response, error=error + ), ) else: getattr(iris, slug)( data=params, request=self.request, callback=lambda response=False, error=False: self.handle_result( - id=id, - method=slug, - response=response, - error=error - ) + id=id, method=slug, response=response, error=error + ), ) except tornado.web.HTTPError as e: - self.handle_result(id=id, error={'code': 32601, 'message': "Invalid JSON payload"}) + self.handle_result( + id=id, error={"code": 32601, "message": "Invalid JSON payload"} + ) return else: - self.handle_result(id=id, error={'code': 32601, 'message': "Method "+slug+" does not exist"}) + self.handle_result( + id=id, + error={"code": 32601, "message": "Method " + slug + " does not exist"}, + ) return ## @@ -248,41 +260,37 @@ class HttpHandler(tornado.web.RequestHandler): # This is just our callback from an Async request ## def handle_result(self, *args, **kwargs): - id = kwargs.get('id', None) - method = kwargs.get('method', None) - response = kwargs.get('response', None) - error = kwargs.get('error', None) - request_response = { - 'id': id, - 'jsonrpc': '2.0', - 'method': method - } + id = kwargs.get("id", None) + method = kwargs.get("method", None) + response = kwargs.get("response", None) + error = kwargs.get("error", None) + request_response = {"id": id, "jsonrpc": "2.0", "method": method} if error: - request_response['error'] = error + request_response["error"] = error self.set_status(400) - # We've been handed an AsyncHTTPClient callback. This is the case # when our request calls subsequent external requests (eg Spotify, Genius). # We don't need to wrap non-HTTPResponse responses as these are dicts elif isinstance(response, tornado.httpclient.HTTPResponse): # Digest JSON responses into JSON - content_type = response.headers.get('Content-Type') - if content_type.startswith('application/json') or content_type.startswith('text/json'): + content_type = response.headers.get("Content-Type") + if content_type.startswith("application/json") or content_type.startswith( + "text/json" + ): body = json.loads(response.body) # Non-JSON so just copy as-is else: body = json_encode(response.body) - request_response['result'] = body + request_response["result"] = body # Regular ol successful response else: - request_response['result'] = response - + request_response["result"] = response # Write our response @@ -305,4 +313,3 @@ class ReactRouterHandler(tornado.web.StaticFileHandler): def get(self, path=None, include_body=True): return super().get(self.path, include_body) - diff --git a/mopidy_iris/mem.py b/mopidy_iris/mem.py index eada4948..9ff836ea 100755 --- a/mopidy_iris/mem.py +++ b/mopidy_iris/mem.py @@ -1,3 +1,3 @@ from .core import IrisCore -iris = IrisCore() \ No newline at end of file +iris = IrisCore() diff --git a/mopidy_iris/system.py b/mopidy_iris/system.py index e4afc2e6..138a1b49 100755 --- a/mopidy_iris/system.py +++ b/mopidy_iris/system.py @@ -13,7 +13,10 @@ class IrisSystemPermissionError(IrisSystemError): reason = "Permission denied" def __init__(self, path): - message = "Password-less access to %s was refused. Check your /etc/sudoers file." % path.as_uri() + message = ( + "Password-less access to %s was refused. Check your /etc/sudoers file." + % path.as_uri() + ) logger.error(message) super().__init__(message) @@ -31,57 +34,56 @@ class IrisSystemThread(Thread): def get_command(self, action=None, *, non_interactive=False): if self._USE_SUDO: if non_interactive: - args = [b'sudo -n'] + args = [b"sudo -n"] else: - args = [b'sudo'] + args = [b"sudo"] else: args = [] - + if action is None: action = self.action args = args + [bytes(self.script_path), action.encode()] return args - + ## # Run the defined action ## def run(self): - logger.info("Running system action '"+self.action+"'") + logger.info("Running system action '" + self.action + "'") try: self.can_run() except IrisSystemError as e: logger.error(e) - error = { - 'message': e.reason, - 'description': e.message - } - - return { - 'error': error - } + error = {"message": e.reason, "description": e.message} + + return {"error": error} command = self.get_command() - logger.debug("Running '%s'", os.fsdecode(b' '.join(command))) - process = subprocess.Popen(command, stdout=subprocess.PIPE, encoding='utf8') + logger.debug("Running '%s'", os.fsdecode(b" ".join(command))) + process = subprocess.Popen(command, stdout=subprocess.PIPE, encoding="utf8") - lines = '' + lines = "" while True: line = process.stdout.readline() if process.poll() is not None: break if line: logger.info(line) - lines = lines+'\n'+line + lines = lines + "\n" + line # This seems to be ignored. Detected as spammy io? - #self.ioloop.add_callback(lambda: self.callback(None, None, {'output': line})) + # self.ioloop.add_callback(lambda: self.callback(None, None, {'output': line})) if process.returncode == 0: - self.ioloop.add_callback(lambda: self.callback({'output': lines}, None, None)) + self.ioloop.add_callback( + lambda: self.callback({"output": lines}, None, None) + ) else: - self.ioloop.add_callback(lambda: self.callback(None, {'error': lines}, None)) + self.ioloop.add_callback( + lambda: self.callback(None, {"error": lines}, None) + ) ## # Check if we have access to the system script (system.sh) @@ -90,8 +92,10 @@ class IrisSystemThread(Thread): ## def can_run(self, *args, **kwargs): # Attempt an empty call to our system file - command_bytes = b' '.join(self.get_command('check', non_interactive=True)) - process = subprocess.Popen(command_bytes, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + command_bytes = b" ".join(self.get_command("check", non_interactive=True)) + process = subprocess.Popen( + command_bytes, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) result, error = process.communicate() exitCode = process.wait()