home redirect + http port configurable
This commit is contained in:
parent
6ffeadc576
commit
0a7cad0e2d
3 changed files with 140 additions and 126 deletions
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
'''
|
||||
"""
|
||||
Taken from flask-appconfig
|
||||
'''
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
@ -9,30 +9,32 @@ import os
|
|||
from xdg import BaseDirectory
|
||||
|
||||
|
||||
def get_conf(prefix='LARIGIRA_'):
|
||||
'''This is where everyone should get configuration from'''
|
||||
conf_dir = BaseDirectory.save_config_path('larigira')
|
||||
def get_conf(prefix="LARIGIRA_"):
|
||||
"""This is where everyone should get configuration from"""
|
||||
conf_dir = BaseDirectory.save_config_path("larigira")
|
||||
conf = {}
|
||||
conf['CONTINOUS_AUDIOSPEC'] = dict(kind='mpd', howmany=1)
|
||||
conf['MPD_HOST'] = os.getenv('MPD_HOST', 'localhost')
|
||||
conf['MPD_PORT'] = int(os.getenv('MPD_PORT', '6600'))
|
||||
conf['CACHING_TIME'] = 10
|
||||
conf['DB_URI'] = os.path.join(conf_dir, 'db.json')
|
||||
conf['SCRIPTS_PATH'] = os.path.join(conf_dir, 'scripts')
|
||||
conf['ROUTE_PREFIX'] = ''
|
||||
conf['BOOTSTRAP_SERVE_LOCAL'] = True
|
||||
conf['SECRET_KEY'] = 'Please replace me!'
|
||||
conf['MPD_WAIT_START'] = True
|
||||
conf['MPD_WAIT_START_RETRYSECS'] = 5
|
||||
conf['CHECK_SECS'] = 20 # period for checking playlist length
|
||||
conf['EVENT_TICK_SECS'] = 30 # period for scheduling events
|
||||
conf['DEBUG'] = False
|
||||
conf['LOG_CONFIG'] = False
|
||||
conf['TMPDIR'] = os.getenv('TMPDIR', '/tmp/')
|
||||
conf['FILE_PATH_SUGGESTION'] = () # tuple of paths
|
||||
conf['UI_CALENDAR_FREQUENCY_THRESHOLD'] = 4*60*60 # 4 hours
|
||||
conf['UI_CALENDAR_DATE_FMT'] = 'medium'
|
||||
conf['EVENT_FILTERS'] = []
|
||||
conf["CONTINOUS_AUDIOSPEC"] = dict(kind="mpd", howmany=1)
|
||||
conf["MPD_HOST"] = os.getenv("MPD_HOST", "localhost")
|
||||
conf["MPD_PORT"] = int(os.getenv("MPD_PORT", "6600"))
|
||||
conf["CACHING_TIME"] = 10
|
||||
conf["DB_URI"] = os.path.join(conf_dir, "db.json")
|
||||
conf["SCRIPTS_PATH"] = os.path.join(conf_dir, "scripts")
|
||||
conf["ROUTE_PREFIX"] = ""
|
||||
conf["BOOTSTRAP_SERVE_LOCAL"] = True
|
||||
conf["SECRET_KEY"] = "Please replace me!"
|
||||
conf["MPD_WAIT_START"] = True
|
||||
conf["MPD_WAIT_START_RETRYSECS"] = 5
|
||||
conf["CHECK_SECS"] = 20 # period for checking playlist length
|
||||
conf["EVENT_TICK_SECS"] = 30 # period for scheduling events
|
||||
conf["DEBUG"] = False
|
||||
conf["LOG_CONFIG"] = False
|
||||
conf["TMPDIR"] = os.getenv("TMPDIR", "/tmp/")
|
||||
conf["FILE_PATH_SUGGESTION"] = () # tuple of paths
|
||||
conf["UI_CALENDAR_FREQUENCY_THRESHOLD"] = 4 * 60 * 60 # 4 hours
|
||||
conf["UI_CALENDAR_DATE_FMT"] = "medium"
|
||||
conf["EVENT_FILTERS"] = []
|
||||
conf["HTTP_PORT"] = 5000
|
||||
conf["HOME_URL"] = "/db/calendar"
|
||||
conf.update(from_envvars(prefix=prefix))
|
||||
return conf
|
||||
|
||||
|
@ -54,15 +56,16 @@ def from_envvars(prefix=None, envvars=None, as_json=True):
|
|||
"""
|
||||
conf = {}
|
||||
if prefix is None and envvars is None:
|
||||
raise RuntimeError('Must either give prefix or envvars argument')
|
||||
raise RuntimeError("Must either give prefix or envvars argument")
|
||||
|
||||
# if it's a list, convert to dict
|
||||
if isinstance(envvars, list):
|
||||
envvars = {k: None for k in envvars}
|
||||
|
||||
if not envvars:
|
||||
envvars = {k: k[len(prefix):] for k in os.environ.keys()
|
||||
if k.startswith(prefix)}
|
||||
envvars = {
|
||||
k: k[len(prefix) :] for k in os.environ.keys() if k.startswith(prefix)
|
||||
}
|
||||
|
||||
for env_name, name in envvars.items():
|
||||
if name is None:
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
'''
|
||||
"""
|
||||
This module is for the main application logic
|
||||
'''
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from gevent import monkey
|
||||
|
||||
monkey.patch_all(subprocess=True)
|
||||
|
||||
import sys
|
||||
|
@ -29,12 +30,13 @@ def on_main_crash(*args, **kwargs):
|
|||
|
||||
class Larigira(object):
|
||||
def __init__(self):
|
||||
self.log = logging.getLogger('larigira')
|
||||
self.log = logging.getLogger("larigira")
|
||||
self.conf = get_conf()
|
||||
self.controller = Controller(self.conf)
|
||||
self.controller.link_exception(on_main_crash)
|
||||
self.http_server = WSGIServer(('', 5000),
|
||||
create_app(self.controller.q, self))
|
||||
self.http_server = WSGIServer(
|
||||
("", int(self.conf["HTTP_PORT"])), create_app(self.controller.q, self)
|
||||
)
|
||||
|
||||
def start(self):
|
||||
self.controller.start()
|
||||
|
@ -42,11 +44,11 @@ class Larigira(object):
|
|||
|
||||
|
||||
def sd_notify(ready=False, status=None):
|
||||
args = ['systemd-notify']
|
||||
args = ["systemd-notify"]
|
||||
if ready:
|
||||
args.append('--ready')
|
||||
args.append("--ready")
|
||||
if status is not None:
|
||||
args.append('--status')
|
||||
args.append("--status")
|
||||
args.append(status)
|
||||
try:
|
||||
subprocess.check_call(args)
|
||||
|
@ -55,43 +57,46 @@ def sd_notify(ready=False, status=None):
|
|||
|
||||
|
||||
def main():
|
||||
tempfile.tempdir = os.environ['TMPDIR'] = os.path.join(
|
||||
os.getenv('TMPDIR', '/tmp'),
|
||||
'larigira.%d' % os.getuid())
|
||||
if not os.path.isdir(os.environ['TMPDIR']):
|
||||
os.makedirs(os.environ['TMPDIR'])
|
||||
if get_conf()['LOG_CONFIG']:
|
||||
logging.config.fileConfig(get_conf()['LOG_CONFIG'],
|
||||
disable_existing_loggers=True)
|
||||
tempfile.tempdir = os.environ["TMPDIR"] = os.path.join(
|
||||
os.getenv("TMPDIR", "/tmp"), "larigira.%d" % os.getuid()
|
||||
)
|
||||
if not os.path.isdir(os.environ["TMPDIR"]):
|
||||
os.makedirs(os.environ["TMPDIR"])
|
||||
if get_conf()["LOG_CONFIG"]:
|
||||
logging.config.fileConfig(
|
||||
get_conf()["LOG_CONFIG"], disable_existing_loggers=True
|
||||
)
|
||||
else:
|
||||
log_format = '%(asctime)s|%(levelname)s[%(name)s:%(lineno)d] %(message)s'
|
||||
logging.basicConfig(level=logging.DEBUG if get_conf()['DEBUG'] else logging.INFO,
|
||||
format=log_format,
|
||||
datefmt='%H:%M:%S')
|
||||
if(get_conf()['MPD_WAIT_START']):
|
||||
log_format = "%(asctime)s|%(levelname)s[%(name)s:%(lineno)d] %(message)s"
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG if get_conf()["DEBUG"] else logging.INFO,
|
||||
format=log_format,
|
||||
datefmt="%H:%M:%S",
|
||||
)
|
||||
if get_conf()["MPD_WAIT_START"]:
|
||||
while True:
|
||||
try:
|
||||
get_mpd_client(get_conf())
|
||||
except Exception:
|
||||
logging.debug("Could not connect to MPD, waiting")
|
||||
sd_notify(status='Waiting MPD connection')
|
||||
sleep(int(get_conf()['MPD_WAIT_START_RETRYSECS']))
|
||||
sd_notify(status="Waiting MPD connection")
|
||||
sleep(int(get_conf()["MPD_WAIT_START_RETRYSECS"]))
|
||||
else:
|
||||
logging.info("MPD ready!")
|
||||
sd_notify(ready=True, status='Ready')
|
||||
sd_notify(ready=True, status="Ready")
|
||||
break
|
||||
|
||||
|
||||
|
||||
larigira = Larigira()
|
||||
larigira.start()
|
||||
|
||||
def sig(*args):
|
||||
print('invoked sig', args)
|
||||
larigira.controller.q.put(dict(kind='signal', args=args))
|
||||
print("invoked sig", args)
|
||||
larigira.controller.q.put(dict(kind="signal", args=args))
|
||||
|
||||
for signum in (signal.SIGHUP, signal.SIGALRM):
|
||||
gevent.signal(signum, sig, signum)
|
||||
gevent.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
144
larigira/rpc.py
144
larigira/rpc.py
|
@ -3,8 +3,16 @@ import gc
|
|||
from copy import deepcopy
|
||||
|
||||
from greenlet import greenlet
|
||||
from flask import current_app, Blueprint, Flask, jsonify, render_template, \
|
||||
request, abort
|
||||
from flask import (
|
||||
current_app,
|
||||
Blueprint,
|
||||
Flask,
|
||||
jsonify,
|
||||
render_template,
|
||||
request,
|
||||
abort,
|
||||
redirect,
|
||||
)
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask.ext.babel import Babel
|
||||
from werkzeug.contrib.cache import SimpleCache
|
||||
|
@ -12,79 +20,80 @@ from werkzeug.contrib.cache import SimpleCache
|
|||
from .dbadmin import db
|
||||
from .config import get_conf
|
||||
|
||||
rpc = Blueprint('rpc', __name__,
|
||||
url_prefix=get_conf()['ROUTE_PREFIX'] + '/api')
|
||||
viewui = Blueprint('view', __name__,
|
||||
url_prefix=get_conf()['ROUTE_PREFIX'] + '/view',
|
||||
template_folder='templates')
|
||||
rpc = Blueprint("rpc", __name__, url_prefix=get_conf()["ROUTE_PREFIX"] + "/api")
|
||||
viewui = Blueprint(
|
||||
"view",
|
||||
__name__,
|
||||
url_prefix=get_conf()["ROUTE_PREFIX"] + "/view",
|
||||
template_folder="templates",
|
||||
)
|
||||
|
||||
|
||||
def send_to_parent(kind, *args):
|
||||
'''similar to the behaviour of a ParentedLet'''
|
||||
if not hasattr(current_app, 'queue'):
|
||||
logging.debug('no parent queue; aborting send')
|
||||
"""similar to the behaviour of a ParentedLet"""
|
||||
if not hasattr(current_app, "queue"):
|
||||
logging.debug("no parent queue; aborting send")
|
||||
return
|
||||
msg = {
|
||||
'emitter': current_app._get_current_object(),
|
||||
'class': current_app._get_current_object().__class__.__name__,
|
||||
'kind': kind,
|
||||
'args': args
|
||||
"emitter": current_app._get_current_object(),
|
||||
"class": current_app._get_current_object().__class__.__name__,
|
||||
"kind": kind,
|
||||
"args": args,
|
||||
}
|
||||
current_app.queue.put(msg)
|
||||
|
||||
|
||||
@rpc.route('/')
|
||||
@rpc.route("/")
|
||||
def rpc_index():
|
||||
rules = list(current_app.url_map.iter_rules())
|
||||
return jsonify({'rules': [r.rule for r in rules
|
||||
if r.rule.startswith('/api')]})
|
||||
return jsonify({"rules": [r.rule for r in rules if r.rule.startswith("/api")]})
|
||||
|
||||
|
||||
@rpc.route('/refresh')
|
||||
@rpc.route("/refresh")
|
||||
def rpc_refresh():
|
||||
send_to_parent('refresh')
|
||||
return jsonify(dict(status='ok'))
|
||||
send_to_parent("refresh")
|
||||
return jsonify(dict(status="ok"))
|
||||
|
||||
|
||||
@rpc.route('/audiospec', methods=['GET'])
|
||||
@rpc.route("/audiospec", methods=["GET"])
|
||||
def get_audiospec():
|
||||
return jsonify(current_app.larigira.controller.player.continous_audiospec)
|
||||
|
||||
|
||||
@rpc.route('/audiospec', methods=['PUT'])
|
||||
@rpc.route("/audiospec", methods=["PUT"])
|
||||
def change_audiospec():
|
||||
player = current_app.larigira.controller.player
|
||||
if request.json is None:
|
||||
abort(400, "Must send application/json data")
|
||||
if 'spec' not in request.json or type(request.json['spec']) is not dict:
|
||||
if "spec" not in request.json or type(request.json["spec"]) is not dict:
|
||||
abort(400, "Object must have a key 'spec' whose value is an object")
|
||||
player.continous_audiospec = request.json['spec']
|
||||
if 'kind' not in request.json['spec']:
|
||||
player.continous_audiospec = request.json["spec"]
|
||||
if "kind" not in request.json["spec"]:
|
||||
abort(400, "invalid audiospec")
|
||||
return jsonify(player.continous_audiospec)
|
||||
|
||||
|
||||
@rpc.route('/audiospec', methods=['DELETE'])
|
||||
@rpc.route("/audiospec", methods=["DELETE"])
|
||||
def reset_audiospec():
|
||||
player = current_app.larigira.controller.player
|
||||
player.continous_audiospec = None
|
||||
return jsonify(player.continous_audiospec)
|
||||
|
||||
|
||||
@rpc.route('/eventsenabled/toggle', methods=['POST'])
|
||||
@rpc.route("/eventsenabled/toggle", methods=["POST"])
|
||||
def toggle_events_enabled():
|
||||
status = current_app.larigira.controller.player.events_enabled
|
||||
current_app.larigira.controller.player.events_enabled = not status
|
||||
return jsonify(dict(events_enabled=not status))
|
||||
|
||||
|
||||
@rpc.route('/eventsenabled', methods=['GET'])
|
||||
@rpc.route("/eventsenabled", methods=["GET"])
|
||||
def get_events_enabled():
|
||||
status = current_app.larigira.controller.player.events_enabled
|
||||
return jsonify(dict(events_enabled=status))
|
||||
|
||||
|
||||
@rpc.route('/eventsenabled', methods=['PUT'])
|
||||
@rpc.route("/eventsenabled", methods=["PUT"])
|
||||
def set_events_enabled():
|
||||
player = current_app.larigira.controller.player
|
||||
if request.json is None:
|
||||
|
@ -102,45 +111,42 @@ def get_scheduled_audiogen():
|
|||
for timespec_eid in events:
|
||||
orig_info = running[timespec_eid]
|
||||
info = events[timespec_eid]
|
||||
info['running_time'] = orig_info['running_time'].isoformat()
|
||||
info['audiospecs'] = orig_info['audiospecs']
|
||||
info['timespec'] = orig_info['timespec']
|
||||
info['timespec']['actions'] = {aid: spec
|
||||
for aid, spec
|
||||
in zip(info['timespec']['actions'],
|
||||
info['audiospecs'])
|
||||
}
|
||||
info['greenlet'] = hex(id(orig_info['greenlet']))
|
||||
info["running_time"] = orig_info["running_time"].isoformat()
|
||||
info["audiospecs"] = orig_info["audiospecs"]
|
||||
info["timespec"] = orig_info["timespec"]
|
||||
info["timespec"]["actions"] = {
|
||||
aid: spec
|
||||
for aid, spec in zip(info["timespec"]["actions"], info["audiospecs"])
|
||||
}
|
||||
info["greenlet"] = hex(id(orig_info["greenlet"]))
|
||||
return events
|
||||
|
||||
|
||||
@viewui.route('/status/running')
|
||||
@viewui.route("/status/running")
|
||||
def ui_wip():
|
||||
audiogens = get_scheduled_audiogen()
|
||||
return render_template('running.html',
|
||||
audiogens=sorted(
|
||||
audiogens.items(),
|
||||
key=lambda x: x[1]['running_time'])
|
||||
)
|
||||
return render_template(
|
||||
"running.html",
|
||||
audiogens=sorted(audiogens.items(), key=lambda x: x[1]["running_time"]),
|
||||
)
|
||||
|
||||
|
||||
@rpc.route('/debug/running')
|
||||
@rpc.route("/debug/running")
|
||||
def rpc_wip():
|
||||
def treeify(flat):
|
||||
roots = [obid for obid in flat if flat[obid]['parent'] not in flat]
|
||||
roots = [obid for obid in flat if flat[obid]["parent"] not in flat]
|
||||
tree = deepcopy(flat)
|
||||
for obid in tree:
|
||||
tree[obid]['children'] = {}
|
||||
tree[obid]["children"] = {}
|
||||
|
||||
to_remove = []
|
||||
for obid in tree:
|
||||
if obid in roots:
|
||||
continue
|
||||
if obid not in tree:
|
||||
current_app.logger.warning('How strange, {} not in tree'
|
||||
.format(obid))
|
||||
current_app.logger.warning("How strange, {} not in tree".format(obid))
|
||||
continue
|
||||
tree[tree[obid]['parent']]['children'][obid] = tree[obid]
|
||||
tree[tree[obid]["parent"]]["children"][obid] = tree[obid]
|
||||
to_remove.append(obid)
|
||||
|
||||
for obid in to_remove:
|
||||
|
@ -148,30 +154,28 @@ def rpc_wip():
|
|||
return tree
|
||||
|
||||
greenlets = {}
|
||||
for ob in filter(lambda obj: isinstance(obj, greenlet),
|
||||
gc.get_objects()):
|
||||
objrepr = {
|
||||
'repr': repr(ob),
|
||||
'class': ob.__class__.__name__,
|
||||
}
|
||||
if hasattr(ob, 'parent_greenlet') and ob.parent_greenlet is not None:
|
||||
objrepr['parent'] = hex(id(ob.parent_greenlet))
|
||||
for ob in filter(lambda obj: isinstance(obj, greenlet), gc.get_objects()):
|
||||
objrepr = {"repr": repr(ob), "class": ob.__class__.__name__}
|
||||
if hasattr(ob, "parent_greenlet") and ob.parent_greenlet is not None:
|
||||
objrepr["parent"] = hex(id(ob.parent_greenlet))
|
||||
else:
|
||||
objrepr['parent'] = hex(id(ob.parent)) \
|
||||
if ob.parent is not None else None
|
||||
if hasattr(ob, 'doc'):
|
||||
objrepr['doc'] = ob.doc.split('\n')[0]
|
||||
objrepr["parent"] = hex(id(ob.parent)) if ob.parent is not None else None
|
||||
if hasattr(ob, "doc"):
|
||||
objrepr["doc"] = ob.doc.split("\n")[0]
|
||||
elif ob.__doc__:
|
||||
objrepr['doc'] = ob.__doc__.split('\n')[0]
|
||||
objrepr["doc"] = ob.__doc__.split("\n")[0]
|
||||
|
||||
greenlets[hex(id(ob))] = objrepr
|
||||
|
||||
# TODO: make it a tree
|
||||
|
||||
return jsonify(dict(greenlets=greenlets,
|
||||
greenlets_tree=treeify(greenlets),
|
||||
audiogens=get_scheduled_audiogen(),
|
||||
))
|
||||
return jsonify(
|
||||
dict(
|
||||
greenlets=greenlets,
|
||||
greenlets_tree=treeify(greenlets),
|
||||
audiogens=get_scheduled_audiogen(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def babel_get_locale():
|
||||
|
@ -183,8 +187,7 @@ def babel_get_locale():
|
|||
|
||||
|
||||
def create_app(queue, larigira):
|
||||
app = Flask('larigira',
|
||||
static_url_path=get_conf()['ROUTE_PREFIX'] + '/static')
|
||||
app = Flask("larigira", static_url_path=get_conf()["ROUTE_PREFIX"] + "/static")
|
||||
app.config.update(get_conf())
|
||||
Bootstrap(app)
|
||||
babel = Babel(app)
|
||||
|
@ -192,6 +195,9 @@ def create_app(queue, larigira):
|
|||
app.register_blueprint(rpc)
|
||||
app.register_blueprint(viewui)
|
||||
app.register_blueprint(db)
|
||||
app.route("/")(
|
||||
lambda: redirect(get_conf()["ROUTE_PREFIX"] + get_conf()["HOME_URL"])
|
||||
)
|
||||
app.queue = queue
|
||||
app.larigira = larigira
|
||||
app.cache = SimpleCache()
|
||||
|
|
Loading…
Reference in a new issue