diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index c317a52b..02215a9f 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -404,6 +404,9 @@ "completed": "Completed", "removed": "Not downloaded", "cancel": "Cancel", + "cancel_download": "Cancel download", + "cancel_download_description": "Are you sure you want to cancel this download? This will delete all downloaded files.", + "keep_downloading": "Keep downloading", "filter": "Filter downloaded games", "remove": "Remove", "downloading_metadata": "Downloading metadata…", diff --git a/src/main/main.ts b/src/main/main.ts index 778b0c6c..fb7536a8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -34,7 +34,9 @@ export const loadState = async () => { await import("./events"); - if (!userPreferences?.useNativeHttpDownloader) { + // Only spawn aria2 if user explicitly disabled native HTTP downloader + // Default is to use native HTTP downloader (aria2 is opt-in) + if (userPreferences?.useNativeHttpDownloader === false) { Aria2.spawn(); } @@ -124,8 +126,9 @@ export const loadState = async () => { // For torrents or if JS downloader is disabled, use Python RPC const isTorrent = downloadToResume?.downloader === Downloader.Torrent; + // Default to true - native HTTP downloader is enabled by default const useJsDownloader = - userPreferences?.useNativeHttpDownloader && !isTorrent; + (userPreferences?.useNativeHttpDownloader ?? true) && !isTorrent; if (useJsDownloader && downloadToResume) { // Start Python RPC for seeding only, then resume HTTP download with JS diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index adb42e08..cfb2ba10 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -111,7 +111,8 @@ export class DownloadManager { levelKeys.userPreferences, { valueEncoding: "json" } ); - return userPreferences?.useNativeHttpDownloader ?? false; + // Default to true - native HTTP downloader is enabled by default (opt-out) + return userPreferences?.useNativeHttpDownloader ?? true; } private static isHttpDownloader(downloader: Downloader): boolean { @@ -483,6 +484,9 @@ export class DownloadManager { filename?: string; headers?: Record; } | null> { + // If resuming and we already have a folderName, use it to ensure we find the partial file + const resumingFilename = download.folderName || undefined; + switch (download.downloader) { case Downloader.Gofile: { const id = download.uri.split("/").pop(); @@ -490,6 +494,7 @@ export class DownloadManager { const downloadLink = await GofileApi.getDownloadLink(id!); await GofileApi.checkDownloadUrl(downloadLink); const filename = + resumingFilename || this.extractFilename(download.uri, downloadLink) || this.extractFilename(downloadLink); @@ -504,6 +509,7 @@ export class DownloadManager { const id = download.uri.split("/").pop(); const downloadUrl = await PixelDrainApi.getDownloadUrl(id!); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -516,6 +522,7 @@ export class DownloadManager { case Downloader.Qiwi: { const downloadUrl = await QiwiApi.getDownloadUrl(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -528,6 +535,7 @@ export class DownloadManager { case Downloader.Datanodes: { const downloadUrl = await DatanodesApi.getDownloadUrl(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -543,6 +551,7 @@ export class DownloadManager { ); const directUrl = await BuzzheavierApi.getDirectLink(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, directUrl) || this.extractFilename(directUrl); @@ -558,6 +567,7 @@ export class DownloadManager { ); const directUrl = await FuckingFastApi.getDirectLink(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, directUrl) || this.extractFilename(directUrl); @@ -570,6 +580,7 @@ export class DownloadManager { case Downloader.Mediafire: { const downloadUrl = await MediafireApi.getDownloadUrl(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -583,6 +594,7 @@ export class DownloadManager { const downloadUrl = await RealDebridClient.getDownloadUrl(download.uri); if (!downloadUrl) throw new Error(DownloadError.NotCachedOnRealDebrid); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -599,7 +611,7 @@ export class DownloadManager { return { url, savePath: download.downloadPath, - filename: name, + filename: resumingFilename || name, }; } case Downloader.Hydra: { @@ -608,6 +620,7 @@ export class DownloadManager { ); if (!downloadUrl) throw new Error(DownloadError.NotCachedOnHydra); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); @@ -623,6 +636,7 @@ export class DownloadManager { ); const downloadUrl = await VikingFileApi.getDownloadUrl(download.uri); const filename = + resumingFilename || this.extractFilename(download.uri, downloadUrl) || this.extractFilename(downloadUrl); diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 68b9b0b1..1bb8c76d 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -1,6 +1,6 @@ import type { GameShop, LibraryGame, SeedingStatus } from "@types"; -import { Badge, Button } from "@renderer/components"; +import { Badge, Button, ConfirmationModal } from "@renderer/components"; import { formatDownloadProgress, buildGameDetailsPath, @@ -219,7 +219,7 @@ interface HeroDownloadViewProps { calculateETA: () => string; pauseDownload: (shop: GameShop, objectId: string) => void; resumeDownload: (shop: GameShop, objectId: string) => void; - cancelDownload: (shop: GameShop, objectId: string) => void; + onCancelClick: (shop: GameShop, objectId: string) => void; t: (key: string) => string; } @@ -238,7 +238,7 @@ function HeroDownloadView({ calculateETA, pauseDownload, resumeDownload, - cancelDownload, + onCancelClick, t, }: Readonly) { const navigate = useNavigate(); @@ -353,7 +353,7 @@ function HeroDownloadView({ )} - -
+
    + {downloadInfo.map(({ game, size, progress, isSeeding: seeding }) => { + return ( +
  • -
    -
    - - {DOWNLOADER_NAME[Number(game.download!.downloader)]} - -
    -
    - {extraction?.visibleId === game.id ? ( - - {t("extracting")} ( - {Math.round(extraction.progress * 100)}%) - - ) : ( - - - {size} - - )} - {game.download?.progress === 1 && seeding && ( - - {t("seeding")} - - )} + +
    + +
    +
    + + {DOWNLOADER_NAME[Number(game.download!.downloader)]} + +
    +
    + {extraction?.visibleId === game.id ? ( + + {t("extracting")} ( + {Math.round(extraction.progress * 100)}%) + + ) : ( + + + {size} + + )} + {game.download?.progress === 1 && seeding && ( + + {t("seeding")} + + )} +
    -
    - {isQueuedGroup && ( -
    - - {formatDownloadProgress(progress)} - -
    -
    + {isQueuedGroup && ( +
    + + {formatDownloadProgress(progress)} + +
    +
    +
    -
    - )} + )} -
    - {game.download?.progress === 1 && ( - - )} - {isQueuedGroup && game.download?.progress !== 1 && ( - - )} - - - -
    -
  • - ); - })} -
-
+
+ {game.download?.progress === 1 && ( + + )} + {isQueuedGroup && game.download?.progress !== 1 && ( + + )} + + + +
+ + ); + })} + + + ); }