diff --git a/plugin_loader/browser.py b/plugin_loader/browser.py
index 7fc8773ba..ffec26b30 100644
--- a/plugin_loader/browser.py
+++ b/plugin_loader/browser.py
@@ -9,6 +9,7 @@
from asyncio import get_event_loop
from time import time
from hashlib import sha256
+from subprocess import Popen
class PluginInstallContext:
def __init__(self, gh_url, version, hash) -> None:
@@ -35,6 +36,8 @@ def _unzip_to_plugin_dir(self, zip, name, hash):
zip_file = ZipFile(zip)
zip_file.extractall(self.plugin_path)
rename(path.join(self.plugin_path, zip_file.namelist()[0]), path.join(self.plugin_path, name))
+ Popen(["chown", "-R", "deck:deck", self.plugin_path])
+ Popen(["chmod", "-R", "555", self.plugin_path])
return True
async def _install(self, artifact, version, hash):
diff --git a/plugin_loader/loader.py b/plugin_loader/loader.py
index ae6093017..f060b5b65 100644
--- a/plugin_loader/loader.py
+++ b/plugin_loader/loader.py
@@ -2,19 +2,21 @@
from aiohttp_jinja2 import template
from watchdog.observers.polling import PollingObserver as Observer
from watchdog.events import FileSystemEventHandler
-
+from asyncio import Queue
from os import path, listdir
from logging import getLogger
+from time import time
from injector import get_tabs, get_tab
from plugin import PluginWrapper
+from traceback import print_exc
class FileChangeHandler(FileSystemEventHandler):
- def __init__(self, loader, plugin_path) -> None:
+ def __init__(self, queue, plugin_path) -> None:
super().__init__()
self.logger = getLogger("file-watcher")
- self.loader : Loader = loader
self.plugin_path = plugin_path
+ self.queue = queue
def on_created(self, event):
src_path = event.src_path
@@ -31,7 +33,7 @@ def on_created(self, event):
rel_path = path.relpath(src_path, path.commonprefix([self.plugin_path, src_path]))
plugin_dir = path.split(rel_path)[0]
main_file_path = path.join(self.plugin_path, plugin_dir, "main.py")
- self.loader.import_plugin(main_file_path, plugin_dir, refresh=True)
+ self.queue.put_nowait((main_file_path, plugin_dir, True))
def on_modified(self, event):
src_path = event.src_path
@@ -46,7 +48,7 @@ def on_modified(self, event):
# file that changed is not necessarily the one that needs to be reloaded
self.logger.debug(f"file modified: {src_path}")
plugin_dir = path.split(path.relpath(src_path, path.commonprefix([self.plugin_path, src_path])))[0]
- self.loader.import_plugin(path.join(self.plugin_path, plugin_dir, "main.py"), plugin_dir, refresh=True)
+ self.queue.put_nowait((path.join(self.plugin_path, plugin_dir, "main.py"), plugin_dir, True))
class Loader:
def __init__(self, server_instance, plugin_path, loop, live_reload=False) -> None:
@@ -55,16 +57,18 @@ def __init__(self, server_instance, plugin_path, loop, live_reload=False) -> Non
self.plugin_path = plugin_path
self.logger.info(f"plugin_path: {self.plugin_path}")
self.plugins = {}
+ self.callsigns = {}
self.import_plugins()
if live_reload:
+ self.reload_queue = Queue()
self.observer = Observer()
- self.observer.schedule(FileChangeHandler(self, plugin_path), self.plugin_path, recursive=True)
+ self.observer.schedule(FileChangeHandler(self.reload_queue, plugin_path), self.plugin_path, recursive=True)
self.observer.start()
+ self.loop.create_task(self.handle_reloads())
server_instance.add_routes([
web.get("/plugins/iframe", self.plugin_iframe_route),
- web.get("/plugins/reload", self.reload_plugins),
web.get("/plugins/load_main/{name}", self.load_plugin_main_view),
web.get("/plugins/plugin_resource/{name}/{path:.+}", self.handle_sub_route),
web.get("/plugins/load_tile/{name}", self.load_plugin_tile_view),
@@ -75,18 +79,23 @@ def import_plugin(self, file, plugin_directory, refresh=False):
try:
plugin = PluginWrapper(file, plugin_directory, self.plugin_path)
if plugin.name in self.plugins:
- if not "hot_reload" in plugin.flags and refresh:
+ if not "debug" in plugin.flags and refresh:
self.logger.info(f"Plugin {plugin.name} is already loaded and has requested to not be re-loaded")
return
else:
- self.plugins[plugin.name].stop(self.loop)
+ self.plugins[plugin.name].stop()
self.plugins.pop(plugin.name, None)
+ self.callsigns.pop(plugin.callsign, None)
if plugin.passive:
self.logger.info(f"Plugin {plugin.name} is passive")
- self.plugins[plugin.name] = plugin.start(self.loop)
+ callsign = str(time())
+ plugin.callsign = callsign
+ self.plugins[plugin.name] = plugin.start()
+ self.callsigns[callsign] = plugin
self.logger.info(f"Loaded {plugin.name}")
except Exception as e:
self.logger.error(f"Could not load {file}. {e}")
+ print_exc()
finally:
if refresh:
self.loop.create_task(self.refresh_iframe())
@@ -99,14 +108,15 @@ def import_plugins(self):
self.logger.info(f"found plugin: {directory}")
self.import_plugin(path.join(self.plugin_path, directory, "main.py"), directory)
- async def reload_plugins(self, request=None):
- self.logger.info("Re-importing plugins.")
- self.import_plugins()
+ async def handle_reloads(self):
+ while True:
+ args = await self.reload_queue.get()
+ self.import_plugin(*args)
- async def handle_plugin_method_call(self, plugin_name, method_name, **kwargs):
+ async def handle_plugin_method_call(self, callsign, method_name, **kwargs):
if method_name.startswith("_"):
raise RuntimeError("Tried to call private method")
- return await self.plugins[plugin_name].execute_method(method_name, kwargs)
+ return await self.callsigns[callsign].execute_method(method_name, kwargs)
async def get_steam_resource(self, request):
tab = (await get_tabs())[0]
@@ -116,7 +126,7 @@ async def get_steam_resource(self, request):
return web.Response(text=str(e), status=400)
async def load_plugin_main_view(self, request):
- plugin = self.plugins[request.match_info["name"]]
+ plugin = self.callsigns[request.match_info["name"]]
# open up the main template
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), 'r') as template:
@@ -124,14 +134,14 @@ async def load_plugin_main_view(self, request):
# setup the main script, plugin, and pull in the template
ret = f"""
-
-
+ ${artifact} + Version: ${version} +
+ + +