mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-03 00:33:49 +03:00
chore: merge with main
This commit is contained in:
commit
4636571a25
@ -19,7 +19,7 @@
|
|||||||
"start": "electron-vite preview",
|
"start": "electron-vite preview",
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev",
|
||||||
"build": "npm run typecheck && electron-vite build",
|
"build": "npm run typecheck && electron-vite build",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps && node ./postinstall.js",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "npm run build && electron-builder --dir",
|
||||||
"build:win": "npm run build && electron-builder --win",
|
"build:win": "npm run build && electron-builder --win",
|
||||||
"build:mac": "electron-vite build && electron-builder --mac",
|
"build:mac": "electron-vite build && electron-builder --mac",
|
||||||
@ -56,7 +56,7 @@
|
|||||||
"react-loading-skeleton": "^3.4.0",
|
"react-loading-skeleton": "^3.4.0",
|
||||||
"react-redux": "^9.1.1",
|
"react-redux": "^9.1.1",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"tasklist": "^5.0.0",
|
"tough-cookie": "^4.1.3",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"windows-1251": "^3.0.4",
|
"windows-1251": "^3.0.4",
|
||||||
"winston": "^3.13.0",
|
"winston": "^3.13.0",
|
||||||
|
12
postinstall.cjs
Normal file
12
postinstall.cjs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
if (!fs.existsSync("resources/dist")) {
|
||||||
|
fs.mkdirSync("resources/dist");
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(
|
||||||
|
"node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe",
|
||||||
|
"resources/dist/fastlist.exe"
|
||||||
|
);
|
||||||
|
}
|
@ -16,8 +16,8 @@
|
|||||||
"paused": "{{title}} (Pausado)",
|
"paused": "{{title}} (Pausado)",
|
||||||
"downloading": "{{title}} ({{percentage}} - Descargando…)",
|
"downloading": "{{title}} ({{percentage}} - Descargando…)",
|
||||||
"filter": "Filtrar biblioteca",
|
"filter": "Filtrar biblioteca",
|
||||||
"home": "Hogar",
|
"home": "Inicio",
|
||||||
"follow_us": "Síganos"
|
"follow_us": "Síguenos"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Buscar",
|
"search": "Buscar",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"downloads": "Descargas",
|
"downloads": "Descargas",
|
||||||
"search_results": "Resultados de búsqueda",
|
"search_results": "Resultados de búsqueda",
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
"home": "Início"
|
"home": "Inicio"
|
||||||
},
|
},
|
||||||
"bottom_panel": {
|
"bottom_panel": {
|
||||||
"no_downloads_in_progress": "Sin descargas en progreso",
|
"no_downloads_in_progress": "Sin descargas en progreso",
|
||||||
@ -70,7 +70,7 @@
|
|||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"play": "Jugar",
|
"play": "Jugar",
|
||||||
"not_played_yet": "Aún no has jugado a {{title}}",
|
"not_played_yet": "Aún no has jugado a {{title}}",
|
||||||
"close": "Cerca",
|
"close": "Cerrar",
|
||||||
"deleting": "Eliminando instalador…",
|
"deleting": "Eliminando instalador…",
|
||||||
"playing_now": "Jugando ahora",
|
"playing_now": "Jugando ahora",
|
||||||
"last_time_played": "Jugado por última vez {{period}}"
|
"last_time_played": "Jugado por última vez {{period}}"
|
||||||
@ -100,7 +100,7 @@
|
|||||||
"checking_files": "Verificando archivos…",
|
"checking_files": "Verificando archivos…",
|
||||||
"starting_download": "Iniciando descarga…",
|
"starting_download": "Iniciando descarga…",
|
||||||
"remove_from_list": "Eliminar",
|
"remove_from_list": "Eliminar",
|
||||||
"delete": "Quitar instalador",
|
"delete": "Eliminar instalador",
|
||||||
"delete_modal_description": "Esto eliminará todos los archivos de instalación de su computadora.",
|
"delete_modal_description": "Esto eliminará todos los archivos de instalación de su computadora.",
|
||||||
"delete_modal_title": "¿Está seguro?",
|
"delete_modal_title": "¿Está seguro?",
|
||||||
"deleting": "Eliminando instalador…",
|
"deleting": "Eliminando instalador…",
|
||||||
@ -112,8 +112,8 @@
|
|||||||
"notifications": "Notificaciones",
|
"notifications": "Notificaciones",
|
||||||
"enable_download_notifications": "Cuando se completa una descarga",
|
"enable_download_notifications": "Cuando se completa una descarga",
|
||||||
"enable_repack_list_notifications": "Cuando se añade un repack nuevo",
|
"enable_repack_list_notifications": "Cuando se añade un repack nuevo",
|
||||||
"telemetry": "Telemetria",
|
"telemetry": "Telemetría",
|
||||||
"telemetry_description": "Habilitar estadísticas de uso anónimas"
|
"telemetry_description": "Habilitar recopilación de datos de manera anónima"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Descarga completada",
|
"download_complete": "Descarga completada",
|
||||||
@ -132,7 +132,7 @@
|
|||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
"title": "Programas no instalados",
|
"title": "Programas no instalados",
|
||||||
"description": "Los ejecutables de Wine o Lutris no se encontraron en su sistema",
|
"description": "Los ejecutables de Wine o Lutris no se encontraron en su sistema",
|
||||||
"instructions": "Comprueba la forma correcta de instalar cualquiera de ellos en tu distro Linux para que el juego pueda ejecutarse con normalidad"
|
"instructions": "Comprueba como instalar de forma correcta uno de los dos en tu distro de Linux para ejecutar el juego con normalidad"
|
||||||
},
|
},
|
||||||
"catalogue": {
|
"catalogue": {
|
||||||
"next_page": "Siguiente página",
|
"next_page": "Siguiente página",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"downloads": "Téléchargements",
|
"downloads": "Téléchargements",
|
||||||
"search_results": "Résultats de la recherche",
|
"search_results": "Résultats de la recherche",
|
||||||
"settings": "Paramètres",
|
"settings": "Paramètres",
|
||||||
"home": "Maison"
|
"home": "Accueil"
|
||||||
},
|
},
|
||||||
"bottom_panel": {
|
"bottom_panel": {
|
||||||
"no_downloads_in_progress": "Aucun téléchargement en cours",
|
"no_downloads_in_progress": "Aucun téléchargement en cours",
|
||||||
|
@ -21,6 +21,8 @@ const getRandomGame = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,33 @@
|
|||||||
import psList from "ps-list";
|
import psList from "ps-list";
|
||||||
import { tasklist } from "tasklist";
|
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 () => {
|
export const getProcesses = async () => {
|
||||||
if (process.platform === "win32") {
|
if (process.platform == "win32") {
|
||||||
return tasklist().then((tasks) =>
|
const binaryPath = app.isPackaged
|
||||||
tasks.map((task) => ({ ...task, name: task.imageName }))
|
? path.join(process.resourcesPath, "dist", "fastlist.exe")
|
||||||
);
|
: path.join(__dirname, "..", "..", "resources", "dist", "fastlist.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();
|
return psList();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -48,7 +48,7 @@ Promise.all([writePipe.createPipe(), readPipe.createPipe()]).then(async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readPipe.socket.on("data", (data) => {
|
readPipe.socket?.on("data", (data) => {
|
||||||
TorrentClient.onSocketData(data);
|
TorrentClient.onSocketData(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import { IsNull, Not } from "typeorm";
|
import { IsNull, Not } from "typeorm";
|
||||||
|
|
||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
import { getProcesses } from "@main/helpers";
|
import { getProcesses } from "@main/helpers";
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
@ -9,27 +8,34 @@ import { WindowManager } from "./window-manager";
|
|||||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
export const startProcessWatcher = async () => {
|
export const startProcessWatcher = async () => {
|
||||||
const sleepTime = 100;
|
const sleepTime = 300;
|
||||||
const gamesPlaytime = new Map<number, number>();
|
const gamesPlaytime = new Map<number, number>();
|
||||||
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
|
await sleep(sleepTime);
|
||||||
|
|
||||||
const games = await gameRepository.find({
|
const games = await gameRepository.find({
|
||||||
where: {
|
where: {
|
||||||
executablePath: Not(IsNull()),
|
executablePath: Not(IsNull()),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (games.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const processes = await getProcesses();
|
const processes = await getProcesses();
|
||||||
|
|
||||||
for (const game of games) {
|
for (const game of games) {
|
||||||
const gameProcess = processes.find((runningProcess) => {
|
const executablePath = game.executablePath!;
|
||||||
const basename = path.win32.basename(game.executablePath);
|
const basename = path.win32.basename(executablePath);
|
||||||
const basenameWithoutExtension = path.win32.basename(
|
const basenameWithoutExtension = path.win32.basename(
|
||||||
game.executablePath,
|
executablePath,
|
||||||
path.extname(game.executablePath)
|
path.extname(executablePath)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const gameProcess = processes.find((runningProcess) => {
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
return runningProcess.name === basename;
|
return runningProcess.name === basename;
|
||||||
}
|
}
|
||||||
@ -41,7 +47,7 @@ export const startProcessWatcher = async () => {
|
|||||||
|
|
||||||
if (gameProcess) {
|
if (gameProcess) {
|
||||||
if (gamesPlaytime.has(game.id)) {
|
if (gamesPlaytime.has(game.id)) {
|
||||||
const zero = gamesPlaytime.get(game.id);
|
const zero = gamesPlaytime.get(game.id) ?? 0;
|
||||||
const delta = performance.now() - zero;
|
const delta = performance.now() - zero;
|
||||||
|
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
@ -55,26 +61,15 @@ export const startProcessWatcher = async () => {
|
|||||||
gameRepository.update(game.id, {
|
gameRepository.update(game.id, {
|
||||||
lastTimePlayed: new Date().toUTCString(),
|
lastTimePlayed: new Date().toUTCString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
gamesPlaytime.set(game.id, performance.now());
|
|
||||||
await sleep(sleepTime);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gamesPlaytime.set(game.id, performance.now());
|
gamesPlaytime.set(game.id, performance.now());
|
||||||
|
} else if (gamesPlaytime.has(game.id)) {
|
||||||
await sleep(sleepTime);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gamesPlaytime.has(game.id)) {
|
|
||||||
gamesPlaytime.delete(game.id);
|
gamesPlaytime.delete(game.id);
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
WindowManager.mainWindow.webContents.send("on-game-close", game.id);
|
WindowManager.mainWindow.webContents.send("on-game-close", game.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await sleep(sleepTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
|
import type { Repack } from "@main/entity";
|
||||||
import { repackRepository } from "@main/repository";
|
import { repackRepository } from "@main/repository";
|
||||||
|
|
||||||
import type { GameRepack } from "@types";
|
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
|
||||||
|
|
||||||
export type GameRepackInput = Omit<
|
export const savePage = async (repacks: QueryDeepPartialEntity<Repack>[]) =>
|
||||||
GameRepack,
|
|
||||||
"id" | "repackerFriendlyName" | "createdAt" | "updatedAt"
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const savePage = async (repacks: GameRepackInput[]) =>
|
|
||||||
Promise.all(
|
Promise.all(
|
||||||
repacks.map((repack) => repackRepository.insert(repack).catch(() => {}))
|
repacks.map((repack) => repackRepository.insert(repack).catch(() => {}))
|
||||||
);
|
);
|
||||||
|
@ -31,11 +31,12 @@ const formatXatabDownloadSize = (str: string) =>
|
|||||||
const getXatabRepack = async (url: string) => {
|
const getXatabRepack = async (url: string) => {
|
||||||
const data = await requestWebPage(url);
|
const data = await requestWebPage(url);
|
||||||
const { window } = new JSDOM(data);
|
const { window } = new JSDOM(data);
|
||||||
|
const { document } = window;
|
||||||
|
|
||||||
const $uploadDate = window.document.querySelector(".entry__date");
|
const $uploadDate = document.querySelector(".entry__date");
|
||||||
const $size = window.document.querySelector(".entry__info-size");
|
const $size = document.querySelector(".entry__info-size");
|
||||||
|
|
||||||
const $downloadButton = window.document.querySelector(
|
const $downloadButton = document.querySelector(
|
||||||
".download-torrent"
|
".download-torrent"
|
||||||
) as HTMLAnchorElement;
|
) as HTMLAnchorElement;
|
||||||
|
|
||||||
@ -74,8 +75,10 @@ export const getNewRepacksFromXatab = async (
|
|||||||
...repack,
|
...repack,
|
||||||
page,
|
page,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: unknown) {
|
||||||
logger.error(err.message, { method: "getNewRepacksFromXatab" });
|
logger.error((err as Error).message, {
|
||||||
|
method: "getNewRepacksFromXatab",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ export const requestSteam250 = async (path: string) => {
|
|||||||
const steamGameUrl = ($title as HTMLAnchorElement).href;
|
const steamGameUrl = ($title as HTMLAnchorElement).href;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: $title.textContent,
|
title: $title.textContent!,
|
||||||
objectID: steamGameUrl.split("/").pop(),
|
objectID: steamGameUrl.split("/").pop()!,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,7 @@ export class TorrentClient {
|
|||||||
const commonArgs = [BITTORRENT_PORT, writePipePath, readPipePath];
|
const commonArgs = [BITTORRENT_PORT, writePipePath, readPipePath];
|
||||||
|
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
const binaryName = binaryNameByPlatform[process.platform];
|
const binaryName = binaryNameByPlatform[process.platform]!;
|
||||||
const binaryPath = path.join(
|
const binaryPath = path.join(
|
||||||
process.resourcesPath,
|
process.resourcesPath,
|
||||||
"hydra-download-manager",
|
"hydra-download-manager",
|
||||||
@ -133,7 +133,7 @@ export class TorrentClient {
|
|||||||
relations: { repack: true },
|
relations: { repack: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (game.progress === 1) {
|
if (game?.progress === 1) {
|
||||||
const userPreferences = await userPreferencesRepository.findOne({
|
const userPreferences = await userPreferencesRepository.findOne({
|
||||||
where: { id: 1 },
|
where: { id: 1 },
|
||||||
});
|
});
|
||||||
@ -153,7 +153,7 @@ export class TorrentClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow && game) {
|
||||||
const progress = this.getGameProgress(game);
|
const progress = this.getGameProgress(game);
|
||||||
WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress);
|
WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { ShopDetails } from "@types";
|
|||||||
import { getSteamLanguage, steamUrlBuilder } from "@renderer/helpers";
|
import { getSteamLanguage, steamUrlBuilder } from "@renderer/helpers";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const FEATURED_GAME_ID = "377160";
|
const FEATURED_GAME_ID = "253230";
|
||||||
|
|
||||||
export function Hero() {
|
export function Hero() {
|
||||||
const [featuredGameDetails, setFeaturedGameDetails] =
|
const [featuredGameDetails, setFeaturedGameDetails] =
|
||||||
@ -36,7 +36,7 @@ export function Hero() {
|
|||||||
>
|
>
|
||||||
<div className={styles.backdrop}>
|
<div className={styles.backdrop}>
|
||||||
<AsyncImage
|
<AsyncImage
|
||||||
src="https://cdn2.steamgriddb.com/hero/e7a7ba56b1be30e178cd52820e063396.png"
|
src="https://cdn2.steamgriddb.com/hero/a6115ed32394915aac1e5502382eaaea.jpg"
|
||||||
alt={featuredGameDetails?.name}
|
alt={featuredGameDetails?.name}
|
||||||
className={styles.heroMedia}
|
className={styles.heroMedia}
|
||||||
/>
|
/>
|
||||||
|
@ -38,11 +38,6 @@ export interface SteamAppDetails {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ShopDetails = SteamAppDetails & {
|
|
||||||
objectID: string;
|
|
||||||
repacks: GameRepack[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface GameRepack {
|
export interface GameRepack {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
@ -55,6 +50,11 @@ export interface GameRepack {
|
|||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ShopDetails = SteamAppDetails & {
|
||||||
|
objectID: string;
|
||||||
|
repacks: GameRepack[];
|
||||||
|
};
|
||||||
|
|
||||||
export interface TorrentFile {
|
export interface TorrentFile {
|
||||||
path: string;
|
path: string;
|
||||||
length: number;
|
length: number;
|
||||||
|
Loading…
Reference in New Issue
Block a user