From 34aea2b0c421286b285a58b35e9df86be28bdac6 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 12 Oct 2025 18:51:47 +0100 Subject: [PATCH] feat: adding api call for decky plugin --- src/locales/en/translation.json | 1 + src/locales/pt-BR/translation.json | 1 + .../misc/get-hydra-decky-plugin-info.ts | 36 ++- .../events/misc/install-hydra-decky-plugin.ts | 2 +- src/main/services/decky-plugin.ts | 209 +++++++++++++----- .../src/components/sidebar/sidebar.tsx | 17 +- src/renderer/src/declaration.d.ts | 2 + src/shared/html-sanitizer.ts | 2 - 8 files changed, 199 insertions(+), 71 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 3dc93d90..0ca77d87 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -78,6 +78,7 @@ "edit_game_modal_drop_to_replace_logo": "Drop to replace logo", "edit_game_modal_drop_to_replace_hero": "Drop to replace hero", "install_decky_plugin": "Install Decky Plugin", + "update_decky_plugin": "Update Decky Plugin", "decky_plugin_installed_version": "Decky Plugin (v{{version}})", "install_decky_plugin_title": "Install Hydra Decky Plugin", "install_decky_plugin_message": "This will download and install the Hydra plugin for Decky Loader. This may require elevated permissions. Continue?", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index e9a84c89..b9cee539 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -78,6 +78,7 @@ "edit_game_modal_drop_to_replace_logo": "Solte para substituir o logo", "edit_game_modal_drop_to_replace_hero": "Solte para substituir o hero", "install_decky_plugin": "Instalar Plugin Decky", + "update_decky_plugin": "Atualizar Plugin Decky", "decky_plugin_installed_version": "Plugin Decky (v{{version}})", "install_decky_plugin_title": "Instalar Plugin Hydra Decky", "install_decky_plugin_message": "Isso irá baixar e instalar o plugin Hydra para Decky Loader. Pode ser necessário permissões elevadas. Continuar?", diff --git a/src/main/events/misc/get-hydra-decky-plugin-info.ts b/src/main/events/misc/get-hydra-decky-plugin-info.ts index 6ff7b050..430bd691 100644 --- a/src/main/events/misc/get-hydra-decky-plugin-info.ts +++ b/src/main/events/misc/get-hydra-decky-plugin-info.ts @@ -1,17 +1,37 @@ import { registerEvent } from "../register-event"; -import { logger } from "@main/services"; +import { logger, HydraApi } from "@main/services"; import { HYDRA_DECKY_PLUGIN_LOCATION } from "@main/constants"; import fs from "node:fs"; import path from "node:path"; +interface DeckyReleaseInfo { + version: string; + downloadUrl: string; +} + const getHydraDeckyPluginInfo = async ( _event: Electron.IpcMainInvokeEvent ): Promise<{ installed: boolean; version: string | null; path: string; + outdated: boolean; + expectedVersion: string | null; }> => { try { + // Fetch the expected version from API + let expectedVersion: string | null = null; + try { + const releaseInfo = await HydraApi.get( + "/decky/release", + {}, + { needsAuth: false } + ); + expectedVersion = releaseInfo.version; + } catch (error) { + logger.error("Failed to fetch Decky release info:", error); + } + // Check if plugin folder exists if (!fs.existsSync(HYDRA_DECKY_PLUGIN_LOCATION)) { logger.log("Hydra Decky plugin not installed"); @@ -19,6 +39,8 @@ const getHydraDeckyPluginInfo = async ( installed: false, version: null, path: HYDRA_DECKY_PLUGIN_LOCATION, + outdated: true, + expectedVersion, }; } @@ -34,6 +56,8 @@ const getHydraDeckyPluginInfo = async ( installed: false, version: null, path: HYDRA_DECKY_PLUGIN_LOCATION, + outdated: true, + expectedVersion, }; } @@ -42,12 +66,18 @@ const getHydraDeckyPluginInfo = async ( const packageJson = JSON.parse(packageJsonContent); const version = packageJson.version; - logger.log(`Hydra Decky plugin installed, version: ${version}`); + const outdated = expectedVersion ? version !== expectedVersion : false; + + logger.log( + `Hydra Decky plugin installed, version: ${version}, expected: ${expectedVersion}, outdated: ${outdated}` + ); return { installed: true, version, path: HYDRA_DECKY_PLUGIN_LOCATION, + outdated, + expectedVersion, }; } catch (error) { logger.error("Failed to get plugin info:", error); @@ -55,6 +85,8 @@ const getHydraDeckyPluginInfo = async ( installed: false, version: null, path: HYDRA_DECKY_PLUGIN_LOCATION, + outdated: true, + expectedVersion: null, }; } }; diff --git a/src/main/events/misc/install-hydra-decky-plugin.ts b/src/main/events/misc/install-hydra-decky-plugin.ts index 3ddbbd64..e14ea2ed 100644 --- a/src/main/events/misc/install-hydra-decky-plugin.ts +++ b/src/main/events/misc/install-hydra-decky-plugin.ts @@ -41,7 +41,7 @@ const installHydraDeckyPlugin = async ( success: false, path: HYDRA_DECKY_PLUGIN_LOCATION, currentVersion: null, - expectedVersion: "0.0.3", + expectedVersion: "unknown", error: errorMessage, }; } diff --git a/src/main/services/decky-plugin.ts b/src/main/services/decky-plugin.ts index 7e178189..4dc1fdad 100644 --- a/src/main/services/decky-plugin.ts +++ b/src/main/services/decky-plugin.ts @@ -11,11 +11,35 @@ import { import { logger } from "./logger"; import { SevenZip } from "./7zip"; import { SystemPath } from "./system-path"; +import { HydraApi } from "./hydra-api"; + +interface DeckyReleaseInfo { + version: string; + downloadUrl: string; +} export class DeckyPlugin { - private static readonly EXPECTED_VERSION = "0.0.3"; - private static readonly DOWNLOAD_URL = - "https://github.com/hydralauncher/decky-hydra-launcher/releases/download/0.0.3/Hydra.zip"; + private static releaseInfo: DeckyReleaseInfo | null = null; + + private static async getDeckyReleaseInfo(): Promise { + if (this.releaseInfo) { + return this.releaseInfo; + } + + try { + const response = await HydraApi.get( + "/decky/release", + {}, + { needsAuth: false } + ); + + this.releaseInfo = response; + return response; + } catch (error) { + logger.error("Failed to fetch Decky release info:", error); + throw error; + } + } private static getPackageJsonPath(): string { return path.join(HYDRA_DECKY_PLUGIN_LOCATION, "package.json"); @@ -24,10 +48,11 @@ export class DeckyPlugin { private static async downloadPlugin(): Promise { logger.log("Downloading Hydra Decky plugin..."); + const releaseInfo = await this.getDeckyReleaseInfo(); const tempDir = SystemPath.getPath("temp"); const zipPath = path.join(tempDir, "Hydra.zip"); - const response = await axios.get(this.DOWNLOAD_URL, { + const response = await axios.get(releaseInfo.downloadUrl, { responseType: "arraybuffer", }); @@ -209,14 +234,15 @@ export class DeckyPlugin { return; } + const releaseInfo = await this.getDeckyReleaseInfo(); const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8"); const packageJson = JSON.parse(packageJsonContent); const currentVersion = packageJson.version; - const isOutdated = currentVersion !== this.EXPECTED_VERSION; + const isOutdated = currentVersion !== releaseInfo.version; if (isOutdated) { logger.log( - `Hydra Decky plugin is outdated. Current: ${currentVersion}, Expected: ${this.EXPECTED_VERSION}. Updating...` + `Hydra Decky plugin is outdated. Current: ${currentVersion}, Expected: ${releaseInfo.version}. Updating...` ); await this.updatePlugin(); @@ -235,78 +261,139 @@ export class DeckyPlugin { currentVersion: string | null; expectedVersion: string; }> { - if (!fs.existsSync(HYDRA_DECKY_PLUGIN_LOCATION)) { - logger.log("Hydra Decky plugin folder not found, installing..."); + try { + const releaseInfo = await this.getDeckyReleaseInfo(); + + if (!fs.existsSync(HYDRA_DECKY_PLUGIN_LOCATION)) { + logger.log("Hydra Decky plugin folder not found, installing..."); + + try { + await this.updatePlugin(); + + // Read the actual installed version from package.json + const packageJsonPath = this.getPackageJsonPath(); + if (fs.existsSync(packageJsonPath)) { + const packageJsonContent = fs.readFileSync( + packageJsonPath, + "utf-8" + ); + const packageJson = JSON.parse(packageJsonContent); + return { + exists: true, + outdated: false, + currentVersion: packageJson.version, + expectedVersion: releaseInfo.version, + }; + } + + return { + exists: true, + outdated: false, + currentVersion: releaseInfo.version, + expectedVersion: releaseInfo.version, + }; + } catch (error) { + logger.error("Failed to install plugin:", error); + return { + exists: false, + outdated: true, + currentVersion: null, + expectedVersion: releaseInfo.version, + }; + } + } + + const packageJsonPath = this.getPackageJsonPath(); try { - await this.updatePlugin(); + if (!fs.existsSync(packageJsonPath)) { + logger.log( + "Hydra Decky plugin package.json not found, installing..." + ); + + await this.updatePlugin(); + + // Read the actual installed version from package.json + if (fs.existsSync(packageJsonPath)) { + const packageJsonContent = fs.readFileSync( + packageJsonPath, + "utf-8" + ); + const packageJson = JSON.parse(packageJsonContent); + return { + exists: true, + outdated: false, + currentVersion: packageJson.version, + expectedVersion: releaseInfo.version, + }; + } + + return { + exists: true, + outdated: false, + currentVersion: releaseInfo.version, + expectedVersion: releaseInfo.version, + }; + } + + const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8"); + const packageJson = JSON.parse(packageJsonContent); + const currentVersion = packageJson.version; + const isOutdated = currentVersion !== releaseInfo.version; + + if (isOutdated) { + logger.log( + `Hydra Decky plugin is outdated. Current: ${currentVersion}, Expected: ${releaseInfo.version}` + ); + + await this.updatePlugin(); + + if (fs.existsSync(packageJsonPath)) { + const updatedPackageJsonContent = fs.readFileSync( + packageJsonPath, + "utf-8" + ); + const updatedPackageJson = JSON.parse(updatedPackageJsonContent); + return { + exists: true, + outdated: false, + currentVersion: updatedPackageJson.version, + expectedVersion: releaseInfo.version, + }; + } + + return { + exists: true, + outdated: false, + currentVersion: releaseInfo.version, + expectedVersion: releaseInfo.version, + }; + } else { + logger.log(`Hydra Decky plugin is up to date (${currentVersion})`); + } + return { exists: true, - outdated: false, - currentVersion: this.EXPECTED_VERSION, - expectedVersion: this.EXPECTED_VERSION, + outdated: isOutdated, + currentVersion, + expectedVersion: releaseInfo.version, }; } catch (error) { - logger.error("Failed to install plugin:", error); + logger.error(`Error checking Hydra Decky plugin version: ${error}`); return { exists: false, outdated: true, currentVersion: null, - expectedVersion: this.EXPECTED_VERSION, + expectedVersion: releaseInfo.version, }; } - } - - const packageJsonPath = this.getPackageJsonPath(); - - try { - if (!fs.existsSync(packageJsonPath)) { - logger.log("Hydra Decky plugin package.json not found, installing..."); - - await this.updatePlugin(); - return { - exists: true, - outdated: false, - currentVersion: this.EXPECTED_VERSION, - expectedVersion: this.EXPECTED_VERSION, - }; - } - - const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8"); - const packageJson = JSON.parse(packageJsonContent); - const currentVersion = packageJson.version; - const isOutdated = currentVersion !== this.EXPECTED_VERSION; - - if (isOutdated) { - logger.log( - `Hydra Decky plugin is outdated. Current: ${currentVersion}, Expected: ${this.EXPECTED_VERSION}` - ); - - await this.updatePlugin(); - - return { - exists: true, - outdated: false, - currentVersion: this.EXPECTED_VERSION, - expectedVersion: this.EXPECTED_VERSION, - }; - } else { - logger.log(`Hydra Decky plugin is up to date (${currentVersion})`); - } - - return { - exists: true, - outdated: isOutdated, - currentVersion, - expectedVersion: this.EXPECTED_VERSION, - }; } catch (error) { - logger.error(`Error checking Hydra Decky plugin version: ${error}`); + logger.error(`Error fetching release info: ${error}`); return { exists: false, outdated: true, currentVersion: null, - expectedVersion: this.EXPECTED_VERSION, + expectedVersion: "unknown", }; } } diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index 13647556..5dd6ae94 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -51,7 +51,8 @@ export function Sidebar() { const [deckyPluginInfo, setDeckyPluginInfo] = useState<{ installed: boolean; version: string | null; - }>({ installed: false, version: null }); + outdated: boolean; + }>({ installed: false, version: null, outdated: false }); const [homebrewFolderExists, setHomebrewFolderExists] = useState(false); const [showDeckyConfirmModal, setShowDeckyConfirmModal] = useState(false); const navigate = useNavigate(); @@ -102,6 +103,7 @@ export function Sidebar() { setDeckyPluginInfo({ installed: info.installed, version: info.version, + outdated: info.outdated, }); setHomebrewFolderExists(folderExists); } catch (error) { @@ -110,6 +112,9 @@ export function Sidebar() { }; const handleInstallHydraDeckyPlugin = () => { + if (deckyPluginInfo.installed && !deckyPluginInfo.outdated) { + return; + } setShowDeckyConfirmModal(true); }; @@ -318,11 +323,13 @@ export function Sidebar() { style={{ width: 16, height: 16 }} /> - {deckyPluginInfo.installed + {deckyPluginInfo.installed && !deckyPluginInfo.outdated ? t("decky_plugin_installed_version", { version: deckyPluginInfo.version, }) - : t("install_decky_plugin")} + : deckyPluginInfo.installed && deckyPluginInfo.outdated + ? t("update_decky_plugin") + : t("install_decky_plugin")} @@ -433,12 +440,12 @@ export function Sidebar() { ; checkHomebrewFolderExists: () => Promise; onCommonRedistProgress: ( diff --git a/src/shared/html-sanitizer.ts b/src/shared/html-sanitizer.ts index ea3d475b..c78d7dd8 100644 --- a/src/shared/html-sanitizer.ts +++ b/src/shared/html-sanitizer.ts @@ -6,8 +6,6 @@ function removeZalgoText(text: string): string { return text.replaceAll(zalgoRegex, ""); } - - export function sanitizeHtml(html: string): string { if (!html || typeof html !== "string") { return "";