This commit is contained in:
Wkeynhk
2025-12-26 01:13:36 +03:00
parent f8ac284bc2
commit 37f085e2c0
2 changed files with 47 additions and 110 deletions

View File

@@ -41,7 +41,6 @@ const startGameDownload = async (
const game = await gamesSublevel.get(gameKey); const game = await gamesSublevel.get(gameKey);
const gameAssets = await gamesShopAssetsSublevel.get(gameKey); const gameAssets = await gamesShopAssetsSublevel.get(gameKey);
/* Delete any previous download */
await downloadsSublevel.del(gameKey); await downloadsSublevel.del(gameKey);
if (game) { if (game) {
@@ -82,6 +81,7 @@ const startGameDownload = async (
queued: true, queued: true,
extracting: false, extracting: false,
automaticallyExtract, automaticallyExtract,
extractionProgress: null,
}; };
try { try {
@@ -166,4 +166,4 @@ const startGameDownload = async (
} }
}; };
registerEvent("startGameDownload", startGameDownload); registerEvent("startGameDownload", startGameDownload);

View File

@@ -30,29 +30,21 @@ export class DownloadManager {
private static downloadingGameId: string | null = null; private static downloadingGameId: string | null = null;
private static extractFilename(url: string, originalUrl?: string): string | undefined { private static extractFilename(url: string, originalUrl?: string): string | undefined {
if (originalUrl && originalUrl.includes('#')) { if (originalUrl?.includes('#')) {
const hashPart = originalUrl.split('#')[1]; const hashPart = originalUrl.split('#')[1];
if (hashPart && !hashPart.startsWith('http')) { if (hashPart && !hashPart.startsWith('http')) return hashPart;
return hashPart;
}
} }
if (url.includes('#')) { if (url.includes('#')) {
const hashPart = url.split('#')[1]; const hashPart = url.split('#')[1];
if (hashPart && !hashPart.startsWith('http')) { if (hashPart && !hashPart.startsWith('http')) return hashPart;
return hashPart;
}
} }
try { try {
const urlObj = new URL(url); const urlObj = new URL(url);
const pathname = urlObj.pathname; const filename = urlObj.pathname.split('/').pop();
const filename = pathname.split('/').pop(); if (filename?.length) return filename;
if (filename && filename.length > 0) { } catch {}
return filename;
}
} catch {
}
return undefined; return undefined;
} }
@@ -61,6 +53,24 @@ export class DownloadManager {
return filename.replace(/[<>:"/\\|?*]/g, '_'); return filename.replace(/[<>:"/\\|?*]/g, '_');
} }
private static createDownloadPayload(directUrl: string, originalUrl: string, downloadId: string, savePath: string) {
const filename = this.extractFilename(directUrl, originalUrl);
const sanitizedFilename = filename ? this.sanitizeFilename(filename) : undefined;
if (sanitizedFilename) {
logger.log(`[DownloadManager] Using filename: ${sanitizedFilename}`);
}
return {
action: "start" as const,
game_id: downloadId,
url: directUrl,
save_path: savePath,
out: sanitizedFilename,
allow_multiple_connections: true,
};
}
public static async startRPC( public static async startRPC(
download?: Download, download?: Download,
downloadsToSeed?: Download[] downloadsToSeed?: Download[]
@@ -86,9 +96,7 @@ export class DownloadManager {
} }
private static async getDownloadStatus() { private static async getDownloadStatus() {
const response = await PythonRPC.rpc.get<LibtorrentPayload | null>( const response = await PythonRPC.rpc.get<LibtorrentPayload | null>("/status");
"/status"
);
if (response.data === null || !this.downloadingGameId) return null; if (response.data === null || !this.downloadingGameId) return null;
const downloadId = this.downloadingGameId; const downloadId = this.downloadingGameId;
@@ -104,8 +112,7 @@ export class DownloadManager {
status, status,
} = response.data; } = response.data;
const isDownloadingMetadata = const isDownloadingMetadata = status === LibtorrentStatus.DownloadingMetadata;
status === LibtorrentStatus.DownloadingMetadata;
const isCheckingFiles = status === LibtorrentStatus.CheckingFiles; const isCheckingFiles = status === LibtorrentStatus.CheckingFiles;
const download = await downloadsSublevel.get(downloadId); const download = await downloadsSublevel.get(downloadId);
@@ -154,21 +161,14 @@ export class DownloadManager {
const userPreferences = await db.get<string, UserPreferences | null>( const userPreferences = await db.get<string, UserPreferences | null>(
levelKeys.userPreferences, levelKeys.userPreferences,
{ { valueEncoding: "json" }
valueEncoding: "json",
}
); );
if (WindowManager.mainWindow && download) { if (WindowManager.mainWindow && download) {
WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress); WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress);
WindowManager.mainWindow.webContents.send( WindowManager.mainWindow.webContents.send(
"on-download-progress", "on-download-progress",
JSON.parse( JSON.parse(JSON.stringify({ ...status, game }))
JSON.stringify({
...status,
game,
})
)
); );
} }
@@ -177,10 +177,7 @@ export class DownloadManager {
if (progress === 1 && download) { if (progress === 1 && download) {
publishDownloadCompleteNotification(game); publishDownloadCompleteNotification(game);
if ( if (userPreferences?.seedAfterDownloadComplete && download.downloader === Downloader.Torrent) {
userPreferences?.seedAfterDownloadComplete &&
download.downloader === Downloader.Torrent
) {
await downloadsSublevel.put(gameId, { await downloadsSublevel.put(gameId, {
...download, ...download,
status: "seeding", status: "seeding",
@@ -201,38 +198,25 @@ export class DownloadManager {
} }
if (shouldExtract) { if (shouldExtract) {
const gameFilesManager = new GameFilesManager( const gameFilesManager = new GameFilesManager(game.shop, game.objectId);
game.shop,
game.objectId
);
if ( if (FILE_EXTENSIONS_TO_EXTRACT.some((ext) => download.folderName?.endsWith(ext))) {
FILE_EXTENSIONS_TO_EXTRACT.some((ext) =>
download.folderName?.endsWith(ext)
)
) {
gameFilesManager.extractDownloadedFile(); gameFilesManager.extractDownloadedFile();
} else { } else {
gameFilesManager gameFilesManager
.extractFilesInDirectory( .extractFilesInDirectory(path.join(download.downloadPath, download.folderName!))
path.join(download.downloadPath, download.folderName!) .then(() => gameFilesManager.setExtractionComplete());
)
.then(() => {
gameFilesManager.setExtractionComplete();
});
} }
} }
const downloads = await downloadsSublevel const downloads = await downloadsSublevel
.values() .values()
.all() .all()
.then((games) => { .then((games) => sortBy(
return sortBy( games.filter((game) => game.status === "paused" && game.queued),
games.filter((game) => game.status === "paused" && game.queued), "timestamp",
"timestamp", "DESC"
"DESC" ));
);
});
const [nextItemOnQueue] = downloads; const [nextItemOnQueue] = downloads;
@@ -259,9 +243,7 @@ export class DownloadManager {
if (!download) return; if (!download) return;
const totalSize = await getDirSize( const totalSize = await getDirSize(path.join(download.downloadPath, status.folderName));
path.join(download.downloadPath, status.folderName)
);
if (totalSize < status.fileSize) { if (totalSize < status.fileSize) {
await this.cancelDownload(status.gameId); await this.cancelDownload(status.gameId);
@@ -282,10 +264,7 @@ export class DownloadManager {
static async pauseDownload(downloadKey = this.downloadingGameId) { static async pauseDownload(downloadKey = this.downloadingGameId) {
await PythonRPC.rpc await PythonRPC.rpc
.post("/action", { .post("/action", { action: "pause", game_id: downloadKey } as PauseDownloadPayload)
action: "pause",
game_id: downloadKey,
} as PauseDownloadPayload)
.catch(() => {}); .catch(() => {});
if (downloadKey === this.downloadingGameId) { if (downloadKey === this.downloadingGameId) {
@@ -300,13 +279,8 @@ export class DownloadManager {
static async cancelDownload(downloadKey = this.downloadingGameId) { static async cancelDownload(downloadKey = this.downloadingGameId) {
await PythonRPC.rpc await PythonRPC.rpc
.post("/action", { .post("/action", { action: "cancel", game_id: downloadKey })
action: "cancel", .catch((err) => logger.error("Failed to cancel game download", err));
game_id: downloadKey,
})
.catch((err) => {
logger.error("Failed to cancel game download", err);
});
if (downloadKey === this.downloadingGameId) { if (downloadKey === this.downloadingGameId) {
WindowManager.mainWindow?.setProgressBar(-1); WindowManager.mainWindow?.setProgressBar(-1);
@@ -339,7 +313,6 @@ export class DownloadManager {
const id = download.uri.split("/").pop(); const id = download.uri.split("/").pop();
const token = await GofileApi.authorize(); const token = await GofileApi.authorize();
const downloadLink = await GofileApi.getDownloadLink(id!); const downloadLink = await GofileApi.getDownloadLink(id!);
await GofileApi.checkDownloadUrl(downloadLink); await GofileApi.checkDownloadUrl(downloadLink);
return { return {
@@ -386,22 +359,7 @@ export class DownloadManager {
try { try {
const directUrl = await BuzzheavierApi.getDirectLink(download.uri); const directUrl = await BuzzheavierApi.getDirectLink(download.uri);
logger.log(`[DownloadManager] Buzzheavier direct URL obtained`); logger.log(`[DownloadManager] Buzzheavier direct URL obtained`);
return this.createDownloadPayload(directUrl, download.uri, downloadId, download.downloadPath);
const filename = this.extractFilename(directUrl, download.uri);
const sanitizedFilename = filename ? this.sanitizeFilename(filename) : undefined;
if (sanitizedFilename) {
logger.log(`[DownloadManager] Using filename: ${sanitizedFilename}`);
}
return {
action: "start",
game_id: downloadId,
url: directUrl,
save_path: download.downloadPath,
out: sanitizedFilename,
allow_multiple_connections: true,
};
} catch (error) { } catch (error) {
logger.error(`[DownloadManager] Error processing Buzzheavier download:`, error); logger.error(`[DownloadManager] Error processing Buzzheavier download:`, error);
throw error; throw error;
@@ -412,22 +370,7 @@ export class DownloadManager {
try { try {
const directUrl = await FuckingFastApi.getDirectLink(download.uri); const directUrl = await FuckingFastApi.getDirectLink(download.uri);
logger.log(`[DownloadManager] FuckingFast direct URL obtained`); logger.log(`[DownloadManager] FuckingFast direct URL obtained`);
return this.createDownloadPayload(directUrl, download.uri, downloadId, download.downloadPath);
const filename = this.extractFilename(directUrl, download.uri);
const sanitizedFilename = filename ? this.sanitizeFilename(filename) : undefined;
if (sanitizedFilename) {
logger.log(`[DownloadManager] Using filename: ${sanitizedFilename}`);
}
return {
action: "start",
game_id: downloadId,
url: directUrl,
save_path: download.downloadPath,
out: sanitizedFilename,
allow_multiple_connections: true,
};
} catch (error) { } catch (error) {
logger.error(`[DownloadManager] Error processing FuckingFast download:`, error); logger.error(`[DownloadManager] Error processing FuckingFast download:`, error);
throw error; throw error;
@@ -435,7 +378,6 @@ export class DownloadManager {
} }
case Downloader.Mediafire: { case Downloader.Mediafire: {
const downloadUrl = await MediafireApi.getDownloadUrl(download.uri); const downloadUrl = await MediafireApi.getDownloadUrl(download.uri);
return { return {
action: "start", action: "start",
game_id: downloadId, game_id: downloadId,
@@ -452,7 +394,6 @@ export class DownloadManager {
}; };
case Downloader.RealDebrid: { case Downloader.RealDebrid: {
const downloadUrl = await RealDebridClient.getDownloadUrl(download.uri); const downloadUrl = await RealDebridClient.getDownloadUrl(download.uri);
if (!downloadUrl) throw new Error(DownloadError.NotCachedOnRealDebrid); if (!downloadUrl) throw new Error(DownloadError.NotCachedOnRealDebrid);
return { return {
@@ -465,7 +406,6 @@ export class DownloadManager {
} }
case Downloader.TorBox: { case Downloader.TorBox: {
const { name, url } = await TorBoxClient.getDownloadInfo(download.uri); const { name, url } = await TorBoxClient.getDownloadInfo(download.uri);
if (!url) return; if (!url) return;
return { return {
action: "start", action: "start",
@@ -477,10 +417,7 @@ export class DownloadManager {
}; };
} }
case Downloader.Hydra: { case Downloader.Hydra: {
const downloadUrl = await HydraDebridClient.getDownloadUrl( const downloadUrl = await HydraDebridClient.getDownloadUrl(download.uri);
download.uri
);
if (!downloadUrl) throw new Error(DownloadError.NotCachedOnHydra); if (!downloadUrl) throw new Error(DownloadError.NotCachedOnHydra);
return { return {