From db8da467e81fa2a60f6b3f131b75210e0cf892d5 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 26 Jan 2026 18:57:08 +0200 Subject: [PATCH 1/6] refactor: improve error handling in download processes and simplify button content rendering in download settings modal --- .../events/torrenting/add-game-to-queue.ts | 2 +- .../update-download-queue-position.ts | 2 +- src/main/helpers/download-error-handler.ts | 85 +++++++++++-------- .../modals/download-settings-modal.tsx | 44 ++++++---- 4 files changed, 80 insertions(+), 53 deletions(-) diff --git a/src/main/events/torrenting/add-game-to-queue.ts b/src/main/events/torrenting/add-game-to-queue.ts index f57cfaf6..ce86f40d 100644 --- a/src/main/events/torrenting/add-game-to-queue.ts +++ b/src/main/events/torrenting/add-game-to-queue.ts @@ -57,7 +57,7 @@ const addGameToQueue = async ( const updatedGame = await gamesSublevel.get(gameKey); await Promise.all([ - createGame(updatedGame!).catch(() => {}), + createGame(updatedGame).catch(() => {}), HydraApi.post(`/games/${shop}/${objectId}/download`, null, { needsAuth: false, }).catch(() => {}), diff --git a/src/main/events/torrenting/update-download-queue-position.ts b/src/main/events/torrenting/update-download-queue-position.ts index 5672d63d..6e480778 100644 --- a/src/main/events/torrenting/update-download-queue-position.ts +++ b/src/main/events/torrenting/update-download-queue-position.ts @@ -13,7 +13,7 @@ const updateDownloadQueuePosition = async ( const download = await downloadsSublevel.get(gameKey); - if (!download || !download.queued || download.status !== "paused") { + if (!download?.queued || download.status !== "paused") { return false; } diff --git a/src/main/helpers/download-error-handler.ts b/src/main/helpers/download-error-handler.ts index ad39d263..4b3864d6 100644 --- a/src/main/helpers/download-error-handler.ts +++ b/src/main/helpers/download-error-handler.ts @@ -1,48 +1,63 @@ import { AxiosError } from "axios"; import { Downloader, DownloadError } from "@shared"; +type DownloadErrorResult = { ok: false; error?: string }; + +const handleAxiosError = ( + err: AxiosError, + downloader: Downloader +): DownloadErrorResult | null => { + if (err.response?.status === 429 && downloader === Downloader.Gofile) { + return { ok: false, error: DownloadError.GofileQuotaExceeded }; + } + + if (err.response?.status === 403 && downloader === Downloader.RealDebrid) { + return { ok: false, error: DownloadError.RealDebridAccountNotAuthorized }; + } + + if (downloader === Downloader.TorBox) { + const data = err.response?.data as { detail?: string } | undefined; + return { ok: false, error: data?.detail }; + } + + return null; +}; + +const HOST_NAMES: Partial> = { + [Downloader.Buzzheavier]: "Buzzheavier", + [Downloader.FuckingFast]: "FuckingFast", +}; + +const handleHostSpecificError = ( + message: string, + downloader: Downloader +): DownloadErrorResult | null => { + const hostName = HOST_NAMES[downloader]; + if (!hostName) return null; + + if (message.includes("Rate limit")) { + return { ok: false, error: `${hostName}: Rate limit exceeded` }; + } + + if (message.includes("not found") || message.includes("deleted")) { + return { ok: false, error: `${hostName}: File not found` }; + } + + return null; +}; + export const handleDownloadError = ( err: unknown, downloader: Downloader -): { ok: false; error?: string } => { +): DownloadErrorResult => { if (err instanceof AxiosError) { - if (err.response?.status === 429 && downloader === Downloader.Gofile) { - return { ok: false, error: DownloadError.GofileQuotaExceeded }; - } - - if (err.response?.status === 403 && downloader === Downloader.RealDebrid) { - return { ok: false, error: DownloadError.RealDebridAccountNotAuthorized }; - } - - if (downloader === Downloader.TorBox) { - return { ok: false, error: err.response?.data?.detail }; - } + const result = handleAxiosError(err, downloader); + if (result) return result; } if (err instanceof Error) { - if (downloader === Downloader.Buzzheavier) { - if (err.message.includes("Rate limit")) { - return { ok: false, error: "Buzzheavier: Rate limit exceeded" }; - } - if ( - err.message.includes("not found") || - err.message.includes("deleted") - ) { - return { ok: false, error: "Buzzheavier: File not found" }; - } - } - - if (downloader === Downloader.FuckingFast) { - if (err.message.includes("Rate limit")) { - return { ok: false, error: "FuckingFast: Rate limit exceeded" }; - } - if ( - err.message.includes("not found") || - err.message.includes("deleted") - ) { - return { ok: false, error: "FuckingFast: File not found" }; - } - } + const hostResult = handleHostSpecificError(err.message, downloader); + if (hostResult) return hostResult; return { ok: false, error: err.message }; } diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index 6f20e439..69ea682d 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -221,6 +221,33 @@ export function DownloadSettingsModal({ } }; + const getButtonContent = () => { + if (downloadStarting) { + return ( + <> + + {t("loading")} + + ); + } + + if (hasActiveDownload) { + return ( + <> + + {t("add_to_queue")} + + ); + } + + return ( + <> + + {t("download_now")} + + ); + }; + const handleStartClick = async () => { if (repack) { setDownloadStarting(true); @@ -462,22 +489,7 @@ export function DownloadSettingsModal({ ) } > - {downloadStarting ? ( - <> - - {t("loading")} - - ) : hasActiveDownload ? ( - <> - - {t("add_to_queue")} - - ) : ( - <> - - {t("download_now")} - - )} + {getButtonContent()} From 289b59074ca46b51f8468cd171fd5b01daddec23 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 26 Jan 2026 18:58:29 +0200 Subject: [PATCH 2/6] refactor: enhance error handling in game queue addition by consolidating promises --- src/main/events/torrenting/add-game-to-queue.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/events/torrenting/add-game-to-queue.ts b/src/main/events/torrenting/add-game-to-queue.ts index ce86f40d..f8115533 100644 --- a/src/main/events/torrenting/add-game-to-queue.ts +++ b/src/main/events/torrenting/add-game-to-queue.ts @@ -56,12 +56,17 @@ const addGameToQueue = async ( const updatedGame = await gamesSublevel.get(gameKey); - await Promise.all([ - createGame(updatedGame).catch(() => {}), + const promises: Promise[] = [ HydraApi.post(`/games/${shop}/${objectId}/download`, null, { needsAuth: false, }).catch(() => {}), - ]); + ]; + + if (updatedGame) { + promises.push(createGame(updatedGame).catch(() => {})); + } + + await Promise.all(promises); return { ok: true }; } catch (err: unknown) { From 2e2a95c3146cb92f5d32506fc5e7b3bbb107ff7d Mon Sep 17 00:00:00 2001 From: Moyasee Date: Wed, 28 Jan 2026 13:38:49 +0200 Subject: [PATCH 3/6] feat: implement automatic shortcut creation upon automatic path binding --- src/main/services/game-files-manager.ts | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/main/services/game-files-manager.ts b/src/main/services/game-files-manager.ts index 722e1c9e..307903aa 100644 --- a/src/main/services/game-files-manager.ts +++ b/src/main/services/game-files-manager.ts @@ -8,6 +8,10 @@ import { WindowManager } from "./window-manager"; import { publishExtractionCompleteNotification } from "./notifications"; import { logger } from "./logger"; import { GameExecutables } from "./game-executables"; +import createDesktopShortcut from "create-desktop-shortcuts"; +import { app } from "electron"; +import { removeSymbolsFromName } from "@shared"; +import { SystemPath } from "./system-path"; const PROGRESS_THROTTLE_MS = 1000; @@ -204,6 +208,8 @@ export class GameFilesManager { }); WindowManager.mainWindow?.webContents.send("on-library-batch-complete"); + + await this.createDesktopShortcutForGame(game.title, foundExePath); } } catch (err) { logger.error( @@ -213,6 +219,40 @@ export class GameFilesManager { } } + private async createDesktopShortcutForGame( + gameTitle: string, + executablePath: string + ): Promise { + try { + const windowVbsPath = app.isPackaged + ? path.join(process.resourcesPath, "windows.vbs") + : undefined; + + const options = { + filePath: executablePath, + name: removeSymbolsFromName(gameTitle), + outputPath: SystemPath.getPath("desktop"), + }; + + const success = createDesktopShortcut({ + windows: { ...options, VBScriptPath: windowVbsPath }, + linux: options, + osx: options, + }); + + if (success) { + logger.info( + `[GameFilesManager] Created desktop shortcut for ${this.objectId}` + ); + } + } catch (err) { + logger.error( + `[GameFilesManager] Error creating desktop shortcut: ${this.objectId}`, + err + ); + } + } + private async findExecutableInFolder( folderPath: string, executableNames: string[] From 8bf70cd2b4d0d5b22b9c1aa5c612d4133af597e3 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Wed, 28 Jan 2026 13:43:12 +0200 Subject: [PATCH 4/6] fix: multiple imports --- src/main/services/game-files-manager.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/services/game-files-manager.ts b/src/main/services/game-files-manager.ts index 307903aa..6b700986 100644 --- a/src/main/services/game-files-manager.ts +++ b/src/main/services/game-files-manager.ts @@ -2,7 +2,7 @@ import path from "node:path"; import fs from "node:fs"; import type { GameShop } from "@types"; import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level"; -import { FILE_EXTENSIONS_TO_EXTRACT } from "@shared"; +import { FILE_EXTENSIONS_TO_EXTRACT, removeSymbolsFromName } from "@shared"; import { SevenZip, ExtractionProgress } from "./7zip"; import { WindowManager } from "./window-manager"; import { publishExtractionCompleteNotification } from "./notifications"; @@ -10,7 +10,6 @@ import { logger } from "./logger"; import { GameExecutables } from "./game-executables"; import createDesktopShortcut from "create-desktop-shortcuts"; import { app } from "electron"; -import { removeSymbolsFromName } from "@shared"; import { SystemPath } from "./system-path"; const PROGRESS_THROTTLE_MS = 1000; From 47e5999125bf50a16cb24ee3f7ebf3ec88670b67 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Thu, 29 Jan 2026 19:59:51 +0200 Subject: [PATCH 5/6] fix: add support for vikingfile alternative domain in downloader logic --- src/shared/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/index.ts b/src/shared/index.ts index 4ab56405..3508b819 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -142,7 +142,10 @@ export const getDownloadersForUri = (uri: string) => { if (uri.startsWith("https://fuckingfast.co")) { return [Downloader.FuckingFast]; } - if (uri.startsWith("https://vikingfile.com")) { + if ( + uri.startsWith("https://vikingfile.com") || + uri.startsWith("https://vik1ngfile.site") + ) { return [Downloader.VikingFile]; } if (uri.startsWith("https://www.rootz.so")) { From 52ff39a098edf7675d6d58b54e39e79731bb90e6 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Fri, 30 Jan 2026 12:37:40 +0200 Subject: [PATCH 6/6] refactor: streamline promise handling in addGameToQueue function for improved error management --- src/main/events/torrenting/add-game-to-queue.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/events/torrenting/add-game-to-queue.ts b/src/main/events/torrenting/add-game-to-queue.ts index f8115533..f57cfaf6 100644 --- a/src/main/events/torrenting/add-game-to-queue.ts +++ b/src/main/events/torrenting/add-game-to-queue.ts @@ -56,17 +56,12 @@ const addGameToQueue = async ( const updatedGame = await gamesSublevel.get(gameKey); - const promises: Promise[] = [ + await Promise.all([ + createGame(updatedGame!).catch(() => {}), HydraApi.post(`/games/${shop}/${objectId}/download`, null, { needsAuth: false, }).catch(() => {}), - ]; - - if (updatedGame) { - promises.push(createGame(updatedGame).catch(() => {})); - } - - await Promise.all(promises); + ]); return { ok: true }; } catch (err: unknown) {