From 8c0c3e617baeda5434e9dfd2fb50a130288b48d2 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:05:06 -0300 Subject: [PATCH 1/2] feat: add logs for python process --- src/main/services/download/python-instance.ts | 32 +++++++++++++++---- src/main/services/download/torrent-client.ts | 25 ++++++++++++--- src/main/services/logger.ts | 5 +++ torrent-client/main.py | 17 ++++++++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index c534e41d..345a3588 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -19,6 +19,7 @@ import { LibtorrentPayload, ProcessPayload, } from "./types"; +import { pythonInstanceLogger as logger } from "../logger"; export class PythonInstance { private static pythonProcess: cp.ChildProcess | null = null; @@ -32,11 +33,13 @@ export class PythonInstance { }); public static spawn(args?: StartDownloadPayload) { + logger.log("spawning python process with args:", args); this.pythonProcess = startRPCClient(args); } public static kill() { if (this.pythonProcess) { + logger.log("killing python process"); this.pythonProcess.kill(); this.pythonProcess = null; this.downloadingGameId = -1; @@ -45,7 +48,10 @@ export class PythonInstance { public static killTorrent() { if (this.pythonProcess) { - this.rpc.post("/action", { action: "kill-torrent" }); + logger.log("killing torrent in python process"); + this.rpc + .post("/action", { action: "kill-torrent" }) + .catch((err) => logger.error(err)); this.downloadingGameId = -1; } } @@ -138,12 +144,14 @@ export class PythonInstance { 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); + await this.rpc + .post("/action", { + action: "start", + game_id: game.id, + magnet: game.uri, + save_path: game.downloadPath, + } as StartDownloadPayload) + .catch(this.handleRpcError); } this.downloadingGameId = game.id; @@ -159,4 +167,14 @@ export class PythonInstance { this.downloadingGameId = -1; } + + private static async handleRpcError(_error: unknown) { + await this.rpc.get("/healthcheck").catch(() => { + logger.error( + "RPC healthcheck failed. Killing process and starting again" + ); + this.kill(); + this.spawn(); + }); + } } diff --git a/src/main/services/download/torrent-client.ts b/src/main/services/download/torrent-client.ts index 93d20b7f..2a16acad 100644 --- a/src/main/services/download/torrent-client.ts +++ b/src/main/services/download/torrent-client.ts @@ -4,6 +4,8 @@ import crypto from "node:crypto"; import fs from "node:fs"; import { app, dialog } from "electron"; import type { StartDownloadPayload } from "./types"; +import { Readable } from "node:stream"; +import { pythonInstanceLogger as logger } from "../logger"; const binaryNameByPlatform: Partial> = { darwin: "hydra-download-manager", @@ -15,6 +17,13 @@ export const BITTORRENT_PORT = "5881"; export const RPC_PORT = "8084"; export const RPC_PASSWORD = crypto.randomBytes(32).toString("hex"); +const logStderr = (readable: Readable | null) => { + if (!readable) return; + + readable.setEncoding("utf-8"); + readable.on("data", logger.log); +}; + export const startTorrentClient = (args?: StartDownloadPayload) => { const commonArgs = [ BITTORRENT_PORT, @@ -40,10 +49,14 @@ export const startTorrentClient = (args?: StartDownloadPayload) => { app.quit(); } - return cp.spawn(binaryPath, commonArgs, { - stdio: "inherit", + const childProcess = cp.spawn(binaryPath, commonArgs, { windowsHide: true, + stdio: ["inherit", "inherit"], }); + + logStderr(childProcess.stderr); + + return childProcess; } else { const scriptPath = path.join( __dirname, @@ -53,8 +66,12 @@ export const startTorrentClient = (args?: StartDownloadPayload) => { "main.py" ); - return cp.spawn("python3", [scriptPath, ...commonArgs], { - stdio: "inherit", + const childProcess = cp.spawn("python3", [scriptPath, ...commonArgs], { + stdio: ["inherit", "inherit"], }); + + logStderr(childProcess.stderr); + + return childProcess; } }; diff --git a/src/main/services/logger.ts b/src/main/services/logger.ts index 8da27a9e..1eb7060b 100644 --- a/src/main/services/logger.ts +++ b/src/main/services/logger.ts @@ -6,6 +6,10 @@ log.transports.file.resolvePathFn = ( _: log.PathVariables, message?: log.LogMessage | undefined ) => { + if (message?.scope === "python-instance") { + return path.join(logsPath, "pythoninstance.txt"); + } + if (message?.level === "error") { return path.join(logsPath, "error.txt"); } @@ -23,4 +27,5 @@ log.errorHandler.startCatching({ log.initialize(); +export const pythonInstanceLogger = log.scope("python-instance"); export const logger = log.scope("main"); diff --git a/torrent-client/main.py b/torrent-client/main.py index 004cd108..9a0bf5b0 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -20,6 +20,23 @@ if start_download_payload: class Handler(BaseHTTPRequestHandler): rpc_password_header = 'x-hydra-rpc-password' + skip_log_routes = [ + "process-list", + "status" + ] + + def log_error(self, format, *args): + sys.stderr.write("%s - - [%s] %s\n" % + (self.address_string(), + self.log_date_time_string(), + format%args)) + + def log_message(self, format, *args): + for route in self.skip_log_routes: + if route in args[0]: return + + super().log_message(format, *args) + def do_GET(self): if self.path == "/status": if self.headers.get(self.rpc_password_header) != rpc_password: From 3d8c63bc40150cba41678094437be5d951f7af2e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:05:31 -0300 Subject: [PATCH 2/2] feat: truncate game title on tray --- src/main/services/download/python-instance.ts | 4 +--- src/main/services/window-manager.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index 345a3588..37ec17db 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -49,9 +49,7 @@ export class PythonInstance { public static killTorrent() { if (this.pythonProcess) { logger.log("killing torrent in python process"); - this.rpc - .post("/action", { action: "kill-torrent" }) - .catch((err) => logger.error(err)); + this.rpc.post("/action", { action: "kill-torrent" }); this.downloadingGameId = -1; } } diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 201b13ad..025e7219 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -158,7 +158,7 @@ export class WindowManager { const recentlyPlayedGames: Array = games.map(({ title, executablePath }) => ({ - label: title, + label: title.length > 15 ? `${title.slice(0, 15)}…` : title, type: "normal", click: async () => { if (!executablePath) return;