summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-01-22 21:45:23 -0500
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-01-22 21:45:23 -0500
commit871cdbd04df02b32dbdd07467720b6eb9537178a (patch)
tree34518d6cbc9def79f8c74089fc74ae351b499dc0
parentd6dbb82378b1bd473430c3c2a8a356390e1b9b13 (diff)
downloadDecky-Framegen-871cdbd04df02b32dbdd07467720b6eb9537178a.tar.gz
Decky-Framegen-871cdbd04df02b32dbdd07467720b6eb9537178a.zip
refactor, script status now displaying
-rw-r--r--main.py116
-rwxr-xr-xsrc/index.tsx86
2 files changed, 151 insertions, 51 deletions
diff --git a/main.py b/main.py
index 5b60721..e2c4c3b 100644
--- a/main.py
+++ b/main.py
@@ -1,30 +1,42 @@
+import decky # Old-style Decky import
import os
import subprocess
import json
-import decky
+from pathlib import Path
class Plugin:
+ async def _main(self):
+ decky.logger.info("Framegen plugin loaded (old decorator-free approach).")
+
+ async def _unload(self):
+ decky.logger.info("Framegen plugin unloaded.")
+
+ # Public method: front end can call this via callable("get_installed_games")
async def get_installed_games(self) -> str:
library_file = "/home/deck/.steam/steam/steamapps/libraryfolders.vdf"
libraries = []
- # Parse libraryfolders.vdf
+ # Find library folders
if os.path.exists(library_file):
with open(library_file, "r") as f:
lines = f.readlines()
for line in lines:
if '"path"' in line:
- path = line.split('"')[3]
- libraries.append(os.path.join(path, "steamapps"))
-
- # Fetch installed games from libraries
+ folder_path = line.split('"')[3]
+ libraries.append(os.path.join(folder_path, "steamapps"))
+
+ # Gather installed games
games = []
for library in libraries:
if os.path.exists(library):
- manifest_files = [f for f in os.listdir(library) if f.startswith("appmanifest_")]
+ manifest_files = [
+ f for f in os.listdir(library)
+ if f.startswith("appmanifest_")
+ ]
for manifest in manifest_files:
- with open(os.path.join(library, manifest), "r") as f:
- lines = f.readlines()
+ manifest_path = os.path.join(library, manifest)
+ with open(manifest_path, "r") as mf:
+ lines = mf.readlines()
appid = ""
name = ""
for line in lines:
@@ -35,47 +47,75 @@ class Plugin:
if appid and name:
games.append({"appid": appid, "name": name})
- # Return games as JSON string for compatibility with TSX
return json.dumps(games)
- @callable
+ # Public method: front end can call this via callable("run_install_fgmod")
async def run_install_fgmod(self) -> dict:
try:
- # Define paths
assets_dir = Path("/home/deck/homebrew/plugins/Decky-Framegen/assets")
downloads_dir = Path.home() / "Downloads"
- # Copy files to Downloads, overwriting if they exist
+ if not assets_dir.exists():
+ decky.logger.error(f"Assets directory not found: {assets_dir}")
+ return {
+ "status": "error",
+ "message": f"Assets directory not found: {assets_dir}"
+ }
+
+ downloads_dir.mkdir(parents=True, exist_ok=True)
+
files_to_copy = ["prepare.sh", "fgmod.sh", "fgmod-uninstaller.sh"]
- for file in files_to_copy:
- src = assets_dir / file
- dest = downloads_dir / file
- if src.exists():
- dest.write_bytes(src.read_bytes())
- dest.chmod(0o755) # Make the file executable
- else:
- return {"status": "error", "message": f"{file} is missing in {assets_dir}"}
-
- # Run prepare.sh
+ for file_name in files_to_copy:
+ src = assets_dir / file_name
+ if not src.exists():
+ decky.logger.error(f"Required file missing: {src}")
+ return {
+ "status": "error",
+ "message": f"Required file missing: {file_name}"
+ }
+
+ dest = downloads_dir / file_name
+ dest.write_bytes(src.read_bytes())
+ dest.chmod(0o755)
+
prepare_script = downloads_dir / "prepare.sh"
- process = subprocess.run([str(prepare_script)], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ process = subprocess.run(
+ [str(prepare_script)],
+ capture_output=True,
+ text=True,
+ timeout=300
+ )
- # Verify key files exist, if not create it
fgmod_path = Path("/home/deck/fgmod")
- if not fgmod_path.exists():
- fgmod_path.mkdir(parents=True)
- return {"status": "info", "message": "fgmod directory was not found and has been created"}
+ fgmod_path.mkdir(parents=True, exist_ok=True)
- # Check for success message
if "All done!" not in process.stdout:
- return {"status": "error", "message": "Installation did not complete successfully"}
+ decky.logger.error("Installation did not complete successfully")
+ return {
+ "status": "error",
+ "message": "Installation did not complete successfully"
+ }
- return {"status": "success", "output": process.stdout}
- except subprocess.CalledProcessError as e:
- return {"status": "error", "message": e.stderr}
+ return {
+ "status": "success",
+ "output": process.stdout
+ }
- async def _main(self):
- decky.logger.info("Plugin loaded.")
-
- async def _unload(self):
- decky.logger.info("Plugin unloaded.")
+ except subprocess.TimeoutExpired:
+ decky.logger.error("Installation script timed out")
+ return {
+ "status": "error",
+ "message": "Installation timed out"
+ }
+ except subprocess.CalledProcessError as e:
+ decky.logger.error(f"Script error: {e.stderr}")
+ return {
+ "status": "error",
+ "message": e.stderr
+ }
+ except Exception as e:
+ decky.logger.error(f"Unexpected error: {str(e)}")
+ return {
+ "status": "error",
+ "message": str(e)
+ } \ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 011ce22..afb3cdd 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,15 +1,30 @@
-import { useState } from "react";
-import { PanelSection, PanelSectionRow, ButtonItem } from "@decky/ui";
-import { callable, definePlugin } from "@decky/api";
+import { useState, useEffect } from "react";
+import {
+ PanelSection,
+ PanelSectionRow,
+ ButtonItem,
+ Dropdown,
+ DropdownOption
+} from "@decky/ui";
+import { definePlugin, callable } from "@decky/api";
import { FaShip } from "react-icons/fa";
-const runInstallFGMod = callable<[], { status: string; message?: string; output?: string }>("run_install_fgmod");
+// "run_install_fgmod" corresponds to the Python method run_install_fgmod()
+const runInstallFGMod = callable<
+ [],
+ { status: string; message?: string; output?: string }
+>("run_install_fgmod");
-function Content() {
+// "get_installed_games" corresponds to the Python method get_installed_games()
+const fetchInstalledGames = callable<[], string>("get_installed_games");
+
+function FGModInstallerSection() {
const [installing, setInstalling] = useState(false);
- const [installResult, setInstallResult] = useState<{ status: string; output?: string; message?: string } | null>(
- null
- );
+ const [installResult, setInstallResult] = useState<{
+ status: string;
+ output?: string;
+ message?: string;
+ } | null>(null);
const handleInstallClick = async () => {
setInstalling(true);
@@ -28,7 +43,9 @@ function Content() {
{installResult && (
<PanelSectionRow>
<div>
- <strong>Status:</strong> {installResult.status === "success" ? "Success" : "Error"} <br />
+ <strong>Status:</strong>{" "}
+ {installResult.status === "success" ? "Success" : "Error"}
+ <br />
{installResult.output && (
<>
<strong>Output:</strong>
@@ -47,12 +64,55 @@ function Content() {
);
}
+function GameSelectorSection() {
+ const [games, setGames] = useState<DropdownOption[]>([]);
+ const [selectedGame, setSelectedGame] = useState<DropdownOption | null>(null);
+
+ useEffect(() => {
+ const loadGames = async () => {
+ const result = await fetchInstalledGames();
+ const gameList = JSON.parse(result) as { appid: string; name: string }[];
+ setGames(gameList.map((g) => ({ data: g.appid, label: g.name })));
+ };
+
+ loadGames();
+ }, []);
+
+ return (
+ <PanelSection title="Installed Games">
+ <PanelSectionRow>
+ <Dropdown
+ rgOptions={games}
+ selectedOption={selectedGame?.data || null}
+ onChange={(option) => setSelectedGame(option)}
+ strDefaultLabel="Select a game"
+ />
+ </PanelSectionRow>
+ {selectedGame && (
+ <PanelSectionRow>
+ <div>You selected: {selectedGame.label}</div>
+ </PanelSectionRow>
+ )}
+ </PanelSection>
+ );
+}
+
+function MainContent() {
+ return (
+ <>
+ <FGModInstallerSection />
+ <GameSelectorSection />
+ </>
+ );
+}
+
+// One default export, one plugin
export default definePlugin(() => ({
- name: "FG Mod Installer",
- titleView: <div>FG Mod Installer</div>,
- content: <Content />,
+ name: "Framegen Plugin",
+ titleView: <div>Framegen Plugin</div>,
+ content: <MainContent />,
icon: <FaShip />,
onDismount() {
- console.log("Plugin unmounted");
+ console.log("Framegen Plugin unmounted");
},
})); \ No newline at end of file