feat: reducing http downloads duplicate

This commit is contained in:
Chubby Granny Chaser 2024-08-17 21:43:56 +01:00
parent d098887c51
commit bbe68a0aff
No known key found for this signature in database
6 changed files with 64 additions and 163 deletions

View File

@ -20,8 +20,6 @@ autoUpdater.setFeedURL({
autoUpdater.logger = logger; autoUpdater.logger = logger;
logger.log("Init Hydra");
const gotTheLock = app.requestSingleInstanceLock(); const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) app.quit(); if (!gotTheLock) app.quit();
@ -123,7 +121,6 @@ app.on("window-all-closed", () => {
app.on("before-quit", () => { app.on("before-quit", () => {
/* Disconnects libtorrent */ /* Disconnects libtorrent */
PythonInstance.kill(); PythonInstance.kill();
logger.log("Quit Hydra");
}); });
app.on("activate", () => { app.on("activate", () => {

View File

@ -7,7 +7,7 @@ import { publishDownloadCompleteNotification } from "../notifications";
import { RealDebridDownloader } from "./real-debrid-downloader"; import { RealDebridDownloader } from "./real-debrid-downloader";
import type { DownloadProgress } from "@types"; import type { DownloadProgress } from "@types";
import { GofileApi } from "../hosters"; import { GofileApi } from "../hosters";
import { GenericHTTPDownloader } from "./generic-http-downloader"; import { GenericHttpDownloader } from "./generic-http-downloader";
export class DownloadManager { export class DownloadManager {
private static currentDownloader: Downloader | null = null; private static currentDownloader: Downloader | null = null;
@ -20,7 +20,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) { } else if (this.currentDownloader === Downloader.RealDebrid) {
status = await RealDebridDownloader.getStatus(); status = await RealDebridDownloader.getStatus();
} else { } else {
status = await GenericHTTPDownloader.getStatus(); status = await GenericHttpDownloader.getStatus();
} }
if (status) { if (status) {
@ -71,7 +71,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) { } else if (this.currentDownloader === Downloader.RealDebrid) {
await RealDebridDownloader.pauseDownload(); await RealDebridDownloader.pauseDownload();
} else { } else {
await GenericHTTPDownloader.pauseDownload(); await GenericHttpDownloader.pauseDownload();
} }
WindowManager.mainWindow?.setProgressBar(-1); WindowManager.mainWindow?.setProgressBar(-1);
@ -88,7 +88,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) { } else if (this.currentDownloader === Downloader.RealDebrid) {
RealDebridDownloader.cancelDownload(gameId); RealDebridDownloader.cancelDownload(gameId);
} else { } else {
GenericHTTPDownloader.cancelDownload(gameId); GenericHttpDownloader.cancelDownload(gameId);
} }
WindowManager.mainWindow?.setProgressBar(-1); WindowManager.mainWindow?.setProgressBar(-1);
@ -102,13 +102,13 @@ export class DownloadManager {
const token = await GofileApi.authorize(); const token = await GofileApi.authorize();
const downloadLink = await GofileApi.getDownloadLink(id!); const downloadLink = await GofileApi.getDownloadLink(id!);
GenericHTTPDownloader.startDownload(game, downloadLink, { GenericHttpDownloader.startDownload(game, downloadLink, {
Cookie: `accountToken=${token}`, Cookie: `accountToken=${token}`,
}); });
} else if (game.downloader === Downloader.PixelDrain) { } else if (game.downloader === Downloader.PixelDrain) {
const id = game!.uri!.split("/").pop(); const id = game!.uri!.split("/").pop();
await GenericHTTPDownloader.startDownload( await GenericHttpDownloader.startDownload(
game, game,
`https://pixeldrain.com/api/file/${id}?download` `https://pixeldrain.com/api/file/${id}?download`
); );

View File

@ -4,14 +4,14 @@ import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types"; import { DownloadProgress } from "@types";
import { HttpDownload } from "./http-download"; import { HttpDownload } from "./http-download";
export class GenericHTTPDownloader { export class GenericHttpDownloader {
private static downloads = new Map<number, string>(); public static downloads = new Map<number, HttpDownload>();
private static downloadingGame: Game | null = null; public static downloadingGame: Game | null = null;
public static async getStatus() { public static async getStatus() {
if (this.downloadingGame) { if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id)!; const download = this.downloads.get(this.downloadingGame.id)!;
const status = HttpDownload.getStatus(gid); const status = download.getStatus();
if (status) { if (status) {
const progress = const progress =
@ -57,10 +57,10 @@ export class GenericHTTPDownloader {
static async pauseDownload() { static async pauseDownload() {
if (this.downloadingGame) { if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame!.id!); const httpDownload = this.downloads.get(this.downloadingGame!.id!);
if (gid) { if (httpDownload) {
await HttpDownload.pauseDownload(gid); await httpDownload.pauseDownload();
} }
this.downloadingGame = null; this.downloadingGame = null;
@ -79,29 +79,31 @@ export class GenericHTTPDownloader {
return; return;
} }
const gid = await HttpDownload.startDownload( const httpDownload = new HttpDownload(
game.downloadPath!, game.downloadPath!,
downloadUrl, downloadUrl,
headers headers
); );
this.downloads.set(game.id!, gid); httpDownload.startDownload();
this.downloads.set(game.id!, httpDownload);
} }
static async cancelDownload(gameId: number) { static async cancelDownload(gameId: number) {
const gid = this.downloads.get(gameId); const httpDownload = this.downloads.get(gameId);
if (gid) { if (httpDownload) {
await HttpDownload.cancelDownload(gid); await httpDownload.cancelDownload();
this.downloads.delete(gameId); this.downloads.delete(gameId);
} }
} }
static async resumeDownload(gameId: number) { static async resumeDownload(gameId: number) {
const gid = this.downloads.get(gameId); const httpDownload = this.downloads.get(gameId);
if (gid) { if (httpDownload) {
await HttpDownload.resumeDownload(gid); await httpDownload.resumeDownload();
} }
} }
} }

View File

@ -1,67 +1,54 @@
import { DownloadItem } from "electron";
import { WindowManager } from "../window-manager"; import { WindowManager } from "../window-manager";
import path from "node:path"; import path from "node:path";
export class HttpDownload { export class HttpDownload {
private static id = 0; private downloadItem: Electron.DownloadItem;
private static downloads: Record<string, DownloadItem> = {}; constructor(
private downloadPath: string,
private downloadUrl: string,
private headers?: Record<string, string>
) {}
public static getStatus(gid: string): { public getStatus() {
completedLength: number; return {
totalLength: number; completedLength: this.downloadItem.getReceivedBytes(),
downloadSpeed: number; totalLength: this.downloadItem.getTotalBytes(),
folderName: string; downloadSpeed: this.downloadItem.getCurrentBytesPerSecond(),
} | null { folderName: this.downloadItem.getFilename(),
const downloadItem = this.downloads[gid]; };
if (downloadItem) {
return {
completedLength: downloadItem.getReceivedBytes(),
totalLength: downloadItem.getTotalBytes(),
downloadSpeed: downloadItem.getCurrentBytesPerSecond(),
folderName: downloadItem.getFilename(),
};
}
return null;
} }
static async cancelDownload(gid: string) { async cancelDownload() {
const downloadItem = this.downloads[gid]; this.downloadItem.cancel();
downloadItem?.cancel();
delete this.downloads[gid];
} }
static async pauseDownload(gid: string) { async pauseDownload() {
const downloadItem = this.downloads[gid]; this.downloadItem.pause();
downloadItem?.pause();
} }
static async resumeDownload(gid: string) { async resumeDownload() {
const downloadItem = this.downloads[gid]; this.downloadItem.resume();
downloadItem?.resume();
} }
static async startDownload( async startDownload() {
downloadPath: string, return new Promise((resolve) => {
downloadUrl: string, const options = this.headers ? { headers: this.headers } : {};
headers?: Record<string, string> WindowManager.mainWindow?.webContents.downloadURL(
) { this.downloadUrl,
return new Promise<string>((resolve) => { options
const options = headers ? { headers } : {}; );
WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options);
const gid = ++this.id; WindowManager.mainWindow?.webContents.session.once(
WindowManager.mainWindow?.webContents.session.on(
"will-download", "will-download",
(_event, item, _webContents) => { (_event, item, _webContents) => {
this.downloads[gid.toString()] = item; console.log(_event);
// Set the save path, making Electron not to prompt a save dialog. this.downloadItem = item;
item.setSavePath(path.join(downloadPath, item.getFilename()));
resolve(gid.toString()); item.setSavePath(path.join(this.downloadPath, item.getFilename()));
resolve(null);
} }
); );
}); });

View File

@ -1,14 +1,9 @@
import { Game } from "@main/entity"; import { Game } from "@main/entity";
import { RealDebridClient } from "../real-debrid"; import { RealDebridClient } from "../real-debrid";
import { gameRepository } from "@main/repository";
import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
import { HttpDownload } from "./http-download"; import { HttpDownload } from "./http-download";
import { GenericHttpDownloader } from "./generic-http-downloader";
export class RealDebridDownloader { export class RealDebridDownloader extends GenericHttpDownloader {
private static downloads = new Map<number, string>();
private static downloadingGame: Game | null = null;
private static realDebridTorrentId: string | null = null; private static realDebridTorrentId: string | null = null;
private static async getRealDebridDownloadUrl() { private static async getRealDebridDownloadUrl() {
@ -48,66 +43,6 @@ export class RealDebridDownloader {
return null; return null;
} }
public static async getStatus() {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id)!;
const status = HttpDownload.getStatus(gid);
if (status) {
const progress =
Number(status.completedLength) / Number(status.totalLength);
await gameRepository.update(
{ id: this.downloadingGame!.id },
{
bytesDownloaded: Number(status.completedLength),
fileSize: Number(status.totalLength),
progress,
status: "active",
folderName: status.folderName,
}
);
const result = {
numPeers: 0,
numSeeds: 0,
downloadSpeed: Number(status.downloadSpeed),
timeRemaining: calculateETA(
Number(status.totalLength),
Number(status.completedLength),
Number(status.downloadSpeed)
),
isDownloadingMetadata: false,
isCheckingFiles: false,
progress,
gameId: this.downloadingGame!.id,
} as DownloadProgress;
if (progress === 1) {
this.downloads.delete(this.downloadingGame.id);
this.realDebridTorrentId = null;
this.downloadingGame = null;
}
return result;
}
}
return null;
}
static async pauseDownload() {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id);
if (gid) {
await HttpDownload.pauseDownload(gid);
}
}
this.realDebridTorrentId = null;
this.downloadingGame = null;
}
static async startDownload(game: Game) { static async startDownload(game: Game) {
if (this.downloads.has(game.id)) { if (this.downloads.has(game.id)) {
await this.resumeDownload(game.id!); await this.resumeDownload(game.id!);
@ -128,32 +63,10 @@ export class RealDebridDownloader {
if (downloadUrl) { if (downloadUrl) {
this.realDebridTorrentId = null; this.realDebridTorrentId = null;
const gid = await HttpDownload.startDownload( const httpDownload = new HttpDownload(game.downloadPath!, downloadUrl);
game.downloadPath!, httpDownload.startDownload();
downloadUrl
);
this.downloads.set(game.id!, gid); this.downloads.set(game.id!, httpDownload);
}
}
static async cancelDownload(gameId: number) {
const gid = this.downloads.get(gameId);
if (gid) {
await HttpDownload.cancelDownload(gid);
this.downloads.delete(gameId);
}
this.realDebridTorrentId = null;
this.downloadingGame = null;
}
static async resumeDownload(gameId: number) {
const gid = this.downloads.get(gameId);
if (gid) {
await HttpDownload.resumeDownload(gid);
} }
} }
} }

View File

@ -16,6 +16,8 @@ export interface GofileContentsResponse {
children: Record<string, GofileContentChild>; children: Record<string, GofileContentChild>;
} }
export const WT = "4fd6sg89d7s6";
export class GofileApi { export class GofileApi {
private static token: string; private static token: string;
@ -35,7 +37,7 @@ export class GofileApi {
public static async getDownloadLink(id: string) { public static async getDownloadLink(id: string) {
const searchParams = new URLSearchParams({ const searchParams = new URLSearchParams({
wt: "4fd6sg89d7s6", wt: WT,
}); });
const response = await axios.get<{ const response = await axios.get<{