Running black
This commit is contained in:
@ -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"}),
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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))
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
from .core import IrisCore
|
||||
|
||||
iris = IrisCore()
|
||||
iris = IrisCore()
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user