diff --git a/src/main/services/download/torrent-client.ts b/src/main/services/download/torrent-client.ts index c94d5e87..dea1b9e7 100644 --- a/src/main/services/download/torrent-client.ts +++ b/src/main/services/download/torrent-client.ts @@ -2,6 +2,7 @@ import path from "node:path"; import cp from "node:child_process"; import fs from "node:fs"; import { app, dialog } from "electron"; +import type { StartDownloadPayload } from "./types"; const binaryNameByPlatform: Partial> = { darwin: "hydra-download-manager", @@ -12,9 +13,13 @@ const binaryNameByPlatform: Partial> = { export const BITTORRENT_PORT = "5881"; export const RPC_PORT = "8084"; -const commonArgs = [BITTORRENT_PORT, RPC_PORT]; +export const startTorrentClient = (args: StartDownloadPayload) => { + const commonArgs = [ + BITTORRENT_PORT, + RPC_PORT, + encodeURIComponent(JSON.stringify(args)), + ]; -export const startTorrentClient = () => { if (app.isPackaged) { const binaryName = binaryNameByPlatform[process.platform]!; const binaryPath = path.join( diff --git a/src/main/services/download/torrent-downloader.ts b/src/main/services/download/torrent-downloader.ts index d54e607a..4a7fa660 100644 --- a/src/main/services/download/torrent-downloader.ts +++ b/src/main/services/download/torrent-downloader.ts @@ -7,59 +7,24 @@ import { DownloadProgress } from "@types"; import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; import { calculateETA } from "./helpers"; import axios from "axios"; -import { sleep } from "@main/helpers"; -import { logger } from "../logger"; - -enum LibtorrentStatus { - CheckingFiles = 1, - DownloadingMetadata = 2, - Downloading = 3, - Finished = 4, - Seeding = 5, -} - -interface LibtorrentPayload { - progress: number; - numPeers: number; - numSeeds: number; - downloadSpeed: number; - bytesDownloaded: number; - fileSize: number; - folderName: string; - status: LibtorrentStatus; - gameId: number; -} +import { + type CancelDownloadPayload, + type StartDownloadPayload, + type PauseDownloadPayload, + LibtorrentStatus, + LibtorrentPayload, +} from "./types"; export class TorrentDownloader { private static torrentClient: cp.ChildProcess | null = null; private static downloadingGameId = -1; + private static rpc = axios.create({ baseURL: `http://localhost:${RPC_PORT}`, }); - private static async healthCheck(retries = 15) { - try { - await this.rpc.get("/healthcheck"); - } catch (err) { - if (retries === 0) { - throw new Error("Failed to connect to libtorrent client"); - } - - await sleep(1000); - - return this.healthCheck(retries - 1); - } - } - - private static async spawn() { - try { - this.torrentClient = startTorrentClient(); - await this.healthCheck(); - - logger.log("libtorrent client started"); - } catch (err) { - logger.error(err); - } + private static spawn(args: StartDownloadPayload) { + this.torrentClient = startTorrentClient(args); } public static kill() { @@ -71,7 +36,6 @@ export class TorrentDownloader { } public static async getStatus() { - if (!this.torrentClient) this.spawn(); if (this.downloadingGameId === -1) return null; const response = await this.rpc.get("/status"); @@ -134,36 +98,42 @@ export class TorrentDownloader { } static async pauseDownload() { - if (!this.torrentClient) await this.spawn(); - - await this.rpc.post("/action", { - action: "pause", - game_id: this.downloadingGameId, - }); + await this.rpc + .post("/action", { + action: "pause", + game_id: this.downloadingGameId, + } as PauseDownloadPayload) + .catch(() => {}); this.downloadingGameId = -1; } static async startDownload(game: Game) { - if (!this.torrentClient) await this.spawn(); - - await this.rpc.post("/action", { - action: "start", - game_id: game.id, - magnet: game.uri, - save_path: game.downloadPath, - }); + if (!this.torrentClient) { + this.spawn({ + game_id: game.id, + magnet: game.uri!, + save_path: game.downloadPath!, + }); + } else { + await this.rpc.post("/action", { + action: "start", + game_id: game.id, + magnet: game.uri, + save_path: game.downloadPath, + } as StartDownloadPayload); + } this.downloadingGameId = game.id; } static async cancelDownload(gameId: number) { - if (!this.torrentClient) await this.spawn(); - - await this.rpc.post("/action", { - action: "cancel", - game_id: gameId, - }); + await this.rpc + .post("/action", { + action: "cancel", + game_id: gameId, + } as CancelDownloadPayload) + .catch(() => {}); this.downloadingGameId = -1; } diff --git a/src/main/services/download/types.ts b/src/main/services/download/types.ts new file mode 100644 index 00000000..516cd84f --- /dev/null +++ b/src/main/services/download/types.ts @@ -0,0 +1,33 @@ +export interface StartDownloadPayload { + game_id: number; + magnet: string; + save_path: string; +} + +export interface PauseDownloadPayload { + game_id: number; +} + +export interface CancelDownloadPayload { + game_id: number; +} + +export enum LibtorrentStatus { + CheckingFiles = 1, + DownloadingMetadata = 2, + Downloading = 3, + Finished = 4, + Seeding = 5, +} + +export interface LibtorrentPayload { + progress: number; + numPeers: number; + numSeeds: number; + downloadSpeed: number; + bytesDownloaded: number; + fileSize: number; + folderName: string; + status: LibtorrentStatus; + gameId: number; +} diff --git a/torrent-client/main.py b/torrent-client/main.py index 19f38f6a..01dd5922 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -4,9 +4,11 @@ from http.server import HTTPServer, BaseHTTPRequestHandler import json import threading import time +import urllib.parse torrent_port = sys.argv[1] http_port = sys.argv[2] +initial_download = json.loads(urllib.parse.unquote(sys.argv[3])) class Downloader: def __init__(self): @@ -62,6 +64,8 @@ class Downloader: downloader = Downloader() +downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path']) + class Handler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/status":