Compare commits

..

2 Commits

Author SHA1 Message Date
Moyasee
8bf70cd2b4 fix: multiple imports 2026-01-28 13:43:12 +02:00
Moyasee
2e2a95c314 feat: implement automatic shortcut creation upon automatic path binding 2026-01-28 13:38:49 +02:00
5 changed files with 95 additions and 88 deletions

View File

@@ -56,17 +56,12 @@ const addGameToQueue = async (
const updatedGame = await gamesSublevel.get(gameKey);
const promises: Promise<unknown>[] = [
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) {

View File

@@ -13,7 +13,7 @@ const updateDownloadQueuePosition = async (
const download = await downloadsSublevel.get(gameKey);
if (!download?.queued || download.status !== "paused") {
if (!download || !download.queued || download.status !== "paused") {
return false;
}

View File

@@ -1,63 +1,48 @@
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<Record<Downloader, string>> = {
[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
): DownloadErrorResult => {
): { ok: false; error?: string } => {
if (err instanceof AxiosError) {
const result = handleAxiosError(err, downloader);
if (result) return result;
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 };
}
}
if (err instanceof Error) {
const hostResult = handleHostSpecificError(err.message, downloader);
if (hostResult) return hostResult;
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" };
}
}
return { ok: false, error: err.message };
}

View File

@@ -2,12 +2,15 @@ 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";
import { logger } from "./logger";
import { GameExecutables } from "./game-executables";
import createDesktopShortcut from "create-desktop-shortcuts";
import { app } from "electron";
import { SystemPath } from "./system-path";
const PROGRESS_THROTTLE_MS = 1000;
@@ -204,6 +207,8 @@ export class GameFilesManager {
});
WindowManager.mainWindow?.webContents.send("on-library-batch-complete");
await this.createDesktopShortcutForGame(game.title, foundExePath);
}
} catch (err) {
logger.error(
@@ -213,6 +218,40 @@ export class GameFilesManager {
}
}
private async createDesktopShortcutForGame(
gameTitle: string,
executablePath: string
): Promise<void> {
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[]

View File

@@ -221,33 +221,6 @@ export function DownloadSettingsModal({
}
};
const getButtonContent = () => {
if (downloadStarting) {
return (
<>
<SyncIcon className="download-settings-modal__loading-spinner" />
{t("loading")}
</>
);
}
if (hasActiveDownload) {
return (
<>
<PlusIcon />
{t("add_to_queue")}
</>
);
}
return (
<>
<DownloadIcon />
{t("download_now")}
</>
);
};
const handleStartClick = async () => {
if (repack) {
setDownloadStarting(true);
@@ -489,7 +462,22 @@ export function DownloadSettingsModal({
)
}
>
{getButtonContent()}
{downloadStarting ? (
<>
<SyncIcon className="download-settings-modal__loading-spinner" />
{t("loading")}
</>
) : hasActiveDownload ? (
<>
<PlusIcon />
{t("add_to_queue")}
</>
) : (
<>
<DownloadIcon />
{t("download_now")}
</>
)}
</Button>
</div>