mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-17 00:37:49 +00:00
Cleanup after merge
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ class PluginBrowser:
|
||||
|
||||
server_instance.add_routes([
|
||||
web.post("/browser/install_plugin", self.install_plugin),
|
||||
web.get("/browser/iframe", self.redirect_to_store)
|
||||
web.get("/browser/redirect", self.redirect_to_store)
|
||||
])
|
||||
|
||||
def _unzip_to_plugin_dir(self, zip, name, hash):
|
||||
|
||||
@@ -48,6 +48,10 @@ class Tab:
|
||||
await self.client.close()
|
||||
return res
|
||||
|
||||
async def get_steam_resource(self, url):
|
||||
res = await self.evaluate_js(f'(async function test() {{ return await (await fetch("{url}")).text() }})()', True)
|
||||
return res["result"]["result"]["value"]
|
||||
|
||||
def __repr__(self):
|
||||
return self.title
|
||||
|
||||
@@ -93,3 +97,15 @@ async def tab_has_global_var(tab_name, var_name):
|
||||
return False
|
||||
|
||||
return res["result"]["result"]["value"]
|
||||
|
||||
async def tab_has_element(tab_name, element_name):
|
||||
try:
|
||||
tab = await get_tab(tab_name)
|
||||
except ValueError:
|
||||
return False
|
||||
res = await tab.evaluate_js(f"document.getElementById('{element_name}') != null", False)
|
||||
|
||||
if not "result" in res or not "result" in res["result"] or not "value" in res["result"]["result"]:
|
||||
return False
|
||||
|
||||
return res["result"]["result"]["value"]
|
||||
+56
-35
@@ -1,17 +1,16 @@
|
||||
from asyncio import Queue
|
||||
from json.decoder import JSONDecodeError
|
||||
from logging import getLogger
|
||||
from os import listdir, path
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from traceback import print_exc
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp_jinja2 import template
|
||||
from genericpath import exists
|
||||
from watchdog.events import RegexMatchingEventHandler
|
||||
from watchdog.observers.inotify import InotifyObserver as Observer
|
||||
|
||||
from injector import inject_to_tab
|
||||
from injector import get_tab, inject_to_tab
|
||||
from plugin import PluginWrapper
|
||||
|
||||
|
||||
@@ -62,7 +61,6 @@ class Loader:
|
||||
self.plugin_path = plugin_path
|
||||
self.logger.info(f"plugin_path: {self.plugin_path}")
|
||||
self.plugins = {}
|
||||
self.import_plugins()
|
||||
|
||||
if live_reload:
|
||||
self.reload_queue = Queue()
|
||||
@@ -72,16 +70,20 @@ class Loader:
|
||||
self.loop.create_task(self.handle_reloads())
|
||||
|
||||
server_instance.add_routes([
|
||||
web.get("/plugins", self.handle_plugins),
|
||||
web.get("/plugins", self.get_plugins),
|
||||
web.get("/plugins/{plugin_name}/frontend_bundle", self.handle_frontend_bundle),
|
||||
web.post("/plugins/{plugin_name}/methods/{method_name}", self.handle_plugin_method_call),
|
||||
web.get("/plugins/{plugin_name}/assets/{path:.*}", self.handle_frontend_assets),
|
||||
web.post("/methods/{method_name}", self.handle_server_method_call)
|
||||
|
||||
# The following is legacy plugin code.
|
||||
web.get("/plugins/load_main/{name}", self.load_plugin_main_view),
|
||||
web.get("/plugins/plugin_resource/{name}/{path:.+}", self.handle_sub_route),
|
||||
web.get("/steam_resource/{path:.+}", self.get_steam_resource)
|
||||
])
|
||||
|
||||
def handle_plugins(self, request):
|
||||
plugins = list(map(lambda kv: dict([("name", kv[0])]), self.plugins.items()))
|
||||
return web.json_response(plugins)
|
||||
async def get_plugins(self, request):
|
||||
plugins = list(self.plugins.values())
|
||||
return web.json_response([str(i) if not i.legacy else "$LEGACY_"+str(i) for i in plugins])
|
||||
|
||||
def handle_frontend_assets(self, request):
|
||||
plugin = self.plugins[request.match_info["plugin_name"]]
|
||||
@@ -109,14 +111,13 @@ class Loader:
|
||||
self.logger.info(f"Plugin {plugin.name} is passive")
|
||||
self.plugins[plugin.name] = plugin.start()
|
||||
self.logger.info(f"Loaded {plugin.name}")
|
||||
if refresh:
|
||||
self.loop.create_task(self.reload_frontend_plugin(plugin.name))
|
||||
#self.loop.create_task(self.dispatch_plugin(plugin.name))
|
||||
except Exception as e:
|
||||
self.logger.error(f"Could not load {file}. {e}")
|
||||
print_exc()
|
||||
|
||||
async def reload_frontend_plugin(self, name):
|
||||
await inject_to_tab("SP", f"window.DeckyPluginLoader?.loadPlugin('{name}')")
|
||||
async def dispatch_plugin(self, name):
|
||||
await inject_to_tab("SP", f"window.importDeckyPlugin('{name}')")
|
||||
|
||||
def import_plugins(self):
|
||||
self.logger.info(f"import plugins from {self.plugin_path}")
|
||||
@@ -131,38 +132,58 @@ class Loader:
|
||||
args = await self.reload_queue.get()
|
||||
self.import_plugin(*args)
|
||||
|
||||
async def handle_server_method_call(self, request):
|
||||
method_name = request.match_info["method_name"]
|
||||
method_info = await request.json()
|
||||
args = method_info["args"]
|
||||
|
||||
res = {}
|
||||
try:
|
||||
r = await self.utilities.util_methods[method_name](**args)
|
||||
res["result"] = r
|
||||
res["success"] = True
|
||||
except Exception as e:
|
||||
res["result"] = str(e)
|
||||
res["success"] = False
|
||||
|
||||
return web.json_response(res)
|
||||
|
||||
async def handle_plugin_method_call(self, request):
|
||||
res = {}
|
||||
plugin = self.plugins[request.match_info["plugin_name"]]
|
||||
method_name = request.match_info["method_name"]
|
||||
|
||||
method_info = await request.json()
|
||||
args = method_info["args"]
|
||||
|
||||
try:
|
||||
method_info = await request.json()
|
||||
args = method_info["args"]
|
||||
except JSONDecodeError:
|
||||
args = {}
|
||||
try:
|
||||
if method_name.startswith("_"):
|
||||
raise RuntimeError("Tried to call private method")
|
||||
|
||||
res["result"] = await plugin.execute_method(method_name, args)
|
||||
res["success"] = True
|
||||
except Exception as e:
|
||||
res["result"] = str(e)
|
||||
res["success"] = False
|
||||
|
||||
return web.json_response(res)
|
||||
|
||||
"""
|
||||
The following methods are used to load legacy plugins, which are considered deprecated.
|
||||
I made the choice to re-add them so that the first iteration/version of the react loader
|
||||
can work as a drop-in replacement for the stable branch of the PluginLoader, so that we
|
||||
can introduce it more smoothly and give people the chance to sample the new features even
|
||||
without plugin support. They will be removed once legacy plugins are no longer relevant.
|
||||
"""
|
||||
async def load_plugin_main_view(self, request):
|
||||
plugin = self.plugins[request.match_info["name"]]
|
||||
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), 'r') as template:
|
||||
template_data = template.read()
|
||||
ret = f"""
|
||||
<script src="/static/legacy-library.js"></script>
|
||||
<script>const plugin_name = '{plugin.name}' </script>
|
||||
<base href="http://127.0.0.1:1337/plugins/plugin_resource/{plugin.name}/">
|
||||
{template_data}
|
||||
"""
|
||||
return web.Response(text=ret, content_type="text/html")
|
||||
|
||||
async def handle_sub_route(self, request):
|
||||
plugin = self.plugins[request.match_info["name"]]
|
||||
route_path = request.match_info["path"]
|
||||
self.logger.info(path)
|
||||
ret = ""
|
||||
file_path = path.join(self.plugin_path, plugin.plugin_directory, route_path)
|
||||
with open(file_path, 'r') as resource_data:
|
||||
ret = resource_data.read()
|
||||
|
||||
return web.Response(text=ret)
|
||||
|
||||
async def get_steam_resource(self, request):
|
||||
tab = await get_tab("QuickAccess")
|
||||
try:
|
||||
return web.Response(text=await tab.get_steam_resource(f"https://steamloopback.host/{request.match_info['path']}"), content_type="text/html")
|
||||
except Exception as e:
|
||||
return web.Response(text=str(e), status=400)
|
||||
|
||||
+20
-7
@@ -1,6 +1,8 @@
|
||||
from logging import DEBUG, INFO, basicConfig, getLogger
|
||||
from os import getenv
|
||||
|
||||
from aiohttp import ClientSession
|
||||
|
||||
CONFIG = {
|
||||
"plugin_path": getenv("PLUGIN_PATH", "/home/deck/homebrew/plugins"),
|
||||
"chown_plugin_path": getenv("CHOWN_PLUGIN_PATH", "1") == "1",
|
||||
@@ -8,7 +10,7 @@ CONFIG = {
|
||||
"server_port": int(getenv("SERVER_PORT", "1337")),
|
||||
"live_reload": getenv("LIVE_RELOAD", "1") == "1",
|
||||
"log_level": {"CRITICAL": 50, "ERROR": 40, "WARNING":30, "INFO": 20, "DEBUG": 10}[getenv("LOG_LEVEL", "INFO")],
|
||||
"store_url": getenv("STORE_URL", "https://sdh.tzatzi.me/browse")
|
||||
"store_url": getenv("STORE_URL", "https://beta.deckbrew.xyz")
|
||||
}
|
||||
|
||||
basicConfig(level=CONFIG["log_level"], format="[%(module)s][%(levelname)s]: %(message)s")
|
||||
@@ -21,10 +23,9 @@ from subprocess import Popen
|
||||
import aiohttp_cors
|
||||
from aiohttp.web import Application, run_app, static
|
||||
from aiohttp_jinja2 import setup as jinja_setup
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
from browser import PluginBrowser
|
||||
from injector import get_tab, inject_to_tab, tab_has_global_var
|
||||
from injector import inject_to_tab, tab_has_global_var
|
||||
from loader import Loader
|
||||
from utilities import Utilities
|
||||
|
||||
@@ -48,22 +49,34 @@ class PluginManager:
|
||||
|
||||
jinja_setup(self.web_app)
|
||||
self.web_app.on_startup.append(self.inject_javascript)
|
||||
|
||||
if CONFIG["chown_plugin_path"] == True:
|
||||
self.web_app.on_startup.append(chown_plugin_dir)
|
||||
|
||||
self.loop.create_task(self.loader_reinjector())
|
||||
|
||||
self.loop.create_task(self.load_plugins())
|
||||
self.loop.set_exception_handler(self.exception_handler)
|
||||
|
||||
for route in list(self.web_app.router.routes()):
|
||||
self.cors.add(route)
|
||||
self.web_app.add_routes([static("/static", path.join(path.dirname(__file__), 'static'))])
|
||||
|
||||
def exception_handler(self, loop, context):
|
||||
if context["message"] == "Unclosed connection":
|
||||
return
|
||||
loop.default_exception_handler(context)
|
||||
|
||||
async def wait_for_server(self):
|
||||
async with ClientSession() as web:
|
||||
while True:
|
||||
try:
|
||||
await web.get(f"http://{CONFIG['server_host']}:{CONFIG['server_port']}")
|
||||
return
|
||||
except Exception as e:
|
||||
await sleep(0.1)
|
||||
|
||||
async def load_plugins(self):
|
||||
await self.wait_for_server()
|
||||
self.plugin_loader.import_plugins()
|
||||
#await inject_to_tab("SP", "window.syncDeckyPlugins();")
|
||||
|
||||
async def loader_reinjector(self):
|
||||
while True:
|
||||
await sleep(1)
|
||||
|
||||
+8
-2
@@ -10,8 +10,6 @@ from signal import SIGINT, signal
|
||||
from sys import exit
|
||||
from time import time
|
||||
|
||||
from injector import inject_to_tab
|
||||
|
||||
|
||||
class PluginWrapper:
|
||||
def __init__(self, file, plugin_directory, plugin_path) -> None:
|
||||
@@ -24,12 +22,20 @@ class PluginWrapper:
|
||||
|
||||
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r"))
|
||||
|
||||
self.legacy = False
|
||||
self.main_view_html = json["main_view_html"] if "main_view_html" in json else ""
|
||||
self.tile_view_html = json["tile_view_html"] if "tile_view_html" in json else ""
|
||||
self.legacy = self.main_view_html or self.tile_view_html
|
||||
|
||||
self.name = json["name"]
|
||||
self.author = json["author"]
|
||||
self.flags = json["flags"]
|
||||
|
||||
self.passive = not path.isfile(self.file)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def _init(self):
|
||||
signal(SIGINT, lambda s, f: exit(0))
|
||||
|
||||
|
||||
+39
-2
@@ -1,5 +1,6 @@
|
||||
from aiohttp import ClientSession
|
||||
from aiohttp import ClientSession, web
|
||||
from injector import inject_to_tab
|
||||
from json.decoder import JSONDecodeError
|
||||
import uuid
|
||||
|
||||
class Utilities:
|
||||
@@ -11,9 +12,32 @@ class Utilities:
|
||||
"confirm_plugin_install": self.confirm_plugin_install,
|
||||
"execute_in_tab": self.execute_in_tab,
|
||||
"inject_css_into_tab": self.inject_css_into_tab,
|
||||
"remove_css_from_tab": self.remove_css_from_tab
|
||||
"remove_css_from_tab": self.remove_css_from_tab,
|
||||
"open_plugin_store": self.open_plugin_store
|
||||
}
|
||||
|
||||
if context:
|
||||
context.web_app.add_routes([
|
||||
web.post("/methods/{method_name}", self._handle_server_method_call)
|
||||
])
|
||||
|
||||
async def _handle_server_method_call(self, request):
|
||||
method_name = request.match_info["method_name"]
|
||||
try:
|
||||
method_info = await request.json()
|
||||
args = method_info["args"]
|
||||
except JSONDecodeError:
|
||||
args = {}
|
||||
res = {}
|
||||
try:
|
||||
r = await self.util_methods[method_name](**args)
|
||||
res["result"] = r
|
||||
res["success"] = True
|
||||
except Exception as e:
|
||||
res["result"] = str(e)
|
||||
res["success"] = False
|
||||
return web.json_response(res)
|
||||
|
||||
async def confirm_plugin_install(self, request_id):
|
||||
return await self.context.plugin_browser.confirm_plugin_install(request_id)
|
||||
|
||||
@@ -104,3 +128,16 @@ class Utilities:
|
||||
"success": False,
|
||||
"result": e
|
||||
}
|
||||
|
||||
async def open_plugin_store(self):
|
||||
await inject_to_tab("SP", """
|
||||
(function() {
|
||||
wpRequire = webpackJsonp.push([[], { get_require: (mod, _exports, wpRequire) => mod.exports = wpRequire }, [["get_require"]]]);
|
||||
const all = () => Object.keys(wpRequire.c).map((x) => wpRequire.c[x].exports).filter((x) => x);
|
||||
router = all().map(m => {
|
||||
if (typeof m !== "object") return undefined;
|
||||
for (let prop in m) { if (m[prop]?.Navigate) return m[prop]}
|
||||
}).find(x => x)
|
||||
router.NavigateToExternalWeb("http://127.0.0.1:1337/browser/redirect")
|
||||
})();
|
||||
""")
|
||||
Reference in New Issue
Block a user