diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-01-22 21:45:23 -0500 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-01-22 21:45:23 -0500 |
| commit | 871cdbd04df02b32dbdd07467720b6eb9537178a (patch) | |
| tree | 34518d6cbc9def79f8c74089fc74ae351b499dc0 | |
| parent | d6dbb82378b1bd473430c3c2a8a356390e1b9b13 (diff) | |
| download | Decky-Framegen-871cdbd04df02b32dbdd07467720b6eb9537178a.tar.gz Decky-Framegen-871cdbd04df02b32dbdd07467720b6eb9537178a.zip | |
refactor, script status now displaying
| -rw-r--r-- | main.py | 116 | ||||
| -rwxr-xr-x | src/index.tsx | 86 |
2 files changed, 151 insertions, 51 deletions
@@ -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 |
