From 6c6fff71fe145cf68e0817d7c2b8ec2364a611e4 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 9 Jul 2024 19:24:02 +0100 Subject: [PATCH 01/12] feat: adding generic http downloads --- .../events/torrenting/start-game-download.ts | 4 + src/main/main.ts | 3 +- .../services/download/download-manager.ts | 56 ++++++--- .../download/generic-http-downloader.ts | 109 ++++++++++++++++++ src/main/services/download/http-download.ts | 10 +- .../download/real-debrid-downloader.ts | 30 +++-- src/main/services/hosters/gofile.ts | 61 ++++++++++ src/main/services/hosters/index.ts | 1 + src/renderer/src/constants.ts | 2 + .../modals/download-settings-modal.tsx | 36 ++++-- .../game-details/modals/repacks-modal.tsx | 6 +- src/shared/index.ts | 17 +++ 12 files changed, 294 insertions(+), 41 deletions(-) create mode 100644 src/main/services/download/generic-http-downloader.ts create mode 100644 src/main/services/hosters/gofile.ts create mode 100644 src/main/services/hosters/index.ts diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index cea41596f..aa33c99ab 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -44,6 +44,8 @@ const startGameDownload = async ( ); if (game) { + console.log("game", game); + await gameRepository.update( { id: game.id, @@ -95,6 +97,8 @@ const startGameDownload = async ( }, }); + console.log(updatedGame); + createGame(updatedGame!); await downloadQueueRepository.delete({ game: { id: updatedGame!.id } }); diff --git a/src/main/main.ts b/src/main/main.ts index fbabc56cd..af594e206 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -22,8 +22,9 @@ const loadState = async (userPreferences: UserPreferences | null) => { import("./events"); - if (userPreferences?.realDebridApiToken) + if (userPreferences?.realDebridApiToken) { RealDebridClient.authorize(userPreferences?.realDebridApiToken); + } HydraApi.setupApi().then(() => { uploadGamesBatch(); diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 31f28992a..d6542396f 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -6,6 +6,8 @@ import { downloadQueueRepository, gameRepository } from "@main/repository"; 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"; export class DownloadManager { private static currentDownloader: Downloader | null = null; @@ -13,10 +15,12 @@ export class DownloadManager { public static async watchDownloads() { let status: DownloadProgress | null = null; - if (this.currentDownloader === Downloader.RealDebrid) { + if (this.currentDownloader === Downloader.Torrent) { + status = await PythonInstance.getStatus(); + } else if (this.currentDownloader === Downloader.RealDebrid) { status = await RealDebridDownloader.getStatus(); } else { - status = await PythonInstance.getStatus(); + status = await GenericHTTPDownloader.getStatus(); } if (status) { @@ -62,10 +66,12 @@ export class DownloadManager { } static async pauseDownload() { - if (this.currentDownloader === Downloader.RealDebrid) { + if (this.currentDownloader === Downloader.Torrent) { + await PythonInstance.pauseDownload(); + } else if (this.currentDownloader === Downloader.RealDebrid) { await RealDebridDownloader.pauseDownload(); } else { - await PythonInstance.pauseDownload(); + await GenericHTTPDownloader.pauseDownload(); } WindowManager.mainWindow?.setProgressBar(-1); @@ -73,20 +79,16 @@ export class DownloadManager { } static async resumeDownload(game: Game) { - if (game.downloader === Downloader.RealDebrid) { - RealDebridDownloader.startDownload(game); - this.currentDownloader = Downloader.RealDebrid; - } else { - PythonInstance.startDownload(game); - this.currentDownloader = Downloader.Torrent; - } + return this.startDownload(game); } static async cancelDownload(gameId: number) { - if (this.currentDownloader === Downloader.RealDebrid) { + if (this.currentDownloader === Downloader.Torrent) { + PythonInstance.cancelDownload(gameId); + } else if (this.currentDownloader === Downloader.RealDebrid) { RealDebridDownloader.cancelDownload(gameId); } else { - PythonInstance.cancelDownload(gameId); + GenericHTTPDownloader.cancelDownload(gameId); } WindowManager.mainWindow?.setProgressBar(-1); @@ -94,12 +96,30 @@ export class DownloadManager { } static async startDownload(game: Game) { - if (game.downloader === Downloader.RealDebrid) { - RealDebridDownloader.startDownload(game); - this.currentDownloader = Downloader.RealDebrid; - } else { + if (game.downloader === Downloader.Gofile) { + const id = game!.uri!.split("/").pop(); + + const token = await GofileApi.authorize(); + const downloadLink = await GofileApi.getDownloadLink(id!); + + console.log(downloadLink, token, "<<<"); + + GenericHTTPDownloader.startDownload(game, downloadLink, [ + `Cookie: accountToken=${token}`, + ]); + } else if (game.downloader === Downloader.PixelDrain) { + const id = game!.uri!.split("/").pop(); + + await GenericHTTPDownloader.startDownload( + game, + `https://pixeldrain.com/api/file/${id}?download` + ); + } else if (game.downloader === Downloader.Torrent) { PythonInstance.startDownload(game); - this.currentDownloader = Downloader.Torrent; + } else if (game.downloader === Downloader.RealDebrid) { + RealDebridDownloader.startDownload(game); } + + this.currentDownloader = game.downloader; } } diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts new file mode 100644 index 000000000..688769a4e --- /dev/null +++ b/src/main/services/download/generic-http-downloader.ts @@ -0,0 +1,109 @@ +import { Game } from "@main/entity"; +import { gameRepository } from "@main/repository"; +import { calculateETA } from "./helpers"; +import { DownloadProgress } from "@types"; +import { HTTPDownload } from "./http-download"; + +export class GenericHTTPDownloader { + private static downloads = new Map(); + private static downloadingGame: Game | null = null; + + public static async getStatus() { + if (this.downloadingGame) { + const gid = this.downloads.get(this.downloadingGame.id)!; + const status = await 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", + } + ); + + 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.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.downloadingGame = null; + } + } + + static async startDownload( + game: Game, + downloadUrl: string, + headers: string[] = [] + ) { + this.downloadingGame = game; + + if (this.downloads.has(game.id)) { + await this.resumeDownload(game.id!); + + return; + } + + if (downloadUrl) { + const gid = await HTTPDownload.startDownload( + game.downloadPath!, + downloadUrl, + headers + ); + + 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); + } + } + + static async resumeDownload(gameId: number) { + const gid = this.downloads.get(gameId); + + if (gid) { + await HTTPDownload.resumeDownload(gid); + } + } +} diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index 4553a6cbf..d147e208c 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -4,7 +4,7 @@ import { sleep } from "@main/helpers"; import { startAria2 } from "../aria2c"; import Aria2 from "aria2"; -export class HttpDownload { +export class HTTPDownload { private static connected = false; private static aria2c: ChildProcess | null = null; @@ -56,11 +56,17 @@ export class HttpDownload { await this.aria2.call("unpause", gid); } - static async startDownload(downloadPath: string, downloadUrl: string) { + static async startDownload( + downloadPath: string, + downloadUrl: string, + header: string[] = [] + ) { + console.log(header); if (!this.connected) await this.connect(); const options = { dir: downloadPath, + header, }; return this.aria2.call("addUri", [downloadUrl], options); diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index 8ead0067a..034ffc492 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -3,7 +3,7 @@ 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"; export class RealDebridDownloader { private static downloads = new Map(); @@ -29,6 +29,18 @@ export class RealDebridDownloader { const { download } = await RealDebridClient.unrestrictLink(link); return decodeURIComponent(download); } + + return null; + } + + if (this.downloadingGame?.uri) { + const { download } = await RealDebridClient.unrestrictLink( + this.downloadingGame?.uri + ); + + console.log("download>>", download); + + return decodeURIComponent(download); } return null; @@ -37,7 +49,7 @@ export class RealDebridDownloader { public static async getStatus() { if (this.downloadingGame) { const gid = this.downloads.get(this.downloadingGame.id)!; - const status = await HttpDownload.getStatus(gid); + const status = await HTTPDownload.getStatus(gid); if (status) { const progress = @@ -111,7 +123,7 @@ export class RealDebridDownloader { static async pauseDownload() { const gid = this.downloads.get(this.downloadingGame!.id!); if (gid) { - await HttpDownload.pauseDownload(gid); + await HTTPDownload.pauseDownload(gid); } this.realDebridTorrentId = null; @@ -127,14 +139,18 @@ export class RealDebridDownloader { return; } - this.realDebridTorrentId = await RealDebridClient.getTorrentId(game!.uri!); + if (game.uri?.startsWith("magnet:")) { + this.realDebridTorrentId = await RealDebridClient.getTorrentId( + game!.uri! + ); + } const downloadUrl = await this.getRealDebridDownloadUrl(); if (downloadUrl) { this.realDebridTorrentId = null; - const gid = await HttpDownload.startDownload( + const gid = await HTTPDownload.startDownload( game.downloadPath!, downloadUrl ); @@ -147,7 +163,7 @@ export class RealDebridDownloader { const gid = this.downloads.get(gameId); if (gid) { - await HttpDownload.cancelDownload(gid); + await HTTPDownload.cancelDownload(gid); this.downloads.delete(gameId); } } @@ -156,7 +172,7 @@ export class RealDebridDownloader { const gid = this.downloads.get(gameId); if (gid) { - await HttpDownload.resumeDownload(gid); + await HTTPDownload.resumeDownload(gid); } } } diff --git a/src/main/services/hosters/gofile.ts b/src/main/services/hosters/gofile.ts new file mode 100644 index 000000000..770bb15f9 --- /dev/null +++ b/src/main/services/hosters/gofile.ts @@ -0,0 +1,61 @@ +import axios from "axios"; + +export interface GofileAccountsReponse { + id: string; + token: string; +} + +export interface GofileContentChild { + id: string; + link: string; +} + +export interface GofileContentsResponse { + id: string; + type: string; + children: Record; +} + +export class GofileApi { + private static token: string; + + public static async authorize() { + const response = await axios.post<{ + status: string; + data: GofileAccountsReponse; + }>("https://api.gofile.io/accounts"); + + if (response.data.status === "ok") { + this.token = response.data.data.token; + return this.token; + } + + throw new Error("Failed to authorize"); + } + + public static async getDownloadLink(id: string) { + const searchParams = new URLSearchParams({ + wt: "4fd6sg89d7s6", + }); + + const response = await axios.get<{ + status: string; + data: GofileContentsResponse; + }>(`https://api.gofile.io/contents/${id}?${searchParams.toString()}`, { + headers: { + Authorization: `Bearer ${this.token}`, + }, + }); + + if (response.data.status === "ok") { + if (response.data.data.type !== "folder") { + throw new Error("Only folders are supported"); + } + + const [firstChild] = Object.values(response.data.data.children); + return firstChild.link; + } + + throw new Error("Failed to get download link"); + } +} diff --git a/src/main/services/hosters/index.ts b/src/main/services/hosters/index.ts new file mode 100644 index 000000000..921c45b11 --- /dev/null +++ b/src/main/services/hosters/index.ts @@ -0,0 +1 @@ +export * from "./gofile"; diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts index 6186bb854..7025df2a0 100644 --- a/src/renderer/src/constants.ts +++ b/src/renderer/src/constants.ts @@ -5,4 +5,6 @@ export const VERSION_CODENAME = "Leviticus"; export const DOWNLOADER_NAME = { [Downloader.RealDebrid]: "Real-Debrid", [Downloader.Torrent]: "Torrent", + [Downloader.Gofile]: "Gofile", + [Downloader.PixelDrain]: "PixelDrain", }; diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index ef4ba0403..d102d2b2d 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -1,11 +1,11 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { DiskSpace } from "check-disk-space"; import * as styles from "./download-settings-modal.css"; import { Button, Link, Modal, TextField } from "@renderer/components"; import { CheckCircleFillIcon, DownloadIcon } from "@primer/octicons-react"; -import { Downloader, formatBytes } from "@shared"; +import { Downloader, formatBytes, getDownloadersForUri } from "@shared"; import type { GameRepack } from "@types"; import { SPACING_UNIT } from "@renderer/theme.css"; @@ -23,8 +23,6 @@ export interface DownloadSettingsModalProps { repack: GameRepack | null; } -const downloaders = [Downloader.Torrent, Downloader.RealDebrid]; - export function DownloadSettingsModal({ visible, onClose, @@ -36,9 +34,8 @@ export function DownloadSettingsModal({ const [diskFreeSpace, setDiskFreeSpace] = useState(null); const [selectedPath, setSelectedPath] = useState(""); const [downloadStarting, setDownloadStarting] = useState(false); - const [selectedDownloader, setSelectedDownloader] = useState( - Downloader.Torrent - ); + const [selectedDownloader, setSelectedDownloader] = + useState(null); const userPreferences = useAppSelector( (state) => state.userPreferences.value @@ -50,6 +47,10 @@ export function DownloadSettingsModal({ } }, [visible, selectedPath]); + const downloaders = useMemo(() => { + return getDownloadersForUri(repack?.magnet ?? ""); + }, [repack?.magnet]); + useEffect(() => { if (userPreferences?.downloadsPath) { setSelectedPath(userPreferences.downloadsPath); @@ -59,9 +60,19 @@ export function DownloadSettingsModal({ .then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath)); } - if (userPreferences?.realDebridApiToken) + if ( + userPreferences?.realDebridApiToken && + downloaders.includes(Downloader.RealDebrid) + ) { setSelectedDownloader(Downloader.RealDebrid); - }, [userPreferences?.downloadsPath, userPreferences?.realDebridApiToken]); + } else { + setSelectedDownloader(downloaders[0]); + } + }, [ + userPreferences?.downloadsPath, + downloaders, + userPreferences?.realDebridApiToken, + ]); const getDiskFreeSpace = (path: string) => { window.electron.getDiskFreeSpace(path).then((result) => { @@ -85,7 +96,7 @@ export function DownloadSettingsModal({ if (repack) { setDownloadStarting(true); - startDownload(repack, selectedDownloader, selectedPath).finally(() => { + startDownload(repack, selectedDownloader!, selectedPath).finally(() => { setDownloadStarting(false); onClose(); }); @@ -167,7 +178,10 @@ export function DownloadSettingsModal({

- diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx index d2d8f5d91..f9d351afd 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -44,8 +44,10 @@ export function RepacksModal({ }, [repacks]); const getInfoHash = useCallback(async () => { - const torrent = await parseTorrent(game?.uri ?? ""); - if (torrent.infoHash) setInfoHash(torrent.infoHash); + if (game?.uri?.startsWith("magnet:")) { + const torrent = await parseTorrent(game?.uri ?? ""); + if (torrent.infoHash) setInfoHash(torrent.infoHash); + } }, [game]); useEffect(() => { diff --git a/src/shared/index.ts b/src/shared/index.ts index 409bdbc98..ba68876f2 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -1,6 +1,8 @@ export enum Downloader { RealDebrid, Torrent, + Gofile, + PixelDrain, } export enum DownloadSourceStatus { @@ -63,3 +65,18 @@ export const formatName = pipe( removeDuplicateSpaces, (str) => str.trim() ); + +const realDebridHosts = ["https://1fichier.com", "https://mediafire.com"]; + +export const getDownloadersForUri = (uri: string) => { + if (uri.startsWith("https://gofile.io")) return [Downloader.Gofile]; + if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain]; + + if (realDebridHosts.some((host) => uri.startsWith(host))) + return [Downloader.RealDebrid]; + + if (uri.startsWith("magnet:")) + return [Downloader.Torrent, Downloader.RealDebrid]; + + return []; +}; From b5b7fe31ae64a28e483ccbe7b1f0cc3470711b50 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:48:10 -0300 Subject: [PATCH 02/12] use webContents.downloadURL to download http --- src/main/services/download/http-download.ts | 107 +++++++++++--------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index d147e208c..ec16f5cd4 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -1,59 +1,42 @@ -import type { ChildProcess } from "node:child_process"; -import { logger } from "../logger"; -import { sleep } from "@main/helpers"; -import { startAria2 } from "../aria2c"; -import Aria2 from "aria2"; +import { DownloadItem } from "electron"; +import { WindowManager } from "../window-manager"; export class HTTPDownload { - private static connected = false; - private static aria2c: ChildProcess | null = null; + private static id = 0; - private static aria2 = new Aria2({}); + private static downloads: Record = {}; - private static async connect() { - this.aria2c = startAria2(); - - let retries = 0; - - while (retries < 4 && !this.connected) { - try { - await this.aria2.open(); - logger.log("Connected to aria2"); - - this.connected = true; - } catch (err) { - await sleep(100); - logger.log("Failed to connect to aria2, retrying..."); - retries++; - } - } - } - - public static getStatus(gid: string) { - if (this.connected) { - return this.aria2.call("tellStatus", gid); + public static getStatus(gid: string): { + completedLength: number; + totalLength: number; + downloadSpeed: number; + } | null { + const downloadItem = this.downloads[gid]; + if (downloadItem) { + return { + completedLength: downloadItem.getReceivedBytes(), + totalLength: downloadItem.getTotalBytes(), + downloadSpeed: 0, + }; } return null; } - public static disconnect() { - if (this.aria2c) { - this.aria2c.kill(); - this.connected = false; - } - } - static async cancelDownload(gid: string) { - await this.aria2.call("forceRemove", gid); + const downloadItem: DownloadItem = this.downloads[gid]; + downloadItem?.cancel(); + this.downloads; } static async pauseDownload(gid: string) { - await this.aria2.call("forcePause", gid); + const downloadItem = this.downloads[gid]; + downloadItem?.pause(); } static async resumeDownload(gid: string) { - await this.aria2.call("unpause", gid); + const downloadItem = this.downloads[gid]; + downloadItem?.resume(); } static async startDownload( @@ -61,14 +44,44 @@ export class HTTPDownload { downloadUrl: string, header: string[] = [] ) { - console.log(header); - if (!this.connected) await this.connect(); + return new Promise((resolve) => { + WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, { + headers: { Cookie: header[0].split(": ")[1] }, + }); + + WindowManager.mainWindow?.webContents.session.on( + "will-download", + (_event, item, _webContents) => { + const gid = ++this.id; + + this.downloads[gid.toString()] = item; + + // Set the save path, making Electron not to prompt a save dialog. + item.setSavePath(downloadPath); + + item.on("updated", (_event, state) => { + if (state === "interrupted") { + console.log("Download is interrupted but can be resumed"); + } else if (state === "progressing") { + if (item.isPaused()) { + console.log("Download is paused"); + } else { + console.log(`Received bytes: ${item.getReceivedBytes()}`); + } + } + }); - const options = { - dir: downloadPath, - header, - }; + item.once("done", (_event, state) => { + if (state === "completed") { + console.log("Download successfully"); + } else { + console.log(`Download failed: ${state}`); + } + }); - return this.aria2.call("addUri", [downloadUrl], options); + resolve(gid.toString()); + } + ); + }); } } From 781e0f4102a26e04a3fcbadbd1fe3f1058f9c849 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 15 Jul 2024 22:29:34 -0300 Subject: [PATCH 03/12] feat: update headers --- .../services/download/download-manager.ts | 6 ++--- .../download/generic-http-downloader.ts | 2 +- src/main/services/download/http-download.ts | 26 +++---------------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index d6542396f..b9c9d5a61 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -104,9 +104,9 @@ export class DownloadManager { console.log(downloadLink, token, "<<<"); - GenericHTTPDownloader.startDownload(game, downloadLink, [ - `Cookie: accountToken=${token}`, - ]); + GenericHTTPDownloader.startDownload(game, downloadLink, { + Cookie: `accountToken=${token}`, + }); } else if (game.downloader === Downloader.PixelDrain) { const id = game!.uri!.split("/").pop(); diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index 688769a4e..a482c0759 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -69,7 +69,7 @@ export class GenericHTTPDownloader { static async startDownload( game: Game, downloadUrl: string, - headers: string[] = [] + headers?: Record ) { this.downloadingGame = game; diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index ec16f5cd4..ec9ba6350 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -26,7 +26,7 @@ export class HTTPDownload { static async cancelDownload(gid: string) { const downloadItem: DownloadItem = this.downloads[gid]; downloadItem?.cancel(); - this.downloads; + delete this.downloads[gid]; } static async pauseDownload(gid: string) { @@ -42,11 +42,11 @@ export class HTTPDownload { static async startDownload( downloadPath: string, downloadUrl: string, - header: string[] = [] + headers?: Record ) { return new Promise((resolve) => { WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, { - headers: { Cookie: header[0].split(": ")[1] }, + headers, }); WindowManager.mainWindow?.webContents.session.on( @@ -59,26 +59,6 @@ export class HTTPDownload { // Set the save path, making Electron not to prompt a save dialog. item.setSavePath(downloadPath); - item.on("updated", (_event, state) => { - if (state === "interrupted") { - console.log("Download is interrupted but can be resumed"); - } else if (state === "progressing") { - if (item.isPaused()) { - console.log("Download is paused"); - } else { - console.log(`Received bytes: ${item.getReceivedBytes()}`); - } - } - }); - - item.once("done", (_event, state) => { - if (state === "completed") { - console.log("Download successfully"); - } else { - console.log(`Download failed: ${state}`); - } - }); - resolve(gid.toString()); } ); From 8a1931f75ce068ff1bec78b968b27699d6149bad Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:52:27 -0300 Subject: [PATCH 04/12] feat: use new electron version to get download speed --- package.json | 2 +- src/main/services/download/generic-http-downloader.ts | 8 ++++---- src/main/services/download/http-download.ts | 2 +- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index c99a9bacd..d7996e6a8 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@types/user-agents": "^1.0.4", "@vanilla-extract/vite-plugin": "^4.0.7", "@vitejs/plugin-react": "^4.2.1", - "electron": "^30.0.9", + "electron": "^30.3.0", "electron-builder": "^24.9.1", "electron-vite": "^2.0.0", "eslint": "^8.56.0", diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index a482c0759..c078be801 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -30,11 +30,11 @@ export class GenericHTTPDownloader { const result = { numPeers: 0, numSeeds: 0, - downloadSpeed: Number(status.downloadSpeed), + downloadSpeed: status.downloadSpeed, timeRemaining: calculateETA( - Number(status.totalLength), - Number(status.completedLength), - Number(status.downloadSpeed) + status.totalLength, + status.completedLength, + status.downloadSpeed ), isDownloadingMetadata: false, isCheckingFiles: false, diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index ec9ba6350..d9c36916f 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -16,7 +16,7 @@ export class HTTPDownload { return { completedLength: downloadItem.getReceivedBytes(), totalLength: downloadItem.getTotalBytes(), - downloadSpeed: 0, + downloadSpeed: downloadItem.getCurrentBytesPerSecond(), }; } diff --git a/yarn.lock b/yarn.lock index 00172038e..9c829f625 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3739,10 +3739,10 @@ electron-vite@^2.0.0: magic-string "^0.30.5" picocolors "^1.0.0" -electron@^30.0.9: - version "30.0.9" - resolved "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz" - integrity sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow== +electron@^30.1.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.0.tgz#9d7af06c11242d5c6ca5b9920c9dc49feab90299" + integrity sha512-/rWPcpCL4sYCUm1bY8if1dO8nyFTwXlPUP0dpL3ir5iLK/9NshN6lIJ8xceEY8CEYVLMIYRkxXb44Q9cdrjtOQ== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" From e1ef8a91934df6a0e0e2908740fc7595ab25baa9 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:21:05 -0300 Subject: [PATCH 05/12] feat: pass headers correctly to downloadURL --- src/main/events/auth/sign-out.ts | 2 ++ src/main/services/download/http-download.ts | 9 ++++----- src/main/services/hydra-api.ts | 8 ++++++++ yarn.lock | 8 ++++---- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index fe640b9df..9998c7333 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -26,6 +26,8 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => { /* Disconnects libtorrent */ PythonInstance.killTorrent(); + HydraApi.handleSignOut(); + await Promise.all([ databaseOperations, HydraApi.post("/auth/logout").catch(() => {}), diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index d9c36916f..d74e862ea 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -45,15 +45,14 @@ export class HTTPDownload { headers?: Record ) { return new Promise((resolve) => { - WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, { - headers, - }); + const options = headers ? { headers } : {}; + WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options); + + const gid = ++this.id; WindowManager.mainWindow?.webContents.session.on( "will-download", (_event, item, _webContents) => { - const gid = ++this.id; - this.downloads[gid.toString()] = item; // Set the save path, making Electron not to prompt a save dialog. diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 5365bd9e1..120d27aca 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -64,6 +64,14 @@ export class HydraApi { } } + static handleSignOut() { + this.userAuth = { + authToken: "", + refreshToken: "", + expirationTimestamp: 0, + }; + } + static async setupApi() { this.instance = axios.create({ baseURL: import.meta.env.MAIN_VITE_API_URL, diff --git a/yarn.lock b/yarn.lock index c2d495749..2dd42cb83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3751,10 +3751,10 @@ electron-vite@^2.0.0: magic-string "^0.30.5" picocolors "^1.0.0" -electron@^30.1.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.0.tgz#9d7af06c11242d5c6ca5b9920c9dc49feab90299" - integrity sha512-/rWPcpCL4sYCUm1bY8if1dO8nyFTwXlPUP0dpL3ir5iLK/9NshN6lIJ8xceEY8CEYVLMIYRkxXb44Q9cdrjtOQ== +electron@^30.3.0: + version "30.3.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.1.tgz#fe27ca2a4739bec832b2edd6f46140ab46bf53a0" + integrity sha512-Ai/OZ7VlbFAVYMn9J5lyvtr+ZWyEbXDVd5wBLb5EVrp4352SRmMAmN5chcIe3n9mjzcgehV9n4Hwy15CJW+YbA== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" From fab248de99c593d2ffbf85c2a7c567061ef6cb5b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 18:55:35 -0300 Subject: [PATCH 06/12] fix: duplicate download with real debrid --- src/main/services/download/real-debrid-downloader.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index 034ffc492..4877e6e28 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -131,11 +131,9 @@ export class RealDebridDownloader { } static async startDownload(game: Game) { - this.downloadingGame = game; - if (this.downloads.has(game.id)) { await this.resumeDownload(game.id!); - + this.downloadingGame = game; return; } @@ -156,6 +154,7 @@ export class RealDebridDownloader { ); this.downloads.set(game.id!, gid); + this.downloadingGame = game; } } From 19b1d29713e57ec3d2229b4f7f11da24c5a44d69 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:15:36 -0300 Subject: [PATCH 07/12] fix: reset downloadingGame and torrentId on cancelDownload --- src/main/services/download/http-download.ts | 2 +- src/main/services/download/real-debrid-downloader.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index d74e862ea..df1151d5f 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -24,7 +24,7 @@ export class HTTPDownload { } static async cancelDownload(gid: string) { - const downloadItem: DownloadItem = this.downloads[gid]; + const downloadItem = this.downloads[gid]; downloadItem?.cancel(); delete this.downloads[gid]; } diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index 4877e6e28..ee5ee881b 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -165,6 +165,9 @@ export class RealDebridDownloader { await HTTPDownload.cancelDownload(gid); this.downloads.delete(gameId); } + + this.realDebridTorrentId = null; + this.downloadingGame = null; } static async resumeDownload(gameId: number) { From 4d60317475285a49dea1435761eacc017601f456 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:39:55 -0300 Subject: [PATCH 08/12] fix: passing complete download path to setSavePath --- src/main/services/download/http-download.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index df1151d5f..81f6b5fd8 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -1,5 +1,6 @@ import { DownloadItem } from "electron"; import { WindowManager } from "../window-manager"; +import path from "node:path"; export class HTTPDownload { private static id = 0; @@ -56,7 +57,7 @@ export class HTTPDownload { this.downloads[gid.toString()] = item; // Set the save path, making Electron not to prompt a save dialog. - item.setSavePath(downloadPath); + item.setSavePath(path.join(downloadPath, item.getFilename())); resolve(gid.toString()); } From 4b7a0ff4023cc53dc7e80e6cc70d3cd3b6a3308f Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:48:43 -0300 Subject: [PATCH 09/12] feat: setting folder name --- src/main/services/download/generic-http-downloader.ts | 1 + src/main/services/download/http-download.ts | 2 ++ src/main/services/download/real-debrid-downloader.ts | 1 + 3 files changed, 4 insertions(+) diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index c078be801..4977d503d 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -24,6 +24,7 @@ export class GenericHTTPDownloader { fileSize: Number(status.totalLength), progress, status: "active", + folderName: status.folderName, } ); diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index 81f6b5fd8..fb573ff45 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -11,6 +11,7 @@ export class HTTPDownload { completedLength: number; totalLength: number; downloadSpeed: number; + folderName: string; } | null { const downloadItem = this.downloads[gid]; if (downloadItem) { @@ -18,6 +19,7 @@ export class HTTPDownload { completedLength: downloadItem.getReceivedBytes(), totalLength: downloadItem.getTotalBytes(), downloadSpeed: downloadItem.getCurrentBytesPerSecond(), + folderName: downloadItem.getFilename(), }; } diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index ee5ee881b..fc32263c5 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -62,6 +62,7 @@ export class RealDebridDownloader { fileSize: Number(status.totalLength), progress, status: "active", + folderName: status.folderName, } ); From f3f78248efc3a67e19b3dcd098fa94b1246fde00 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:49:10 -0300 Subject: [PATCH 10/12] fix: start another download after finishing one --- src/main/services/download/real-debrid-downloader.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index fc32263c5..f61cb1ffa 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -122,9 +122,11 @@ export class RealDebridDownloader { } static async pauseDownload() { - const gid = this.downloads.get(this.downloadingGame!.id!); - if (gid) { - await HTTPDownload.pauseDownload(gid); + if (this.downloadingGame) { + const gid = this.downloads.get(this.downloadingGame.id); + if (gid) { + await HTTPDownload.pauseDownload(gid); + } } this.realDebridTorrentId = null; From 4a149aa62d733f85f11343c2cf4da175c38e332a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 20:47:27 -0300 Subject: [PATCH 11/12] feat: remove aria2 (again) --- .gitignore | 1 - electron-builder.yml | 1 - package.json | 3 +- postinstall.cjs | 50 ----------------------- src/main/declaration.d.ts | 80 ------------------------------------- src/main/services/aria2c.ts | 20 ---------- yarn.lock | 15 +------ 7 files changed, 2 insertions(+), 168 deletions(-) delete mode 100644 postinstall.cjs delete mode 100644 src/main/declaration.d.ts delete mode 100644 src/main/services/aria2c.ts diff --git a/.gitignore b/.gitignore index 7a6496a5a..fb4badd7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .vscode node_modules hydra-download-manager/ -aria2/ fastlist.exe __pycache__ dist diff --git a/electron-builder.yml b/electron-builder.yml index be300d365..cfdafe7d8 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -3,7 +3,6 @@ productName: Hydra directories: buildResources: build extraResources: - - aria2 - hydra-download-manager - seeds - from: node_modules/create-desktop-shortcuts/src/windows.vbs diff --git a/package.json b/package.json index 4693f65bc..54d689b25 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "start": "electron-vite preview", "dev": "electron-vite dev", "build": "npm run typecheck && electron-vite build", - "postinstall": "electron-builder install-app-deps && node ./postinstall.cjs", + "postinstall": "electron-builder install-app-deps", "build:unpack": "npm run build && electron-builder --dir", "build:win": "electron-vite build && electron-builder --win", "build:mac": "electron-vite build && electron-builder --mac", @@ -42,7 +42,6 @@ "@vanilla-extract/css": "^1.14.2", "@vanilla-extract/dynamic": "^2.1.1", "@vanilla-extract/recipes": "^0.5.2", - "aria2": "^4.1.2", "auto-launch": "^5.0.6", "axios": "^1.6.8", "better-sqlite3": "^9.5.0", diff --git a/postinstall.cjs b/postinstall.cjs deleted file mode 100644 index 547af9885..000000000 --- a/postinstall.cjs +++ /dev/null @@ -1,50 +0,0 @@ -const { default: axios } = require("axios"); -const util = require("node:util"); -const fs = require("node:fs"); - -const exec = util.promisify(require("node:child_process").exec); - -const downloadAria2 = async () => { - if (fs.existsSync("aria2")) { - console.log("Aria2 already exists, skipping download..."); - return; - } - - const file = - process.platform === "win32" - ? "aria2-1.37.0-win-64bit-build1.zip" - : "aria2-1.37.0-1-x86_64.pkg.tar.zst"; - - const downloadUrl = - process.platform === "win32" - ? `https://github.com/aria2/aria2/releases/download/release-1.37.0/${file}` - : "https://archlinux.org/packages/extra/x86_64/aria2/download/"; - - console.log(`Downloading ${file}...`); - - const response = await axios.get(downloadUrl, { responseType: "stream" }); - - const stream = response.data.pipe(fs.createWriteStream(file)); - - stream.on("finish", async () => { - console.log(`Downloaded ${file}, extracting...`); - - if (process.platform === "win32") { - await exec(`npx extract-zip ${file}`); - console.log("Extracted. Renaming folder..."); - - fs.renameSync(file.replace(".zip", ""), "aria2"); - } else { - await exec(`tar --zstd -xvf ${file} usr/bin/aria2c`); - console.log("Extracted. Copying binary file..."); - fs.mkdirSync("aria2"); - fs.copyFileSync("usr/bin/aria2c", "aria2/aria2c"); - fs.rmSync("usr", { recursive: true }); - } - - console.log(`Extracted ${file}, removing compressed downloaded file...`); - fs.rmSync(file); - }); -}; - -downloadAria2(); diff --git a/src/main/declaration.d.ts b/src/main/declaration.d.ts deleted file mode 100644 index ac2675a35..000000000 --- a/src/main/declaration.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -declare module "aria2" { - export type Aria2Status = - | "active" - | "waiting" - | "paused" - | "error" - | "complete" - | "removed"; - - export interface StatusResponse { - gid: string; - status: Aria2Status; - totalLength: string; - completedLength: string; - uploadLength: string; - bitfield: string; - downloadSpeed: string; - uploadSpeed: string; - infoHash?: string; - numSeeders?: string; - seeder?: boolean; - pieceLength: string; - numPieces: string; - connections: string; - errorCode?: string; - errorMessage?: string; - followedBy?: string[]; - following: string; - belongsTo: string; - dir: string; - files: { - path: string; - length: string; - completedLength: string; - selected: string; - }[]; - bittorrent?: { - announceList: string[][]; - comment: string; - creationDate: string; - mode: "single" | "multi"; - info: { - name: string; - verifiedLength: string; - verifyIntegrityPending: string; - }; - }; - } - - export default class Aria2 { - constructor(options: any); - open: () => Promise; - call( - method: "addUri", - uris: string[], - options: { dir: string } - ): Promise; - call( - method: "tellStatus", - gid: string, - keys?: string[] - ): Promise; - call(method: "pause", gid: string): Promise; - call(method: "forcePause", gid: string): Promise; - call(method: "unpause", gid: string): Promise; - call(method: "remove", gid: string): Promise; - call(method: "forceRemove", gid: string): Promise; - call(method: "pauseAll"): Promise; - call(method: "forcePauseAll"): Promise; - listNotifications: () => [ - "onDownloadStart", - "onDownloadPause", - "onDownloadStop", - "onDownloadComplete", - "onDownloadError", - "onBtDownloadComplete", - ]; - on: (event: string, callback: (params: any) => void) => void; - } -} diff --git a/src/main/services/aria2c.ts b/src/main/services/aria2c.ts deleted file mode 100644 index b1b1da76d..000000000 --- a/src/main/services/aria2c.ts +++ /dev/null @@ -1,20 +0,0 @@ -import path from "node:path"; -import { spawn } from "node:child_process"; -import { app } from "electron"; - -export const startAria2 = () => { - const binaryPath = app.isPackaged - ? path.join(process.resourcesPath, "aria2", "aria2c") - : path.join(__dirname, "..", "..", "aria2", "aria2c"); - - return spawn( - binaryPath, - [ - "--enable-rpc", - "--rpc-listen-all", - "--file-allocation=none", - "--allow-overwrite=true", - ], - { stdio: "inherit", windowsHide: true } - ); -}; diff --git a/yarn.lock b/yarn.lock index 2dd42cb83..cf749216a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2677,14 +2677,6 @@ aria-query@^5.3.0: dependencies: dequal "^2.0.3" -aria2@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/aria2/-/aria2-4.1.2.tgz#0ecbc50beea82856c88b4de71dac336154f67362" - integrity sha512-qTBr2RY8RZQmiUmbj2KXFvkErNxU4aTHZszszzwhE8svy2PEVX+IYR/c4Rp9Tuw4QkeU8cylGy6McV6Yl8i7Qw== - dependencies: - node-fetch "^2.6.1" - ws "^7.4.0" - array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" @@ -5833,7 +5825,7 @@ node-domexception@^1.0.0: resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -7629,11 +7621,6 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^7.4.0: - version "7.5.10" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" - integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== - ws@^8.16.0: version "8.17.0" resolved "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz" From 68b361e60512d5cd2acb30e300a532414986aeb6 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Thu, 15 Aug 2024 20:46:21 +0100 Subject: [PATCH 12/12] feat: fixing real debrid download --- package.json | 3 +- .../events/torrenting/start-game-download.ts | 4 - .../services/download/download-manager.ts | 2 - .../download/generic-http-downloader.ts | 25 ++- src/main/services/download/http-download.ts | 2 +- .../download/real-debrid-downloader.ts | 58 ++----- src/main/services/real-debrid.ts | 2 +- src/renderer/src/app.css.ts | 2 +- src/renderer/src/components/hero/hero.css.ts | 1 - .../src/components/modal/modal.css.ts | 1 + src/renderer/src/main.tsx | 10 +- .../src/pages/downloads/downloads.tsx | 4 +- .../description-header/description-header.tsx | 5 +- .../pages/game-details/game-details.css.ts | 1 - .../pages/game-details/hero/hero-panel.css.ts | 1 + .../modals/download-settings-modal.tsx | 24 ++- .../modals/game-options-modal.css.ts | 1 - .../pages/game-details/sidebar/sidebar.css.ts | 1 - .../settings/settings-download-sources.tsx | 4 +- .../settings/settings-real-debrid.css.ts | 1 - .../src/pages/user/user-block-modal.tsx | 4 +- src/renderer/src/pages/user/user-content.tsx | 6 +- .../src/pages/user/user-sign-out-modal.tsx | 2 +- src/shared/index.ts | 3 + torrent-client/downloader.py | 62 ------- torrent-client/main.py | 26 +-- torrent-client/torrent_downloader.py | 158 ++++++++++++++++++ yarn.lock | 13 +- 28 files changed, 241 insertions(+), 185 deletions(-) delete mode 100644 torrent-client/downloader.py create mode 100644 torrent-client/torrent_downloader.py diff --git a/package.json b/package.json index 54d689b25..aa77084eb 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,7 @@ "dependencies": { "@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/utils": "^3.0.0", - "@fontsource/fira-mono": "^5.0.13", - "@fontsource/fira-sans": "^5.0.20", + "@fontsource/noto-sans": "^5.0.22", "@primer/octicons-react": "^19.9.0", "@reduxjs/toolkit": "^2.2.3", "@sentry/electron": "^5.1.0", diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index aa33c99ab..cea41596f 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -44,8 +44,6 @@ const startGameDownload = async ( ); if (game) { - console.log("game", game); - await gameRepository.update( { id: game.id, @@ -97,8 +95,6 @@ const startGameDownload = async ( }, }); - console.log(updatedGame); - createGame(updatedGame!); await downloadQueueRepository.delete({ game: { id: updatedGame!.id } }); diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index b9c9d5a61..52a666935 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -102,8 +102,6 @@ export class DownloadManager { const token = await GofileApi.authorize(); const downloadLink = await GofileApi.getDownloadLink(id!); - console.log(downloadLink, token, "<<<"); - GenericHTTPDownloader.startDownload(game, downloadLink, { Cookie: `accountToken=${token}`, }); diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index 4977d503d..8384a5fdf 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -2,7 +2,7 @@ import { Game } from "@main/entity"; import { gameRepository } from "@main/repository"; import { calculateETA } from "./helpers"; import { DownloadProgress } from "@types"; -import { HTTPDownload } from "./http-download"; +import { HttpDownload } from "./http-download"; export class GenericHTTPDownloader { private static downloads = new Map(); @@ -11,7 +11,7 @@ export class GenericHTTPDownloader { public static async getStatus() { if (this.downloadingGame) { const gid = this.downloads.get(this.downloadingGame.id)!; - const status = await HTTPDownload.getStatus(gid); + const status = HttpDownload.getStatus(gid); if (status) { const progress = @@ -60,7 +60,7 @@ export class GenericHTTPDownloader { const gid = this.downloads.get(this.downloadingGame!.id!); if (gid) { - await HTTPDownload.pauseDownload(gid); + await HttpDownload.pauseDownload(gid); } this.downloadingGame = null; @@ -76,26 +76,23 @@ export class GenericHTTPDownloader { if (this.downloads.has(game.id)) { await this.resumeDownload(game.id!); - return; } - if (downloadUrl) { - const gid = await HTTPDownload.startDownload( - game.downloadPath!, - downloadUrl, - headers - ); + const gid = await HttpDownload.startDownload( + game.downloadPath!, + downloadUrl, + headers + ); - this.downloads.set(game.id!, gid); - } + this.downloads.set(game.id!, gid); } static async cancelDownload(gameId: number) { const gid = this.downloads.get(gameId); if (gid) { - await HTTPDownload.cancelDownload(gid); + await HttpDownload.cancelDownload(gid); this.downloads.delete(gameId); } } @@ -104,7 +101,7 @@ export class GenericHTTPDownloader { const gid = this.downloads.get(gameId); if (gid) { - await HTTPDownload.resumeDownload(gid); + await HttpDownload.resumeDownload(gid); } } } diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index fb573ff45..cd8cbee51 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -2,7 +2,7 @@ import { DownloadItem } from "electron"; import { WindowManager } from "../window-manager"; import path from "node:path"; -export class HTTPDownload { +export class HttpDownload { private static id = 0; private static downloads: Record = {}; diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts index f61cb1ffa..c6925f57f 100644 --- a/src/main/services/download/real-debrid-downloader.ts +++ b/src/main/services/download/real-debrid-downloader.ts @@ -3,7 +3,7 @@ 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"; export class RealDebridDownloader { private static downloads = new Map(); @@ -13,19 +13,23 @@ export class RealDebridDownloader { private static async getRealDebridDownloadUrl() { if (this.realDebridTorrentId) { - const torrentInfo = await RealDebridClient.getTorrentInfo( + let torrentInfo = await RealDebridClient.getTorrentInfo( this.realDebridTorrentId ); - const { status, links } = torrentInfo; - - if (status === "waiting_files_selection") { + if (torrentInfo.status === "waiting_files_selection") { await RealDebridClient.selectAllFiles(this.realDebridTorrentId); - return null; + + torrentInfo = await RealDebridClient.getTorrentInfo( + this.realDebridTorrentId + ); } + const { links, status } = torrentInfo; + if (status === "downloaded") { const [link] = links; + const { download } = await RealDebridClient.unrestrictLink(link); return decodeURIComponent(download); } @@ -38,8 +42,6 @@ export class RealDebridDownloader { this.downloadingGame?.uri ); - console.log("download>>", download); - return decodeURIComponent(download); } @@ -49,7 +51,7 @@ export class RealDebridDownloader { public static async getStatus() { if (this.downloadingGame) { const gid = this.downloads.get(this.downloadingGame.id)!; - const status = await HTTPDownload.getStatus(gid); + const status = HttpDownload.getStatus(gid); if (status) { const progress = @@ -91,33 +93,6 @@ export class RealDebridDownloader { } } - if (this.realDebridTorrentId && this.downloadingGame) { - const torrentInfo = await RealDebridClient.getTorrentInfo( - this.realDebridTorrentId - ); - - const { status } = torrentInfo; - - if (status === "downloaded") { - this.startDownload(this.downloadingGame); - } - - const progress = torrentInfo.progress / 100; - const totalDownloaded = progress * torrentInfo.bytes; - - return { - numPeers: 0, - numSeeds: torrentInfo.seeders, - downloadSpeed: torrentInfo.speed, - timeRemaining: calculateETA( - torrentInfo.bytes, - totalDownloaded, - torrentInfo.speed - ), - isDownloadingMetadata: status === "magnet_conversion", - } as DownloadProgress; - } - return null; } @@ -125,7 +100,7 @@ export class RealDebridDownloader { if (this.downloadingGame) { const gid = this.downloads.get(this.downloadingGame.id); if (gid) { - await HTTPDownload.pauseDownload(gid); + await HttpDownload.pauseDownload(gid); } } @@ -146,18 +121,19 @@ export class RealDebridDownloader { ); } + this.downloadingGame = game; + const downloadUrl = await this.getRealDebridDownloadUrl(); if (downloadUrl) { this.realDebridTorrentId = null; - const gid = await HTTPDownload.startDownload( + const gid = await HttpDownload.startDownload( game.downloadPath!, downloadUrl ); this.downloads.set(game.id!, gid); - this.downloadingGame = game; } } @@ -165,7 +141,7 @@ export class RealDebridDownloader { const gid = this.downloads.get(gameId); if (gid) { - await HTTPDownload.cancelDownload(gid); + await HttpDownload.cancelDownload(gid); this.downloads.delete(gameId); } @@ -177,7 +153,7 @@ export class RealDebridDownloader { const gid = this.downloads.get(gameId); if (gid) { - await HTTPDownload.resumeDownload(gid); + await HttpDownload.resumeDownload(gid); } } } diff --git a/src/main/services/real-debrid.ts b/src/main/services/real-debrid.ts index 2e0debe6e..26ba4c797 100644 --- a/src/main/services/real-debrid.ts +++ b/src/main/services/real-debrid.ts @@ -46,7 +46,7 @@ export class RealDebridClient { static async selectAllFiles(id: string) { const searchParams = new URLSearchParams({ files: "all" }); - await this.instance.post( + return this.instance.post( `/torrents/selectFiles/${id}`, searchParams.toString() ); diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts index a5f9394b0..c829021af 100644 --- a/src/renderer/src/app.css.ts +++ b/src/renderer/src/app.css.ts @@ -26,7 +26,7 @@ globalStyle("html, body, #root, main", { globalStyle("body", { overflow: "hidden", userSelect: "none", - fontFamily: "'Fira Mono', monospace", + fontFamily: "Noto Sans, sans-serif", fontSize: vars.size.body, background: vars.color.background, color: vars.color.body, diff --git a/src/renderer/src/components/hero/hero.css.ts b/src/renderer/src/components/hero/hero.css.ts index cdb36ee27..eaf0a1012 100644 --- a/src/renderer/src/components/hero/hero.css.ts +++ b/src/renderer/src/components/hero/hero.css.ts @@ -45,7 +45,6 @@ export const description = style({ maxWidth: "700px", color: vars.color.muted, textAlign: "left", - fontFamily: "'Fira Sans', sans-serif", lineHeight: "20px", marginTop: `${SPACING_UNIT * 2}px`, }); diff --git a/src/renderer/src/components/modal/modal.css.ts b/src/renderer/src/components/modal/modal.css.ts index 451540155..d9d14fda9 100644 --- a/src/renderer/src/components/modal/modal.css.ts +++ b/src/renderer/src/components/modal/modal.css.ts @@ -24,6 +24,7 @@ export const modal = recipe({ animation: `${scaleFadeIn} 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running`, backgroundColor: vars.color.background, borderRadius: "4px", + minWidth: "400px", maxWidth: "600px", color: vars.color.body, maxHeight: "100%", diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index f87d66bfa..b88348f0e 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -8,12 +8,10 @@ import { HashRouter, Route, Routes } from "react-router-dom"; import * as Sentry from "@sentry/electron/renderer"; -import "@fontsource/fira-mono/400.css"; -import "@fontsource/fira-mono/500.css"; -import "@fontsource/fira-mono/700.css"; -import "@fontsource/fira-sans/400.css"; -import "@fontsource/fira-sans/500.css"; -import "@fontsource/fira-sans/700.css"; +import "@fontsource/noto-sans/400.css"; +import "@fontsource/noto-sans/500.css"; +import "@fontsource/noto-sans/700.css"; + import "react-loading-skeleton/dist/skeleton.css"; import { App } from "./app"; diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx index 531bc5268..5a9c228c1 100644 --- a/src/renderer/src/pages/downloads/downloads.tsx +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -132,9 +132,7 @@ export function Downloads() {

{t("no_downloads_title")}

-

- {t("no_downloads_description")} -

+

{t("no_downloads_description")}

)} diff --git a/src/renderer/src/pages/game-details/description-header/description-header.tsx b/src/renderer/src/pages/game-details/description-header/description-header.tsx index e42725349..cd73c52a7 100644 --- a/src/renderer/src/pages/game-details/description-header/description-header.tsx +++ b/src/renderer/src/pages/game-details/description-header/description-header.tsx @@ -19,7 +19,10 @@ export function DescriptionHeader() { date: shopDetails?.release_date.date, })}

-

{t("publisher", { publisher: shopDetails.publishers[0] })}

+ + {Array.isArray(shopDetails.publishers) && ( +

{t("publisher", { publisher: shopDetails.publishers[0] })}

+ )} ); diff --git a/src/renderer/src/pages/game-details/game-details.css.ts b/src/renderer/src/pages/game-details/game-details.css.ts index f0bbfd2ee..3dc0ec946 100644 --- a/src/renderer/src/pages/game-details/game-details.css.ts +++ b/src/renderer/src/pages/game-details/game-details.css.ts @@ -101,7 +101,6 @@ export const descriptionContent = style({ export const description = style({ userSelect: "text", lineHeight: "22px", - fontFamily: "'Fira Sans', sans-serif", fontSize: "16px", padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`, "@media": { diff --git a/src/renderer/src/pages/game-details/hero/hero-panel.css.ts b/src/renderer/src/pages/game-details/hero/hero-panel.css.ts index e10d55a5a..c379c1c33 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel.css.ts +++ b/src/renderer/src/pages/game-details/hero/hero-panel.css.ts @@ -9,6 +9,7 @@ export const panel = recipe({ height: "72px", minHeight: "72px", padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`, + backgroundColor: vars.color.darkBackground, display: "flex", alignItems: "center", justifyContent: "space-between", diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index d102d2b2d..dff73ea01 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -60,14 +60,22 @@ export function DownloadSettingsModal({ .then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath)); } - if ( - userPreferences?.realDebridApiToken && - downloaders.includes(Downloader.RealDebrid) - ) { - setSelectedDownloader(Downloader.RealDebrid); - } else { - setSelectedDownloader(downloaders[0]); - } + const filteredDownloaders = downloaders.filter((downloader) => { + if (downloader === Downloader.RealDebrid) + return userPreferences?.realDebridApiToken; + return true; + }); + + /* Gives preference to Real Debrid */ + const selectedDownloader = filteredDownloaders.includes( + Downloader.RealDebrid + ) + ? Downloader.RealDebrid + : filteredDownloaders[0]; + + setSelectedDownloader( + selectedDownloader === undefined ? null : selectedDownloader + ); }, [ userPreferences?.downloadsPath, downloaders, diff --git a/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts b/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts index 8bf0ae7f7..f844a686b 100644 --- a/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts +++ b/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts @@ -15,7 +15,6 @@ export const gameOptionHeader = style({ }); export const gameOptionHeaderDescription = style({ - fontFamily: "'Fira Sans', sans-serif", fontWeight: "400", }); diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index e6d8b60a7..734e6eb3f 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -46,7 +46,6 @@ export const requirementButton = style({ export const requirementsDetails = style({ padding: `${SPACING_UNIT * 2}px`, lineHeight: "22px", - fontFamily: "'Fira Sans', sans-serif", fontSize: "16px", }); diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx index 8214117a3..64d653b0f 100644 --- a/src/renderer/src/pages/settings/settings-download-sources.tsx +++ b/src/renderer/src/pages/settings/settings-download-sources.tsx @@ -82,9 +82,7 @@ export function SettingsDownloadSources() { onAddDownloadSource={handleAddDownloadSource} /> -

- {t("download_sources_description")} -

+

{t("download_sources_description")}

{t("no_recent_activity_title")}

- {isMe && ( -

- {t("no_recent_activity_description")} -

- )} + {isMe &&

{t("no_recent_activity_description")}

} ) : (
-

{t("sign_out_modal_text")}

+

{t("sign_out_modal_text")}