mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
.
This commit is contained in:
@@ -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);
|
||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user