summaryrefslogtreecommitdiff
path: root/backend/decky_loader
diff options
context:
space:
mode:
Diffstat (limited to 'backend/decky_loader')
-rw-r--r--backend/decky_loader/browser.py6
-rw-r--r--backend/decky_loader/loader.py9
-rw-r--r--backend/decky_loader/locales/en-US.json21
-rw-r--r--backend/decky_loader/plugin/plugin.py1
-rw-r--r--backend/decky_loader/utilities.py38
5 files changed, 66 insertions, 9 deletions
diff --git a/backend/decky_loader/browser.py b/backend/decky_loader/browser.py
index 975a917a..fe8ae71a 100644
--- a/backend/decky_loader/browser.py
+++ b/backend/decky_loader/browser.py
@@ -150,6 +150,7 @@ class PluginBrowser:
# plugins_snapshot = self.plugins.copy()
# snapshot_string = pformat(plugins_snapshot)
# logger.debug("current plugins: %s", snapshot_string)
+
if name in self.plugins:
logger.debug("Plugin %s was found", name)
await self.plugins[name].stop(uninstall=True)
@@ -345,5 +346,10 @@ class PluginBrowser:
if name in plugin_order:
plugin_order.remove(name)
self.settings.setSetting("pluginOrder", plugin_order)
+
+ disabled_plugins: List[str] = self.settings.getSetting("disabled_plugins", [])
+ if name in disabled_plugins:
+ disabled_plugins.remove(name)
+ self.settings.setSetting("disabled_plugins", disabled_plugins)
logger.debug("Removed any settings for plugin %s", name)
diff --git a/backend/decky_loader/loader.py b/backend/decky_loader/loader.py
index e2e619f7..4574cd1d 100644
--- a/backend/decky_loader/loader.py
+++ b/backend/decky_loader/loader.py
@@ -78,6 +78,7 @@ class Loader:
self.live_reload = live_reload
self.reload_queue: ReloadQueue = Queue()
self.loop.create_task(self.handle_reloads())
+ self.context: PluginManager = server_instance
if live_reload:
self.observer = Observer()
@@ -130,7 +131,7 @@ class Loader:
async def get_plugins(self):
plugins = list(self.plugins.values())
- return [{"name": str(i), "version": i.version, "load_type": i.load_type} for i in plugins]
+ return [{"name": str(i), "version": i.version, "load_type": i.load_type, "disabled": i.disabled} for i in plugins]
async def handle_plugin_dist(self, request: web.Request):
plugin = self.plugins[request.match_info["plugin_name"]]
@@ -164,6 +165,10 @@ class Loader:
await self.ws.emit(f"loader/plugin_event", {"plugin": plugin.name, "event": event, "args": args})
plugin = PluginWrapper(file, plugin_directory, self.plugin_path, plugin_emitted_event)
+ if hasattr(self.context, "utilities") and plugin.name in await self.context.utilities.get_setting("disabled_plugins",[]):
+ plugin.disabled = True
+ self.plugins[plugin.name] = plugin
+ return
if plugin.name in self.plugins:
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")
@@ -183,7 +188,7 @@ class Loader:
print_exc()
async def dispatch_plugin(self, name: str, version: str | None, load_type: int = PluginLoadType.ESMODULE_V1.value):
- await self.ws.emit("loader/import_plugin", name, version, load_type)
+ await self.ws.emit("loader/import_plugin", name, version, load_type, True, 15000)
async def import_plugins(self):
self.logger.info(f"import plugins from {self.plugin_path}")
diff --git a/backend/decky_loader/locales/en-US.json b/backend/decky_loader/locales/en-US.json
index 836f4878..1f87fe3b 100644
--- a/backend/decky_loader/locales/en-US.json
+++ b/backend/decky_loader/locales/en-US.json
@@ -102,6 +102,7 @@
},
"no_hash": "This plugin does not have a hash, you are installing it at your own risk.",
"not_installed": "(not installed)",
+ "disabled": "The plugin will be re-enabled after installation",
"overwrite": {
"button_idle": "Overwrite",
"button_processing": "Overwriting",
@@ -133,10 +134,13 @@
"uninstall": "Uninstall",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins",
- "update_to": "Update to {{name}}"
+ "update_to": "Update to {{name}}",
+ "disable": "Disable",
+ "enable": "Enable"
},
"PluginListLabel": {
- "hidden": "Hidden from the quick access menu"
+ "hidden": "Hidden from the quick access menu",
+ "disabled": "Plugin disabled"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -152,12 +156,23 @@
"desc": "Are you sure you want to uninstall {{name}}?",
"title": "Uninstall {{name}}"
},
+ "plugin_disable": {
+ "button": "Disable",
+ "desc": "Are you sure you want to disable {{name}}?",
+ "title": "Disable {{name}}",
+ "error": "Error disabling {{name}}"
+ },
+ "plugin_enable": {
+ "error": "Error enabling {{name}}"
+ },
"plugin_update_one": "Updates available for 1 plugin!",
"plugin_update_other": "Updates available for {{count}} plugins!"
},
"PluginView": {
"hidden_one": "1 plugin is hidden from this list",
- "hidden_other": "{{count}} plugins are hidden from this list"
+ "hidden_other": "{{count}} plugins are hidden from this list",
+ "disabled_one": "1 plugin is disabled",
+ "disabled_other": "{{count}} plugins are disabled"
},
"RemoteDebugging": {
"remote_cef": {
diff --git a/backend/decky_loader/plugin/plugin.py b/backend/decky_loader/plugin/plugin.py
index 61de4b1f..a7edaa45 100644
--- a/backend/decky_loader/plugin/plugin.py
+++ b/backend/decky_loader/plugin/plugin.py
@@ -41,6 +41,7 @@ class PluginWrapper:
self.author = json["author"]
self.flags = json["flags"]
self.api_version = json["api_version"] if "api_version" in json else 0
+ self.disabled = False
self.passive = not path.isfile(self.file)
diff --git a/backend/decky_loader/utilities.py b/backend/decky_loader/utilities.py
index 69c69fe6..75593fd5 100644
--- a/backend/decky_loader/utilities.py
+++ b/backend/decky_loader/utilities.py
@@ -1,5 +1,5 @@
from __future__ import annotations
-from os import stat_result
+from os import path, stat_result
import uuid
from urllib.parse import unquote
from json.decoder import JSONDecodeError
@@ -8,7 +8,7 @@ import re
from traceback import format_exc
from stat import FILE_ATTRIBUTE_HIDDEN # pyright: ignore [reportAttributeAccessIssue, reportUnknownVariableType]
-from asyncio import StreamReader, StreamWriter, start_server, gather, open_connection
+from asyncio import StreamReader, StreamWriter, sleep, start_server, gather, open_connection
from aiohttp import ClientSession, hdrs
from aiohttp.web import Request, StreamResponse, Response, json_response, post
from typing import TYPE_CHECKING, Callable, Coroutine, Dict, Any, List, TypedDict
@@ -80,6 +80,8 @@ class Utilities:
context.ws.add_route("utilities/restart_webhelper", self.restart_webhelper)
context.ws.add_route("utilities/close_cef_socket", self.close_cef_socket)
context.ws.add_route("utilities/_call_legacy_utility", self._call_legacy_utility)
+ context.ws.add_route("utilities/enable_plugin", self.enable_plugin)
+ context.ws.add_route("utilities/disable_plugin", self.disable_plugin)
context.web_app.add_routes([
post("/methods/{method_name}", self._handle_legacy_server_method_call)
@@ -214,7 +216,7 @@ class Utilities:
async def http_request_legacy(self, method: str, url: str, extra_opts: Any = {}, timeout: int | None = None):
async with ClientSession() as web:
- res = await web.request(method, url, ssl=helpers.get_ssl_context(), timeout=timeout, **extra_opts)
+ res = await web.request(method, url, ssl=helpers.get_ssl_context(), timeout=timeout, **extra_opts) # type: ignore
text = await res.text()
return {
"status": res.status,
@@ -390,7 +392,6 @@ class Utilities:
"total": len(all),
}
-
# Based on https://stackoverflow.com/a/46422554/13174603
def start_rdt_proxy(self, ip: str, port: int):
async def pipe(reader: StreamReader, writer: StreamWriter):
@@ -474,3 +475,32 @@ class Utilities:
async def get_tab_id(self, name: str):
return (await get_tab(name)).id
+
+ async def disable_plugin(self, name: str):
+ disabled_plugins: List[str] = await self.get_setting("disabled_plugins", [])
+ if name not in disabled_plugins:
+ disabled_plugins.append(name)
+ await self.set_setting("disabled_plugins", disabled_plugins)
+
+ await self.context.plugin_loader.plugins[name].stop()
+ await self.context.ws.emit("loader/disable_plugin", name)
+
+ async def enable_plugin(self, name: str):
+ plugin_folder = self.context.plugin_browser.find_plugin_folder(name)
+ assert plugin_folder is not None
+ plugin_dir = path.join(self.context.plugin_browser.plugin_path, plugin_folder)
+
+ if name in self.context.plugin_loader.plugins:
+ plugin = self.context.plugin_loader.plugins[name]
+ if plugin.proc and plugin.proc.is_alive():
+ await plugin.stop()
+ self.context.plugin_loader.plugins.pop(name, None)
+ await sleep(1)
+
+ disabled_plugins: List[str] = await self.get_setting("disabled_plugins", [])
+
+ if name in disabled_plugins:
+ disabled_plugins.remove(name)
+ await self.set_setting("disabled_plugins", disabled_plugins)
+
+ await self.context.plugin_loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_folder) \ No newline at end of file