From 0c1a75eedd4ba89e73e6f536a7af594e6a189eec Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:23:39 -0300 Subject: [PATCH 1/6] poc: psutil for process watcher --- requirements.txt | 1 + torrent-client/main.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/requirements.txt b/requirements.txt index 6cee730a..b1488003 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ cx_Freeze cx_Logging; sys_platform == 'win32' lief; sys_platform == 'win32' pywin32; sys_platform == 'win32' +psutil diff --git a/torrent-client/main.py b/torrent-client/main.py index 9a5737a4..a0d2a57e 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -2,7 +2,9 @@ import sys from http.server import HTTPServer, BaseHTTPRequestHandler import json import urllib.parse +import psutil from downloader import Downloader +from time import time torrent_port = sys.argv[1] http_port = sys.argv[2] @@ -33,6 +35,15 @@ class Handler(BaseHTTPRequestHandler): if self.path == "/healthcheck": self.send_response(200) self.end_headers() + + if self.path == "/process": + start_time = time() + process_path = set([proc.info["exe"] for proc in psutil.process_iter(['exe'])]) + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + self.wfile.write(json.dumps(list(process_path)).encode('utf-8')) + print(time() - start_time) def do_POST(self): if self.path == "/action": From 75c8f69e816033223e56eb9064b76e2ecc4b89b1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 3 Jul 2024 11:23:50 -0300 Subject: [PATCH 2/6] feat: get process list from rpc --- src/main/events/auth/sign-out.ts | 4 +-- src/main/index.ts | 4 +-- src/main/main.ts | 16 +++++++++--- .../services/download/download-manager.ts | 12 ++++----- src/main/services/download/index.ts | 2 +- .../{torrent-downloader.ts => rpc-manager.ts} | 20 +++++++++------ src/main/services/download/torrent-client.ts | 4 +-- src/main/services/process-watcher.ts | 17 +++---------- .../game-details/hero/hero-panel-actions.tsx | 2 +- torrent-client/main.py | 25 ++++++++++++------- 10 files changed, 57 insertions(+), 49 deletions(-) rename src/main/services/download/{torrent-downloader.ts => rpc-manager.ts} (88%) diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index 9c2ce2e0..0eb8cf07 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -1,6 +1,6 @@ import { registerEvent } from "../register-event"; import * as Sentry from "@sentry/electron/main"; -import { HydraApi, TorrentDownloader, gamesPlaytime } from "@main/services"; +import { HydraApi, RPCManager, gamesPlaytime } from "@main/services"; import { dataSource } from "@main/data-source"; import { DownloadQueue, Game, UserAuth } from "@main/entity"; @@ -24,7 +24,7 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => { Sentry.setUser(null); /* Disconnects libtorrent */ - TorrentDownloader.kill(); + RPCManager.kill(); await Promise.all([ databaseOperations, diff --git a/src/main/index.ts b/src/main/index.ts index cb20733a..1372539d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -5,7 +5,7 @@ import i18n from "i18next"; import path from "node:path"; import url from "node:url"; import { electronApp, optimizer } from "@electron-toolkit/utils"; -import { logger, TorrentDownloader, WindowManager } from "@main/services"; +import { logger, RPCManager, WindowManager } from "@main/services"; import { dataSource } from "@main/data-source"; import * as resources from "@locales"; import { userPreferencesRepository } from "@main/repository"; @@ -116,7 +116,7 @@ app.on("window-all-closed", () => { app.on("before-quit", () => { /* Disconnects libtorrent */ - TorrentDownloader.kill(); + RPCManager.kill(); }); app.on("activate", () => { diff --git a/src/main/main.ts b/src/main/main.ts index 1abd8c2f..759b1714 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,4 +1,9 @@ -import { DownloadManager, RepacksManager, startMainLoop } from "./services"; +import { + DownloadManager, + RepacksManager, + RPCManager, + startMainLoop, +} from "./services"; import { downloadQueueRepository, repackRepository, @@ -12,8 +17,6 @@ import { MoreThan } from "typeorm"; import { HydraApi } from "./services/hydra-api"; import { uploadGamesBatch } from "./services/library-sync"; -startMainLoop(); - const loadState = async (userPreferences: UserPreferences | null) => { await RepacksManager.updateRepacks(); @@ -35,8 +38,13 @@ const loadState = async (userPreferences: UserPreferences | null) => { }, }); - if (nextQueueItem?.game.status === "active") + if (nextQueueItem?.game.status === "active") { DownloadManager.startDownload(nextQueueItem.game); + } else { + RPCManager.spawn(); + } + + startMainLoop(); const now = new Date(); diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index f7553c9c..8d402e5a 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -1,6 +1,6 @@ import { Game } from "@main/entity"; import { Downloader } from "@shared"; -import { TorrentDownloader } from "./torrent-downloader"; +import { RPCManager } from "./rpc-manager"; import { WindowManager } from "../window-manager"; import { downloadQueueRepository, gameRepository } from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; @@ -16,7 +16,7 @@ export class DownloadManager { if (this.currentDownloader === Downloader.RealDebrid) { status = await RealDebridDownloader.getStatus(); } else { - status = await TorrentDownloader.getStatus(); + status = await RPCManager.getStatus(); } if (status) { @@ -65,7 +65,7 @@ export class DownloadManager { if (this.currentDownloader === Downloader.RealDebrid) { RealDebridDownloader.pauseDownload(); } else { - await TorrentDownloader.pauseDownload(); + await RPCManager.pauseDownload(); } WindowManager.mainWindow?.setProgressBar(-1); @@ -77,7 +77,7 @@ export class DownloadManager { RealDebridDownloader.startDownload(game); this.currentDownloader = Downloader.RealDebrid; } else { - TorrentDownloader.startDownload(game); + RPCManager.startDownload(game); this.currentDownloader = Downloader.Torrent; } } @@ -86,7 +86,7 @@ export class DownloadManager { if (this.currentDownloader === Downloader.RealDebrid) { RealDebridDownloader.cancelDownload(); } else { - TorrentDownloader.cancelDownload(gameId); + RPCManager.cancelDownload(gameId); } WindowManager.mainWindow?.setProgressBar(-1); @@ -98,7 +98,7 @@ export class DownloadManager { RealDebridDownloader.startDownload(game); this.currentDownloader = Downloader.RealDebrid; } else { - TorrentDownloader.startDownload(game); + RPCManager.startDownload(game); this.currentDownloader = Downloader.Torrent; } } diff --git a/src/main/services/download/index.ts b/src/main/services/download/index.ts index 6244b81a..0298f7d8 100644 --- a/src/main/services/download/index.ts +++ b/src/main/services/download/index.ts @@ -1,2 +1,2 @@ export * from "./download-manager"; -export * from "./torrent-downloader"; +export * from "./rpc-manager"; diff --git a/src/main/services/download/torrent-downloader.ts b/src/main/services/download/rpc-manager.ts similarity index 88% rename from src/main/services/download/torrent-downloader.ts rename to src/main/services/download/rpc-manager.ts index e35bb617..12d40a28 100644 --- a/src/main/services/download/torrent-downloader.ts +++ b/src/main/services/download/rpc-manager.ts @@ -15,8 +15,8 @@ import { LibtorrentPayload, } from "./types"; -export class TorrentDownloader { - private static torrentClient: cp.ChildProcess | null = null; +export class RPCManager { + private static rpcProcess: cp.ChildProcess | null = null; private static downloadingGameId = -1; private static rpc = axios.create({ @@ -26,18 +26,22 @@ export class TorrentDownloader { }, }); - private static spawn(args: StartDownloadPayload) { - this.torrentClient = startTorrentClient(args); + public static spawn(args?: StartDownloadPayload) { + this.rpcProcess = startTorrentClient(args); } public static kill() { - if (this.torrentClient) { - this.torrentClient.kill(); - this.torrentClient = null; + if (this.rpcProcess) { + this.rpcProcess.kill(); + this.rpcProcess = null; this.downloadingGameId = -1; } } + public static async getProccessList() { + return (await this.rpc.get("/process-list")).data; + } + public static async getStatus() { if (this.downloadingGameId === -1) return null; @@ -113,7 +117,7 @@ export class TorrentDownloader { } static async startDownload(game: Game) { - if (!this.torrentClient) { + if (!this.rpcProcess) { this.spawn({ game_id: game.id, magnet: game.uri!, diff --git a/src/main/services/download/torrent-client.ts b/src/main/services/download/torrent-client.ts index 176ec664..93d20b7f 100644 --- a/src/main/services/download/torrent-client.ts +++ b/src/main/services/download/torrent-client.ts @@ -15,12 +15,12 @@ export const BITTORRENT_PORT = "5881"; export const RPC_PORT = "8084"; export const RPC_PASSWORD = crypto.randomBytes(32).toString("hex"); -export const startTorrentClient = (args: StartDownloadPayload) => { +export const startTorrentClient = (args?: StartDownloadPayload) => { const commonArgs = [ BITTORRENT_PORT, RPC_PORT, RPC_PASSWORD, - encodeURIComponent(JSON.stringify(args)), + args ? encodeURIComponent(JSON.stringify(args)) : "", ]; if (app.isPackaged) { diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index f20b4128..4dc12bc1 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -1,11 +1,9 @@ -import path from "node:path"; - import { IsNull, Not } from "typeorm"; import { gameRepository } from "@main/repository"; -import { getProcesses } from "@main/helpers"; import { WindowManager } from "./window-manager"; import { createGame, updateGamePlaytime } from "./library-sync"; import { GameRunning } from "@types"; +import { RPCManager } from "./download"; export const gamesPlaytime = new Map< number, @@ -22,22 +20,13 @@ export const watchProcesses = async () => { if (games.length === 0) return; - const processes = await getProcesses(); + const processes = (await RPCManager.getProccessList()) || []; for (const game of games) { const executablePath = game.executablePath!; - const basename = path.win32.basename(executablePath); - const basenameWithoutExtension = path.win32.basename( - executablePath, - path.extname(executablePath) - ); const gameProcess = processes.find((runningProcess) => { - if (process.platform === "win32") { - return runningProcess.name === basename; - } - - return [basename, basenameWithoutExtension].includes(runningProcess.name); + return executablePath == runningProcess; }); if (gameProcess) { diff --git a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx index 4741668e..795aa8af 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx @@ -122,7 +122,7 @@ export function HeroPanelActions() {