summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/tasks.json39
-rw-r--r--backend/helpers.py22
-rw-r--r--backend/injector.py263
-rw-r--r--backend/loader.py2
-rw-r--r--backend/main.py82
-rw-r--r--backend/updater.py66
-rw-r--r--backend/utilities.py34
-rw-r--r--frontend/rollup.config.js11
-rw-r--r--frontend/src/plugin-loader.tsx6
9 files changed, 296 insertions, 229 deletions
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index a7062156..152a0051 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -14,7 +14,9 @@
"label": "localrun",
"type": "shell",
"group": "none",
- "dependsOn" : ["buildall"],
+ "dependsOn": [
+ "buildall"
+ ],
"detail": "Check for local runs, create a plugins folder",
"command": "mkdir -p plugins",
"problemMatcher": []
@@ -49,14 +51,23 @@
"problemMatcher": []
},
{
+ "script": "watch",
+ "type": "npm",
+ "path": "frontend",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "watchfrontend",
+ "detail": "rollup -c -w",
+ "isBackground": true
+ },
+ {
"label": "buildfrontend",
"type": "npm",
"group": "build",
"detail": "rollup -c",
"script": "build",
"path": "frontend",
- "problemMatcher": [],
-
+ "problemMatcher": []
},
{
"label": "buildall",
@@ -95,7 +106,9 @@
"detail": "Run indev PluginLoader on Deck",
"type": "shell",
"group": "none",
- "dependsOn" : ["checkforsettings"],
+ "dependsOn": [
+ "checkforsettings"
+ ],
"command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PLUGIN_PATH=${config:deckdir}/homebrew/dev/plugins; export CHOWN_PLUGIN_PATH=0; export LOG_LEVEL=DEBUG; cd ${config:deckdir}/homebrew/services; echo '${config:deckpass}' | sudo -SE python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py'",
"problemMatcher": []
},
@@ -109,13 +122,27 @@
},
// ALL-IN-ONES
{
+ "label": "deployandrun",
+ "detail": "Deploy and run, skipping JS build. Useful when combined with npm:watch",
+ "dependsOrder": "sequence",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "dependsOn": [
+ "deploy",
+ "runpydeck"
+ ],
+ "problemMatcher": []
+ },
+ {
"label": "updateremote",
"detail": "Build and deploy",
"dependsOrder": "sequence",
"group": "none",
"dependsOn": [
"buildall",
- "deploy",
+ "deploy"
],
"problemMatcher": []
},
@@ -152,4 +179,4 @@
"problemMatcher": []
}
]
-}
+} \ No newline at end of file
diff --git a/backend/helpers.py b/backend/helpers.py
index 0b6e7746..c12a2fa5 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -94,18 +94,18 @@ async def download_remote_binary_to_path(url, binHash, path) -> bool:
if os.access(os.path.dirname(path), os.W_OK):
async with ClientSession() as client:
res = await client.get(url, ssl=get_ssl_context())
- if res.status == 200:
- data = BytesIO(await res.read())
- remoteHash = sha256(data.getbuffer()).hexdigest()
- if binHash == remoteHash:
- data.seek(0)
- with open(path, 'wb') as f:
- f.write(data.getbuffer())
- rv = True
- else:
- raise Exception(f"Fatal Error: Hash Mismatch for remote binary {path}@{url}")
+ if res.status == 200:
+ data = BytesIO(await res.read())
+ remoteHash = sha256(data.getbuffer()).hexdigest()
+ if binHash == remoteHash:
+ data.seek(0)
+ with open(path, 'wb') as f:
+ f.write(data.getbuffer())
+ rv = True
else:
- rv = False
+ raise Exception(f"Fatal Error: Hash Mismatch for remote binary {path}@{url}")
+ else:
+ rv = False
except:
rv = False
diff --git a/backend/injector.py b/backend/injector.py
index 5112efe1..ec7f0aa9 100644
--- a/backend/injector.py
+++ b/backend/injector.py
@@ -6,7 +6,7 @@ from traceback import format_exc
from typing import List
from aiohttp import ClientSession, WSMsgType
-from aiohttp.client_exceptions import ClientConnectorError
+from aiohttp.client_exceptions import ClientConnectorError, ClientOSError
from asyncio.exceptions import TimeoutError
import uuid
@@ -32,13 +32,16 @@ class Tab:
self.websocket = await self.client.ws_connect(self.ws_url)
async def close_websocket(self):
+ await self.websocket.close()
await self.client.close()
async def listen_for_message(self):
async for message in self.websocket:
data = message.json()
yield data
-
+ logger.warn(f"The Tab {self.title} socket has been disconnected while listening for messages.")
+ await self.close_websocket()
+
async def _send_devtools_cmd(self, dc, receive=True):
if self.websocket:
self.cmd_id += 1
@@ -52,20 +55,22 @@ class Tab:
raise RuntimeError("Websocket not opened")
async def evaluate_js(self, js, run_async=False, manage_socket=True, get_result=True):
- if manage_socket:
- await self.open_websocket()
-
- res = await self._send_devtools_cmd({
- "method": "Runtime.evaluate",
- "params": {
- "expression": js,
- "userGesture": True,
- "awaitPromise": run_async
- }
- }, get_result)
+ try:
+ if manage_socket:
+ await self.open_websocket()
- if manage_socket:
- await self.close_websocket()
+ res = await self._send_devtools_cmd({
+ "method": "Runtime.evaluate",
+ "params": {
+ "expression": js,
+ "userGesture": True,
+ "awaitPromise": run_async
+ }
+ }, get_result)
+
+ finally:
+ if manage_socket:
+ await self.close_websocket()
return res
async def has_global_var(self, var_name, manage_socket=True):
@@ -77,15 +82,17 @@ class Tab:
return res["result"]["result"]["value"]
async def close(self, manage_socket=True):
- if manage_socket:
- await self.open_websocket()
+ try:
+ if manage_socket:
+ await self.open_websocket()
- res = await self._send_devtools_cmd({
- "method": "Page.close",
- }, False)
+ res = await self._send_devtools_cmd({
+ "method": "Page.close",
+ }, False)
- if manage_socket:
- await self.close_websocket()
+ finally:
+ if manage_socket:
+ await self.close_websocket()
return res
async def enable(self):
@@ -105,78 +112,82 @@ class Tab:
}, False)
async def refresh(self):
- if manage_socket:
- await self.open_websocket()
+ try:
+ if manage_socket:
+ await self.open_websocket()
- await self._send_devtools_cmd({
- "method": "Page.reload",
- }, False)
+ await self._send_devtools_cmd({
+ "method": "Page.reload",
+ }, False)
- if manage_socket:
- await self.close_websocket()
+ finally:
+ if manage_socket:
+ await self.close_websocket()
return
async def reload_and_evaluate(self, js, manage_socket=True):
"""
Reloads the current tab, with JS to run on load via debugger
"""
- if manage_socket:
- await self.open_websocket()
-
- await self._send_devtools_cmd({
- "method": "Debugger.enable"
- }, True)
-
- await self._send_devtools_cmd({
- "method": "Runtime.evaluate",
- "params": {
- "expression": "location.reload();",
- "userGesture": True,
- "awaitPromise": False
- }
- }, False)
-
- breakpoint_res = await self._send_devtools_cmd({
- "method": "Debugger.setInstrumentationBreakpoint",
- "params": {
- "instrumentation": "beforeScriptExecution"
- }
- }, True)
+ try:
+ if manage_socket:
+ await self.open_websocket()
- logger.info(breakpoint_res)
-
- # Page finishes loading when breakpoint hits
+ await self._send_devtools_cmd({
+ "method": "Debugger.enable"
+ }, True)
- for x in range(20):
- # this works around 1/5 of the time, so just send it 8 times.
- # the js accounts for being injected multiple times allowing only one instance to run at a time anyway
await self._send_devtools_cmd({
"method": "Runtime.evaluate",
"params": {
- "expression": js,
+ "expression": "location.reload();",
"userGesture": True,
"awaitPromise": False
}
}, False)
- await self._send_devtools_cmd({
- "method": "Debugger.removeBreakpoint",
- "params": {
- "breakpointId": breakpoint_res["result"]["breakpointId"]
- }
- }, False)
+ breakpoint_res = await self._send_devtools_cmd({
+ "method": "Debugger.setInstrumentationBreakpoint",
+ "params": {
+ "instrumentation": "beforeScriptExecution"
+ }
+ }, True)
+
+ logger.info(breakpoint_res)
+
+ # Page finishes loading when breakpoint hits
+
+ for x in range(20):
+ # this works around 1/5 of the time, so just send it 8 times.
+ # the js accounts for being injected multiple times allowing only one instance to run at a time anyway
+ await self._send_devtools_cmd({
+ "method": "Runtime.evaluate",
+ "params": {
+ "expression": js,
+ "userGesture": True,
+ "awaitPromise": False
+ }
+ }, False)
- for x in range(4):
await self._send_devtools_cmd({
- "method": "Debugger.resume"
+ "method": "Debugger.removeBreakpoint",
+ "params": {
+ "breakpointId": breakpoint_res["result"]["breakpointId"]
+ }
}, False)
- await self._send_devtools_cmd({
- "method": "Debugger.disable"
- }, True)
+ for x in range(4):
+ await self._send_devtools_cmd({
+ "method": "Debugger.resume"
+ }, False)
- if manage_socket:
- await self.close_websocket()
+ await self._send_devtools_cmd({
+ "method": "Debugger.disable"
+ }, True)
+
+ finally:
+ if manage_socket:
+ await self.close_websocket()
return
async def add_script_to_evaluate_on_new_document(self, js, add_dom_wrapper=True, manage_socket=True, get_result=True):
@@ -212,32 +223,34 @@ class Tab:
(see remove_script_to_evaluate_on_new_document below)
None is returned if `get_result` is False
"""
+ try:
- wrappedjs = """
- function scriptFunc() {
- {js}
- }
- if (document.readyState === 'loading') {
- addEventListener('DOMContentLoaded', () => {
- scriptFunc();
- });
- } else {
- scriptFunc();
- }
- """.format(js=js) if add_dom_wrapper else js
-
- if manage_socket:
- await self.open_websocket()
-
- res = await self._send_devtools_cmd({
- "method": "Page.addScriptToEvaluateOnNewDocument",
- "params": {
- "source": wrappedjs
+ wrappedjs = """
+ function scriptFunc() {
+ {js}
}
- }, get_result)
+ if (document.readyState === 'loading') {
+ addEventListener('DOMContentLoaded', () => {
+ scriptFunc();
+ });
+ } else {
+ scriptFunc();
+ }
+ """.format(js=js) if add_dom_wrapper else js
+
+ if manage_socket:
+ await self.open_websocket()
- if manage_socket:
- await self.close_websocket()
+ res = await self._send_devtools_cmd({
+ "method": "Page.addScriptToEvaluateOnNewDocument",
+ "params": {
+ "source": wrappedjs
+ }
+ }, get_result)
+
+ finally:
+ if manage_socket:
+ await self.close_websocket()
return res
async def remove_script_to_evaluate_on_new_document(self, script_id, manage_socket=True):
@@ -250,18 +263,20 @@ class Tab:
The identifier of the script to remove (returned from `add_script_to_evaluate_on_new_document`)
"""
- if manage_socket:
- await self.open_websocket()
+ try:
+ if manage_socket:
+ await self.open_websocket()
- res = await self._send_devtools_cmd({
- "method": "Page.removeScriptToEvaluateOnNewDocument",
- "params": {
- "identifier": script_id
- }
- }, False)
+ res = await self._send_devtools_cmd({
+ "method": "Page.removeScriptToEvaluateOnNewDocument",
+ "params": {
+ "identifier": script_id
+ }
+ }, False)
- if manage_socket:
- await self.close_websocket()
+ finally:
+ if manage_socket:
+ await self.close_websocket()
async def has_element(self, element_name, manage_socket=True):
res = await self.evaluate_js(f"document.getElementById('{element_name}') != null", False, manage_socket)
@@ -337,28 +352,32 @@ class Tab:
async def get_tabs() -> List[Tab]:
- async with ClientSession() as web:
- res = {}
+ res = {}
- while True:
- try:
+ na = False
+ while True:
+ try:
+ async with ClientSession() as web:
res = await web.get(f"{BASE_ADDRESS}/json", timeout=3)
- except ClientConnectorError:
- logger.debug("ClientConnectorError excepted.")
+ except ClientConnectorError:
+ if not na:
logger.debug("Steam isn't available yet. Wait for a moment...")
- logger.error(format_exc())
- await sleep(5)
- except TimeoutError:
- logger.warn(f"The request to {BASE_ADDRESS}/json timed out")
- await sleep(1)
- else:
- break
-
- if res.status == 200:
- r = await res.json()
- return [Tab(i) for i in r]
+ na = True
+ await sleep(5)
+ except ClientOSError:
+ logger.warn(f"The request to {BASE_ADDRESS}/json was reset")
+ await sleep(1)
+ except TimeoutError:
+ logger.warn(f"The request to {BASE_ADDRESS}/json timed out")
+ await sleep(1)
else:
- raise Exception(f"/json did not return 200. {await res.text()}")
+ break
+
+ if res.status == 200:
+ r = await res.json()
+ return [Tab(i) for i in r]
+ else:
+ raise Exception(f"/json did not return 200. {await res.text()}")
async def get_tab(tab_name) -> Tab:
diff --git a/backend/loader.py b/backend/loader.py
index e756ba5e..2eeead36 100644
--- a/backend/loader.py
+++ b/backend/loader.py
@@ -208,7 +208,7 @@ class Loader:
return web.Response(text=ret)
async def get_steam_resource(self, request):
- tab = await get_tab("QuickAccess")
+ tab = await get_tab("SP")
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:
diff --git a/backend/main.py b/backend/main.py
index 7e3cdd2a..cd73ce26 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -4,7 +4,7 @@ from subprocess import call
if hasattr(sys, '_MEIPASS'):
call(['chmod', '-R', '755', sys._MEIPASS])
# Full imports
-from asyncio import get_event_loop, sleep
+from asyncio import new_event_loop, set_event_loop, sleep
from json import dumps, loads
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv, chmod, path
@@ -12,7 +12,7 @@ from traceback import format_exc
import aiohttp_cors
# Partial imports
-from aiohttp import ClientSession, client_exceptions, WSMsgType
+from aiohttp import client_exceptions, WSMsgType
from aiohttp.web import Application, Response, get, run_app, static
from aiohttp_jinja2 import setup as jinja_setup
@@ -21,7 +21,7 @@ from browser import PluginBrowser
from helpers import (REMOTE_DEBUGGER_UNIT, csrf_middleware, get_csrf_token,
get_home_path, get_homebrew_path, get_user,
get_user_group, set_user, set_user_group,
- stop_systemd_unit)
+ stop_systemd_unit, start_systemd_unit)
from injector import get_gamepadui_tab, Tab, get_tabs
from loader import Loader
from settings import SettingsManager
@@ -56,15 +56,15 @@ basicConfig(
logger = getLogger("Main")
-async def chown_plugin_dir(_):
+async def chown_plugin_dir():
code_chown = call(["chown", "-R", USER+":"+GROUP, CONFIG["plugin_path"]])
code_chmod = call(["chmod", "-R", "555", CONFIG["plugin_path"]])
if code_chown != 0 or code_chmod != 0:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
class PluginManager:
- def __init__(self) -> None:
- self.loop = get_event_loop()
+ def __init__(self, loop) -> None:
+ self.loop = loop
self.web_app = Application()
self.web_app.middlewares.append(csrf_middleware)
self.cors = aiohttp_cors.setup(self.web_app, defaults={
@@ -81,12 +81,19 @@ class PluginManager:
self.updater = Updater(self)
jinja_setup(self.web_app)
- 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())
- if not self.settings.getSetting("cef_forward", False):
- self.loop.create_task(stop_systemd_unit(REMOTE_DEBUGGER_UNIT))
+
+ async def startup(_):
+ if self.settings.getSetting("cef_forward", False):
+ self.loop.create_task(start_systemd_unit(REMOTE_DEBUGGER_UNIT))
+ else:
+ self.loop.create_task(stop_systemd_unit(REMOTE_DEBUGGER_UNIT))
+ if CONFIG["chown_plugin_path"] == True:
+ chown_plugin_dir()
+ self.loop.create_task(self.loader_reinjector())
+ self.loop.create_task(self.load_plugins())
+
+ self.web_app.on_startup.append(startup)
+
self.loop.set_exception_handler(self.exception_handler)
self.web_app.add_routes([get("/auth/token", self.get_auth_token)])
@@ -103,31 +110,29 @@ class PluginManager:
async def get_auth_token(self, request):
return Response(text=get_csrf_token())
- 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()
+ # await self.wait_for_server()
+ logger.debug("Loading plugins")
self.plugin_loader.import_plugins()
# await inject_to_tab("SP", "window.syncDeckyPlugins();")
async def loader_reinjector(self):
while True:
tab = None
+ nf = False
+ dc = False
while not tab:
try:
tab = await get_gamepadui_tab()
except client_exceptions.ClientConnectorError or client_exceptions.ServerDisconnectedError:
- logger.debug("Couldn't connect to debugger, waiting 5 seconds.")
+ if not dc:
+ logger.debug("Couldn't connect to debugger, waiting...")
+ dc = True
pass
except ValueError:
- logger.debug("Couldn't find GamepadUI tab, waiting 5 seconds")
+ if not nf:
+ logger.debug("Couldn't find GamepadUI tab, waiting...")
+ nf = True
pass
if not tab:
await sleep(5)
@@ -136,15 +141,20 @@ class PluginManager:
await self.inject_javascript(tab, True)
try:
async for msg in tab.listen_for_message():
- logger.debug("Page event: " + str(msg.get("method", None)))
- if msg.get("method", None) == "Page.domContentEventFired":
- if not await tab.has_global_var("deckyHasLoaded", False):
- await self.inject_javascript(tab)
- if msg.get("method", None) == "Inspector.detached" or msg.get("type", None) in (WSMsgType.CLOSED, WSMsgType.ERROR):
- logger.info("CEF has disconnected...")
- logger.debug("Exit message: " + str(msg))
- await tab.close_websocket()
- break
+ # this gets spammed a lot
+ if msg.get("method", None) != "Page.navigatedWithinDocument":
+ logger.debug("Page event: " + str(msg.get("method", None)))
+ if msg.get("method", None) == "Page.domContentEventFired":
+ if not await tab.has_global_var("deckyHasLoaded", False):
+ await self.inject_javascript(tab)
+ if msg.get("method", None) == "Inspector.detached":
+ logger.info("CEF has requested that we detach.")
+ await tab.close_websocket()
+ break
+ # If this is a forceful disconnect the loop will just stop without any failure message. In this case, injector.py will handle this for us so we don't need to close the socket.
+ # This is because of https://github.com/aio-libs/aiohttp/blob/3ee7091b40a1bc58a8d7846e7878a77640e96996/aiohttp/client_ws.py#L321
+ logger.info("CEF has disconnected...")
+ # At this point the loop starts again and we connect to the freshly started Steam client once it is ready.
except Exception as e:
logger.error("Exception while reading page events " + format_exc())
await tab.close_websocket()
@@ -166,7 +176,7 @@ class PluginManager:
# logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
# await t.close()
# await sleep(0.5)
- await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartRestart(), 100)}else{window.deckyHasLoaded = true;(async()=>{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')})();}}catch(e){console.error(e)}", False, False, False)
+ await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartRestart(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
except:
logger.info("Failed to inject JavaScript into tab\n" + format_exc())
pass
@@ -175,4 +185,6 @@ class PluginManager:
return run_app(self.web_app, host=CONFIG["server_host"], port=CONFIG["server_port"], loop=self.loop, access_log=None)
if __name__ == "__main__":
- PluginManager().run()
+ loop = new_event_loop()
+ set_event_loop(loop)
+ PluginManager(loop).run()
diff --git a/backend/updater.py b/backend/updater.py
index e20242bc..301ca396 100644
--- a/backend/updater.py
+++ b/backend/updater.py
@@ -112,20 +112,20 @@ class Updater:
async with ClientSession() as web:
async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases", ssl=helpers.get_ssl_context()) as res:
remoteVersions = await res.json()
- self.allRemoteVers = remoteVersions
- logger.debug("determining release type to find, branch is %i" % selectedBranch)
- if selectedBranch == 0:
- logger.debug("release type: release")
- self.remoteVer = next(filter(lambda ver: ver["tag_name"].startswith("v") and not ver["prerelease"] and ver["tag_name"], remoteVersions), None)
- elif selectedBranch == 1:
- logger.debug("release type: pre-release")
- self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("-pre"), remoteVersions), None)
- else:
- logger.error("release type: NOT FOUND")
- raise ValueError("no valid branch found")
- logger.info("Updated remote version information")
- tab = await get_gamepadui_tab()
- await tab.evaluate_js(f"window.DeckyPluginLoader.notifyUpdates()", False, True, False)
+ self.allRemoteVers = remoteVersions
+ logger.debug("determining release type to find, branch is %i" % selectedBranch)
+ if selectedBranch == 0:
+ logger.debug("release type: release")
+ self.remoteVer = next(filter(lambda ver: ver["tag_name"].startswith("v") and not ver["prerelease"] and ver["tag_name"], remoteVersions), None)
+ elif selectedBranch == 1:
+ logger.debug("release type: pre-release")
+ self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("-pre"), remoteVersions), None)
+ else:
+ logger.error("release type: NOT FOUND")
+ raise ValueError("no valid branch found")
+ logger.info("Updated remote version information")
+ tab = await get_gamepadui_tab()
+ await tab.evaluate_js(f"window.DeckyPluginLoader.notifyUpdates()", False, True, False)
return await self.get_version()
async def version_reloader(self):
@@ -152,18 +152,18 @@ class Updater:
async with web.request("GET", service_url, ssl=helpers.get_ssl_context(), allow_redirects=True) as res:
logger.debug("Downloading service file")
data = await res.content.read()
- logger.debug(str(data))
- service_file_path = path.join(getcwd(), "plugin_loader.service")
- try:
- with open(path.join(getcwd(), "plugin_loader.service"), "wb") as out:
- out.write(data)
- except Exception as e:
- logger.error(f"Error at %s", exc_info=e)
- with open(path.join(getcwd(), "plugin_loader.service"), 'r') as service_file:
- service_data = service_file.read()
- service_data = service_data.replace("${HOMEBREW_FOLDER}", "/home/"+helpers.get_user()+"/homebrew")
- with open(path.join(getcwd(), "plugin_loader.service"), 'w') as service_file:
- service_file.write(service_data)
+ logger.debug(str(data))
+ service_file_path = path.join(getcwd(), "plugin_loader.service")
+ try:
+ with open(path.join(getcwd(), "plugin_loader.service"), "wb") as out:
+ out.write(data)
+ except Exception as e:
+ logger.error(f"Error at %s", exc_info=e)
+ with open(path.join(getcwd(), "plugin_loader.service"), 'r') as service_file:
+ service_data = service_file.read()
+ service_data = service_data.replace("${HOMEBREW_FOLDER}", "/home/"+helpers.get_user()+"/homebrew")
+ with open(path.join(getcwd(), "plugin_loader.service"), 'w') as service_file:
+ service_file.write(service_data)
logger.debug("Saved service file")
logger.debug("Copying service file over current file.")
@@ -191,14 +191,14 @@ class Updater:
self.context.loop.create_task(tab.evaluate_js(f"window.DeckyUpdater.updateProgress({new_progress})", False, False, False))
progress = new_progress
- with open(path.join(getcwd(), ".loader.version"), "w") as out:
- out.write(version)
+ with open(path.join(getcwd(), ".loader.version"), "w") as out:
+ out.write(version)
- call(['chmod', '+x', path.join(getcwd(), "PluginLoader")])
- logger.info("Updated loader installation.")
- await tab.evaluate_js("window.DeckyUpdater.finish()", False, False)
- await self.do_restart()
- await tab.client.close()
+ call(['chmod', '+x', path.join(getcwd(), "PluginLoader")])
+ logger.info("Updated loader installation.")
+ await tab.evaluate_js("window.DeckyUpdater.finish()", False, False)
+ await self.do_restart()
+ await tab.close_websocket()
async def do_restart(self):
call(["systemctl", "daemon-reload"])
diff --git a/backend/utilities.py b/backend/utilities.py
index 61e6b523..10651d2f 100644
--- a/backend/utilities.py
+++ b/backend/utilities.py
@@ -80,12 +80,12 @@ class Utilities:
async def http_request(self, method="", url="", **kwargs):
async with ClientSession() as web:
- async with web.request(method, url, ssl=helpers.get_ssl_context(), **kwargs) as res:
- return {
- "status": res.status,
- "headers": dict(res.headers),
- "body": await res.text()
- }
+ res = await web.request(method, url, ssl=helpers.get_ssl_context(), **kwargs)
+ return {
+ "status": res.status,
+ "headers": dict(res.headers),
+ "body": await res.text()
+ }
async def ping(self, **kwargs):
return "pong"
@@ -241,17 +241,17 @@ class Utilities:
if ip != None:
self.logger.info("Connecting to React DevTools at " + ip)
async with ClientSession() as web:
- async with web.request("GET", "http://" + ip + ":8097", ssl=helpers.get_ssl_context()) as res:
- if res.status != 200:
- self.logger.error("Failed to connect to React DevTools at " + ip)
- return False
- self.start_rdt_proxy(ip, 8097)
- script = "if(!window.deckyHasConnectedRDT){window.deckyHasConnectedRDT=true;\n" + await res.text() + "\n}"
- self.logger.info("Connected to React DevTools, loading script")
- tab = await get_gamepadui_tab()
- # RDT needs to load before React itself to work.
- result = await tab.reload_and_evaluate(script)
- self.logger.info(result)
+ res = await web.request("GET", "http://" + ip + ":8097", ssl=helpers.get_ssl_context())
+ if res.status != 200:
+ self.logger.error("Failed to connect to React DevTools at " + ip)
+ return False
+ self.start_rdt_proxy(ip, 8097)
+ script = "if(!window.deckyHasConnectedRDT){window.deckyHasConnectedRDT=true;\n" + await res.text() + "\n}"
+ self.logger.info("Connected to React DevTools, loading script")
+ tab = await get_gamepadui_tab()
+ # RDT needs to load before React itself to work.
+ result = await tab.reload_and_evaluate(script)
+ self.logger.info(result)
except Exception:
self.logger.error("Failed to connect to React DevTools")
diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js
index c4bcd0a2..eef52c22 100644
--- a/frontend/rollup.config.js
+++ b/frontend/rollup.config.js
@@ -5,7 +5,12 @@ import externalGlobals from "rollup-plugin-external-globals";
import del from 'rollup-plugin-delete'
import replace from '@rollup/plugin-replace';
import typescript from '@rollup/plugin-typescript';
-import { defineConfig } from 'rollup';
+import { defineConfig, handleWarning } from 'rollup';
+
+const hiddenWarnings = [
+ "THIS_IS_UNDEFINED",
+ "EVAL"
+];
export default defineConfig({
input: 'src/index.tsx',
@@ -35,5 +40,9 @@ export default defineConfig({
chunkFileNames: (chunkInfo) => {
return 'chunk-[hash].js'
}
+ },
+ onwarn: function ( message ) {
+ if (hiddenWarnings.some(warning => message.code === warning)) return;
+ handleWarning(message);
}
});
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index 73cfdf9a..381d7954 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -249,11 +249,11 @@ class PluginLoader extends Logger {
<>
Error:{' '}
<pre>
- <code>{e instanceof Error ? e.stack : e?.toString()}</code>
+ <code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
</pre>
<>
- Please go to <FaCog style={{ display: 'inline' }} /> in Decky Loader.e settings menu if you need to
- uninstall this plugin.
+ Please go to <FaCog style={{ display: 'inline' }} /> in the Decky menu if you need to uninstall this
+ plugin.
</>
</>
);