mirror of
				https://github.com/hydralauncher/hydra.git
				synced 2025-03-09 15:40:26 +00:00 
			
		
		
		
	feat: remove pslist and use sudo-prompt to close game if needed
This commit is contained in:
		
							parent
							
								
									0f5db4f34e
								
							
						
					
					
						commit
						1397e3932d
					
				
					 14 changed files with 51 additions and 114 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
.vscode
 | 
			
		||||
node_modules
 | 
			
		||||
hydra-download-manager/
 | 
			
		||||
fastlist.exe
 | 
			
		||||
__pycache__
 | 
			
		||||
dist
 | 
			
		||||
out
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,10 +5,7 @@ directories:
 | 
			
		|||
extraResources:
 | 
			
		||||
  - hydra-download-manager
 | 
			
		||||
  - seeds
 | 
			
		||||
  - from: node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe
 | 
			
		||||
    to: fastlist.exe
 | 
			
		||||
  - from: node_modules/create-desktop-shortcuts/src/windows.vbs
 | 
			
		||||
  - from: resources/hydralauncher.vbs
 | 
			
		||||
files:
 | 
			
		||||
  - "!**/.vscode/*"
 | 
			
		||||
  - "!src/*"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +17,6 @@ asarUnpack:
 | 
			
		|||
  - resources/**
 | 
			
		||||
win:
 | 
			
		||||
  executableName: Hydra
 | 
			
		||||
  requestedExecutionLevel: requireAdministrator
 | 
			
		||||
  target:
 | 
			
		||||
    - nsis
 | 
			
		||||
    - portable
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +29,6 @@ nsis:
 | 
			
		|||
  allowToChangeInstallationDirectory: true
 | 
			
		||||
portable:
 | 
			
		||||
  artifactName: ${name}-${version}-portable.${ext}
 | 
			
		||||
  requestExecutionLevel: admin
 | 
			
		||||
mac:
 | 
			
		||||
  entitlementsInherit: build/entitlements.mac.plist
 | 
			
		||||
  extendInfo:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,11 +65,11 @@
 | 
			
		|||
    "lottie-react": "^2.4.0",
 | 
			
		||||
    "parse-torrent": "^11.0.16",
 | 
			
		||||
    "piscina": "^4.5.1",
 | 
			
		||||
    "ps-list": "^8.1.1",
 | 
			
		||||
    "react-i18next": "^14.1.0",
 | 
			
		||||
    "react-loading-skeleton": "^3.4.0",
 | 
			
		||||
    "react-redux": "^9.1.1",
 | 
			
		||||
    "react-router-dom": "^6.22.3",
 | 
			
		||||
    "sudo-prompt": "^9.2.1",
 | 
			
		||||
    "typeorm": "^0.3.20",
 | 
			
		||||
    "user-agents": "^1.1.193",
 | 
			
		||||
    "yaml": "^2.4.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
Set WshShell = CreateObject("WScript.Shell" ) 
 | 
			
		||||
WshShell.Run """%localappdata%\Programs\Hydra\Hydra.exe""", 0 'Must quote command if it has spaces; must escape quotes
 | 
			
		||||
Set WshShell = Nothing
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +14,3 @@ export const logsPath = path.join(app.getPath("appData"), "hydra", "logs");
 | 
			
		|||
export const seedsPath = app.isPackaged
 | 
			
		||||
  ? path.join(process.resourcesPath, "seeds")
 | 
			
		||||
  : path.join(__dirname, "..", "..", "seeds");
 | 
			
		||||
 | 
			
		||||
export const windowsStartupPath = path.join(
 | 
			
		||||
  app.getPath("appData"),
 | 
			
		||||
  "Microsoft",
 | 
			
		||||
  "Windows",
 | 
			
		||||
  "Start Menu",
 | 
			
		||||
  "Programs",
 | 
			
		||||
  "Startup"
 | 
			
		||||
);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,39 +1,45 @@
 | 
			
		|||
import path from "node:path";
 | 
			
		||||
 | 
			
		||||
import { gameRepository } from "@main/repository";
 | 
			
		||||
import { getProcesses } from "@main/helpers";
 | 
			
		||||
 | 
			
		||||
import { registerEvent } from "../register-event";
 | 
			
		||||
import { RPCManager, logger } from "@main/services";
 | 
			
		||||
import sudo from "sudo-prompt";
 | 
			
		||||
import { app } from "electron";
 | 
			
		||||
 | 
			
		||||
const getKillCommand = (pid: number) => {
 | 
			
		||||
  if (process.platform == "win32") {
 | 
			
		||||
    return `taskkill /PID ${pid}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return `kill -9 ${pid}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const closeGame = async (
 | 
			
		||||
  _event: Electron.IpcMainInvokeEvent,
 | 
			
		||||
  gameId: number
 | 
			
		||||
) => {
 | 
			
		||||
  const processes = await getProcesses();
 | 
			
		||||
  const processes = await RPCManager.getProcessList();
 | 
			
		||||
  const game = await gameRepository.findOne({
 | 
			
		||||
    where: { id: gameId, isDeleted: false },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (!game) return false;
 | 
			
		||||
 | 
			
		||||
  const executablePath = game.executablePath!;
 | 
			
		||||
 | 
			
		||||
  const basename = path.win32.basename(executablePath);
 | 
			
		||||
  const basenameWithoutExtension = path.win32.basename(
 | 
			
		||||
    executablePath,
 | 
			
		||||
    path.extname(executablePath)
 | 
			
		||||
  );
 | 
			
		||||
  if (!game) return;
 | 
			
		||||
 | 
			
		||||
  const gameProcess = processes.find((runningProcess) => {
 | 
			
		||||
    if (process.platform === "win32") {
 | 
			
		||||
      return runningProcess.name === basename;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [basename, basenameWithoutExtension].includes(runningProcess.name);
 | 
			
		||||
    return runningProcess.exe === game.executablePath;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (gameProcess) return process.kill(gameProcess.pid);
 | 
			
		||||
  return false;
 | 
			
		||||
  if (gameProcess) {
 | 
			
		||||
    try {
 | 
			
		||||
      process.kill(gameProcess.pid);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      sudo.exec(
 | 
			
		||||
        getKillCommand(gameProcess.pid),
 | 
			
		||||
        { name: app.getName() },
 | 
			
		||||
        (error, _stdout, _stderr) => {
 | 
			
		||||
          logger.error(error);
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
registerEvent("closeGame", closeGame);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,6 @@
 | 
			
		|||
import { windowsStartupPath } from "@main/constants";
 | 
			
		||||
import { registerEvent } from "../register-event";
 | 
			
		||||
import AutoLaunch from "auto-launch";
 | 
			
		||||
import { app } from "electron";
 | 
			
		||||
import fs from "node:fs";
 | 
			
		||||
import path from "node:path";
 | 
			
		||||
 | 
			
		||||
const autoLaunch = async (
 | 
			
		||||
  _event: Electron.IpcMainInvokeEvent,
 | 
			
		||||
| 
						 | 
				
			
			@ -15,23 +12,10 @@ const autoLaunch = async (
 | 
			
		|||
    name: app.getName(),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (process.platform == "win32") {
 | 
			
		||||
    const destination = path.join(windowsStartupPath, "Hydra.vbs");
 | 
			
		||||
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
      const scriptPath = path.join(process.resourcesPath, "hydralauncher.vbs");
 | 
			
		||||
 | 
			
		||||
      fs.copyFileSync(scriptPath, destination);
 | 
			
		||||
    } else {
 | 
			
		||||
      appLauncher.disable().catch();
 | 
			
		||||
      fs.rmSync(destination);
 | 
			
		||||
    }
 | 
			
		||||
  if (enabled) {
 | 
			
		||||
    appLauncher.enable().catch();
 | 
			
		||||
  } else {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
      appLauncher.enable().catch();
 | 
			
		||||
    } else {
 | 
			
		||||
      appLauncher.disable().catch();
 | 
			
		||||
    }
 | 
			
		||||
    appLauncher.disable().catch();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,5 +57,4 @@ export const requestWebPage = async (url: string) => {
 | 
			
		|||
    .then((response) => response.data);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export * from "./ps";
 | 
			
		||||
export * from "./download-source";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,41 +0,0 @@
 | 
			
		|||
import psList from "ps-list";
 | 
			
		||||
import path from "node:path";
 | 
			
		||||
import childProcess from "node:child_process";
 | 
			
		||||
import { promisify } from "node:util";
 | 
			
		||||
import { app } from "electron";
 | 
			
		||||
 | 
			
		||||
const TEN_MEGABYTES = 1000 * 1000 * 10;
 | 
			
		||||
const execFile = promisify(childProcess.execFile);
 | 
			
		||||
 | 
			
		||||
export const getProcesses = async () => {
 | 
			
		||||
  if (process.platform == "win32") {
 | 
			
		||||
    const binaryPath = app.isPackaged
 | 
			
		||||
      ? path.join(process.resourcesPath, "fastlist.exe")
 | 
			
		||||
      : path.join(
 | 
			
		||||
          __dirname,
 | 
			
		||||
          "..",
 | 
			
		||||
          "..",
 | 
			
		||||
          "node_modules",
 | 
			
		||||
          "ps-list",
 | 
			
		||||
          "vendor",
 | 
			
		||||
          "fastlist-0.3.0-x64.exe"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    const { stdout } = await execFile(binaryPath, {
 | 
			
		||||
      maxBuffer: TEN_MEGABYTES,
 | 
			
		||||
      windowsHide: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return stdout
 | 
			
		||||
      .trim()
 | 
			
		||||
      .split("\r\n")
 | 
			
		||||
      .map((line) => line.split("\t"))
 | 
			
		||||
      .map(([pid, ppid, name]) => ({
 | 
			
		||||
        pid: Number.parseInt(pid, 10),
 | 
			
		||||
        ppid: Number.parseInt(ppid, 10),
 | 
			
		||||
        name,
 | 
			
		||||
      }));
 | 
			
		||||
  } else {
 | 
			
		||||
    return psList();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ import {
 | 
			
		|||
  PauseDownloadPayload,
 | 
			
		||||
  LibtorrentStatus,
 | 
			
		||||
  LibtorrentPayload,
 | 
			
		||||
  ProcessPayload,
 | 
			
		||||
} from "./types";
 | 
			
		||||
 | 
			
		||||
export class RPCManager {
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +50,10 @@ export class RPCManager {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async getProccessList() {
 | 
			
		||||
    return (await this.rpc.get<string[] | null>("/process-list")).data || [];
 | 
			
		||||
  public static async getProcessList() {
 | 
			
		||||
    return (
 | 
			
		||||
      (await this.rpc.get<ProcessPayload[] | null>("/process-list")).data || []
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async getStatus() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,3 +31,8 @@ export interface LibtorrentPayload {
 | 
			
		|||
  status: LibtorrentStatus;
 | 
			
		||||
  gameId: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ProcessPayload {
 | 
			
		||||
  exe: string;
 | 
			
		||||
  pid: number;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,14 +19,13 @@ export const watchProcesses = async () => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  if (games.length === 0) return;
 | 
			
		||||
 | 
			
		||||
  const processes = await RPCManager.getProccessList();
 | 
			
		||||
  const processes = await RPCManager.getProcessList();
 | 
			
		||||
 | 
			
		||||
  for (const game of games) {
 | 
			
		||||
    const executablePath = game.executablePath!;
 | 
			
		||||
 | 
			
		||||
    const gameProcess = processes.find((runningProcess) => {
 | 
			
		||||
      return executablePath == runningProcess;
 | 
			
		||||
      return executablePath == runningProcess.exe;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (gameProcess) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,13 +44,13 @@ class Handler(BaseHTTPRequestHandler):
 | 
			
		|||
                self.end_headers()
 | 
			
		||||
                return
 | 
			
		||||
            
 | 
			
		||||
            process_path = list(set([proc.info["exe"] for proc in psutil.process_iter(['exe'])]))
 | 
			
		||||
            process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'username'])]
 | 
			
		||||
 | 
			
		||||
            self.send_response(200)
 | 
			
		||||
            self.send_header("Content-type", "application/json")
 | 
			
		||||
            self.end_headers()
 | 
			
		||||
 | 
			
		||||
            self.wfile.write(json.dumps(process_path).encode('utf-8'))
 | 
			
		||||
            self.wfile.write(json.dumps(process_list).encode('utf-8'))
 | 
			
		||||
    
 | 
			
		||||
    def do_POST(self):
 | 
			
		||||
        global downloader
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								yarn.lock
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								yarn.lock
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -6311,11 +6311,6 @@ proxy-from-env@^1.1.0:
 | 
			
		|||
  resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
 | 
			
		||||
  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
 | 
			
		||||
 | 
			
		||||
ps-list@^8.1.1:
 | 
			
		||||
  version "8.1.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/ps-list/-/ps-list-8.1.1.tgz"
 | 
			
		||||
  integrity sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==
 | 
			
		||||
 | 
			
		||||
psl@^1.1.33:
 | 
			
		||||
  version "1.9.0"
 | 
			
		||||
  resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
 | 
			
		||||
| 
						 | 
				
			
			@ -6999,6 +6994,11 @@ strtok3@^7.0.0:
 | 
			
		|||
    "@tokenizer/token" "^0.3.0"
 | 
			
		||||
    peek-readable "^5.0.0"
 | 
			
		||||
 | 
			
		||||
sudo-prompt@^9.2.1:
 | 
			
		||||
  version "9.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
 | 
			
		||||
  integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==
 | 
			
		||||
 | 
			
		||||
sumchecker@^3.0.1:
 | 
			
		||||
  version "3.0.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue