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

View File

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

View File

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

View File

@ -1,67 +1,54 @@
import { DownloadItem } from "electron";
import { WindowManager } from "../window-manager";
import path from "node:path";
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): {
completedLength: number;
totalLength: number;
downloadSpeed: number;
folderName: string;
} | null {
const downloadItem = this.downloads[gid];
if (downloadItem) {
return {
completedLength: downloadItem.getReceivedBytes(),
totalLength: downloadItem.getTotalBytes(),
downloadSpeed: downloadItem.getCurrentBytesPerSecond(),
folderName: downloadItem.getFilename(),
};
}
return null;
public getStatus() {
return {
completedLength: this.downloadItem.getReceivedBytes(),
totalLength: this.downloadItem.getTotalBytes(),
downloadSpeed: this.downloadItem.getCurrentBytesPerSecond(),
folderName: this.downloadItem.getFilename(),
};
}
static async cancelDownload(gid: string) {
const downloadItem = this.downloads[gid];
downloadItem?.cancel();
delete this.downloads[gid];
async cancelDownload() {
this.downloadItem.cancel();
}
static async pauseDownload(gid: string) {
const downloadItem = this.downloads[gid];
downloadItem?.pause();
async pauseDownload() {
this.downloadItem.pause();
}
static async resumeDownload(gid: string) {
const downloadItem = this.downloads[gid];
downloadItem?.resume();
async resumeDownload() {
this.downloadItem.resume();
}
static async startDownload(
downloadPath: string,
downloadUrl: string,
headers?: Record<string, string>
) {
return new Promise<string>((resolve) => {
const options = headers ? { headers } : {};
WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options);
async startDownload() {
return new Promise((resolve) => {
const options = this.headers ? { headers: this.headers } : {};
WindowManager.mainWindow?.webContents.downloadURL(
this.downloadUrl,
options
);
const gid = ++this.id;
WindowManager.mainWindow?.webContents.session.on(
WindowManager.mainWindow?.webContents.session.once(
"will-download",
(_event, item, _webContents) => {
this.downloads[gid.toString()] = item;
console.log(_event);
// Set the save path, making Electron not to prompt a save dialog.
item.setSavePath(path.join(downloadPath, item.getFilename()));
this.downloadItem = item;
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 { RealDebridClient } from "../real-debrid";
import { gameRepository } from "@main/repository";
import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
import { HttpDownload } from "./http-download";
import { GenericHttpDownloader } from "./generic-http-downloader";
export class RealDebridDownloader {
private static downloads = new Map<number, string>();
private static downloadingGame: Game | null = null;
export class RealDebridDownloader extends GenericHttpDownloader {
private static realDebridTorrentId: string | null = null;
private static async getRealDebridDownloadUrl() {
@ -48,66 +43,6 @@ export class RealDebridDownloader {
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) {
if (this.downloads.has(game.id)) {
await this.resumeDownload(game.id!);
@ -128,32 +63,10 @@ export class RealDebridDownloader {
if (downloadUrl) {
this.realDebridTorrentId = null;
const gid = await HttpDownload.startDownload(
game.downloadPath!,
downloadUrl
);
const httpDownload = new HttpDownload(game.downloadPath!, downloadUrl);
httpDownload.startDownload();
this.downloads.set(game.id!, gid);
}
}
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);
this.downloads.set(game.id!, httpDownload);
}
}
}

View File

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