mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-03 00:33:49 +03:00
ci: testing pipeline
This commit is contained in:
parent
7e3f53a0d0
commit
ae6484b7b1
78
.github/workflows/build.yml
vendored
78
.github/workflows/build.yml
vendored
@ -8,21 +8,9 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os: [windows-latest, ubuntu-latest]
|
||||||
[
|
|
||||||
{
|
|
||||||
name: windows-latest,
|
|
||||||
build_path: out/Hydra-win32-x64,
|
|
||||||
artifact: Hydra-win32-x64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ubuntu-latest,
|
|
||||||
build_path: out/Hydra-linux-x64,
|
|
||||||
artifact: Hydra-linux-x64,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os.name }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
@ -47,10 +35,10 @@ jobs:
|
|||||||
- name: Build with cx_Freeze
|
- name: Build with cx_Freeze
|
||||||
run: python torrent-client/setup.py build
|
run: python torrent-client/setup.py build
|
||||||
|
|
||||||
- name: Publish
|
- name: Build Linux
|
||||||
run: yarn run publish
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: yarn build:linux
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }}
|
MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
@ -58,15 +46,55 @@ jobs:
|
|||||||
MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
|
MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
|
||||||
MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
|
MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
|
||||||
|
|
||||||
- name: VirusTotal Scan
|
- name: Build Windows
|
||||||
uses: crazy-max/ghaction-virustotal@v4
|
if: matrix.os == 'windows-latest'
|
||||||
with:
|
run: yarn build:win
|
||||||
vt_api_key: ${{ secrets.VT_API_KEY }}
|
env:
|
||||||
files: |
|
MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }}
|
||||||
./hydra-download-manager/hydra-download-manager.exe
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
|
MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
|
||||||
|
MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
|
||||||
|
|
||||||
- name: Create artifact
|
- name: Create artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os.artifact }}
|
name: Build-${{ matrix.os }}
|
||||||
path: ${{ matrix.os.build_path }}
|
path: |
|
||||||
|
dist/*.exe
|
||||||
|
dist/*.zip
|
||||||
|
dist/*.dmg
|
||||||
|
dist/*.AppImage
|
||||||
|
dist/*.snap
|
||||||
|
dist/*.deb
|
||||||
|
dist/*.rpm
|
||||||
|
dist/*.tar.gz
|
||||||
|
dist/*.yml
|
||||||
|
dist/*.blockmap
|
||||||
|
|
||||||
|
- name: VirusTotal Scan
|
||||||
|
uses: crazy-max/ghaction-virustotal@v4
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
with:
|
||||||
|
vt_api_key: ${{ secrets.VT_API_KEY }}
|
||||||
|
files: |
|
||||||
|
./dist/*.exe
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
files: |
|
||||||
|
dist/*.exe
|
||||||
|
dist/*.zip
|
||||||
|
dist/*.dmg
|
||||||
|
dist/*.AppImage
|
||||||
|
dist/*.snap
|
||||||
|
dist/*.deb
|
||||||
|
dist/*.rpm
|
||||||
|
dist/*.tar.gz
|
||||||
|
dist/*.yml
|
||||||
|
dist/*.blockmap
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -31,6 +31,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
alias: {
|
alias: {
|
||||||
"@main": resolve("src/main"),
|
"@main": resolve("src/main"),
|
||||||
"@locales": resolve("src/locales"),
|
"@locales": resolve("src/locales"),
|
||||||
|
"@resources": resolve("resources"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"build": "npm run typecheck && electron-vite build",
|
"build": "npm run typecheck && electron-vite build",
|
||||||
"postinstall": "electron-builder install-app-deps && node ./postinstall.cjs",
|
"postinstall": "electron-builder install-app-deps && node ./postinstall.cjs",
|
||||||
"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": "electron-builder --win",
|
||||||
"build:mac": "electron-vite build && electron-builder --mac",
|
"build:mac": "electron-vite build && electron-builder --mac",
|
||||||
"build:linux": "electron-vite build && electron-builder --linux"
|
"build:linux": "electron-vite build && electron-builder --linux"
|
||||||
},
|
},
|
||||||
@ -70,6 +70,7 @@
|
|||||||
"@types/jsdom": "^21.1.6",
|
"@types/jsdom": "^21.1.6",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
|
"@types/parse-torrent": "^5.8.7",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
"@vanilla-extract/vite-plugin": "^4.0.7",
|
"@vanilla-extract/vite-plugin": "^4.0.7",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
|
import { getProcesses } from "@main/helpers";
|
||||||
|
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { getProcesses } from "@main/helpers";
|
|
||||||
|
|
||||||
const closeGame = async (
|
const closeGame = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@ -12,13 +12,17 @@ const closeGame = async (
|
|||||||
const processes = await getProcesses();
|
const processes = await getProcesses();
|
||||||
const game = await gameRepository.findOne({ where: { id: gameId } });
|
const game = await gameRepository.findOne({ where: { id: gameId } });
|
||||||
|
|
||||||
const gameProcess = processes.find((runningProcess) => {
|
if (!game) return false;
|
||||||
const basename = path.win32.basename(game.executablePath);
|
|
||||||
|
const executablePath = 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;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
getNewGOGGames,
|
getNewGOGGames,
|
||||||
getNewRepacksFromCPG,
|
getNewRepacksFromCPG,
|
||||||
getNewRepacksFromUser,
|
getNewRepacksFromUser,
|
||||||
// getNewRepacksFromXatab,
|
getNewRepacksFromXatab,
|
||||||
getNewRepacksFromOnlineFix,
|
getNewRepacksFromOnlineFix,
|
||||||
readPipe,
|
readPipe,
|
||||||
startProcessWatcher,
|
startProcessWatcher,
|
||||||
@ -22,13 +22,6 @@ import { Repack } from "./entity";
|
|||||||
import { Notification } from "electron";
|
import { Notification } from "electron";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import creatWorker from "./workers/test?nodeWorker";
|
|
||||||
|
|
||||||
creatWorker({ workerData: "worker" })
|
|
||||||
.on("message", (message) => {
|
|
||||||
console.log(`\nMessage from worker: ${message}`);
|
|
||||||
})
|
|
||||||
.postMessage("");
|
|
||||||
|
|
||||||
startProcessWatcher();
|
startProcessWatcher();
|
||||||
|
|
||||||
@ -80,9 +73,9 @@ const checkForNewRepacks = async () => {
|
|||||||
getNewGOGGames(
|
getNewGOGGames(
|
||||||
existingRepacks.filter((repack) => repack.repacker === "GOG")
|
existingRepacks.filter((repack) => repack.repacker === "GOG")
|
||||||
),
|
),
|
||||||
// getNewRepacksFromXatab(
|
getNewRepacksFromXatab(
|
||||||
// existingRepacks.filter((repack) => repack.repacker === "Xatab")
|
existingRepacks.filter((repack) => repack.repacker === "Xatab")
|
||||||
// ),
|
),
|
||||||
getNewRepacksFromCPG(
|
getNewRepacksFromCPG(
|
||||||
existingRepacks.filter((repack) => repack.repacker === "CPG")
|
existingRepacks.filter((repack) => repack.repacker === "CPG")
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,6 @@ import { formatUploadDate } from "@main/helpers";
|
|||||||
|
|
||||||
import { Repack } from "@main/entity";
|
import { Repack } from "@main/entity";
|
||||||
import { requestWebPage, savePage } from "./helpers";
|
import { requestWebPage, savePage } from "./helpers";
|
||||||
import type { GameRepackInput } from "./helpers";
|
|
||||||
|
|
||||||
export const request1337x = async (path: string) =>
|
export const request1337x = async (path: string) =>
|
||||||
requestWebPage(`https://1337xx.to${path}`);
|
requestWebPage(`https://1337xx.to${path}`);
|
||||||
@ -68,7 +67,7 @@ export const extractTorrentsFromDocument = async (
|
|||||||
user: string,
|
user: string,
|
||||||
document: Document,
|
document: Document,
|
||||||
existingRepacks: Repack[] = []
|
existingRepacks: Repack[] = []
|
||||||
): Promise<GameRepackInput[]> => {
|
) => {
|
||||||
const $trs = Array.from(document.querySelectorAll("tbody tr"));
|
const $trs = Array.from(document.querySelectorAll("tbody tr"));
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
@ -108,7 +107,7 @@ export const getNewRepacksFromUser = async (
|
|||||||
user: string,
|
user: string,
|
||||||
existingRepacks: Repack[],
|
existingRepacks: Repack[],
|
||||||
page = 1
|
page = 1
|
||||||
): Promise<Repack[]> => {
|
) => {
|
||||||
const response = await request1337x(`/user/${user}/${page}`);
|
const response = await request1337x(`/user/${user}/${page}`);
|
||||||
const { window } = new JSDOM(response);
|
const { window } = new JSDOM(response);
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { JSDOM } from "jsdom";
|
|||||||
import { Repack } from "@main/entity";
|
import { Repack } from "@main/entity";
|
||||||
|
|
||||||
import { requestWebPage, savePage } from "./helpers";
|
import { requestWebPage, savePage } from "./helpers";
|
||||||
import type { GameRepackInput } from "./helpers";
|
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
|
|
||||||
export const getNewRepacksFromCPG = async (
|
export const getNewRepacksFromCPG = async (
|
||||||
@ -14,7 +13,7 @@ export const getNewRepacksFromCPG = async (
|
|||||||
|
|
||||||
const { window } = new JSDOM(data);
|
const { window } = new JSDOM(data);
|
||||||
|
|
||||||
const repacks: GameRepackInput[] = [];
|
const repacks = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Array.from(window.document.querySelectorAll(".post")).forEach(($post) => {
|
Array.from(window.document.querySelectorAll(".post")).forEach(($post) => {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { JSDOM, VirtualConsole } from "jsdom";
|
import { JSDOM, VirtualConsole } from "jsdom";
|
||||||
import { GameRepackInput, requestWebPage, savePage } from "./helpers";
|
import { requestWebPage, savePage } from "./helpers";
|
||||||
import { Repack } from "@main/entity";
|
import { Repack } from "@main/entity";
|
||||||
import { logger } from "../logger";
|
|
||||||
|
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
|
||||||
|
|
||||||
const virtualConsole = new VirtualConsole();
|
const virtualConsole = new VirtualConsole();
|
||||||
|
|
||||||
@ -36,7 +37,6 @@ const getGOGGame = async (url: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
|
export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
|
||||||
try {
|
|
||||||
const data = await requestWebPage(
|
const data = await requestWebPage(
|
||||||
"https://freegogpcgames.com/a-z-games-list/"
|
"https://freegogpcgames.com/a-z-games-list/"
|
||||||
);
|
);
|
||||||
@ -46,7 +46,7 @@ export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
|
|||||||
const $uls = Array.from(window.document.querySelectorAll(".az-columns"));
|
const $uls = Array.from(window.document.querySelectorAll(".az-columns"));
|
||||||
|
|
||||||
for (const $ul of $uls) {
|
for (const $ul of $uls) {
|
||||||
const repacks: GameRepackInput[] = [];
|
const repacks: QueryDeepPartialEntity<Repack>[] = [];
|
||||||
const $lis = Array.from($ul.querySelectorAll("li"));
|
const $lis = Array.from($ul.querySelectorAll("li"));
|
||||||
|
|
||||||
for (const $li of $lis) {
|
for (const $li of $lis) {
|
||||||
@ -60,19 +60,12 @@ export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!gameExists) {
|
if (!gameExists) {
|
||||||
try {
|
|
||||||
const game = await getGOGGame(href);
|
const game = await getGOGGame(href);
|
||||||
|
|
||||||
repacks.push({ ...game, title });
|
repacks.push({ ...game, title });
|
||||||
} catch (err) {
|
|
||||||
logger.error(err.message, { method: "getGOGGame", url: href });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repacks.length) await savePage(repacks);
|
if (repacks.length) await savePage(repacks);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
logger.error(err.message, { method: "getNewGOGGames" });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Repack } from "@main/entity";
|
import { Repack } from "@main/entity";
|
||||||
import { savePage } from "./helpers";
|
import { savePage } from "./helpers";
|
||||||
import type { GameRepackInput } from "./helpers";
|
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import parseTorrent, {
|
import parseTorrent, {
|
||||||
toMagnetURI,
|
toMagnetURI,
|
||||||
@ -85,7 +84,7 @@ export const getNewRepacksFromOnlineFix = async (
|
|||||||
});
|
});
|
||||||
const document = new JSDOM(home.body).window.document;
|
const document = new JSDOM(home.body).window.document;
|
||||||
|
|
||||||
const repacks: GameRepackInput[] = [];
|
const repacks = [];
|
||||||
const articles = Array.from(document.querySelectorAll(".news"));
|
const articles = Array.from(document.querySelectorAll(".news"));
|
||||||
const totalPages = Number(
|
const totalPages = Number(
|
||||||
document.querySelector("nav > a:nth-child(13)")?.textContent
|
document.querySelector("nav > a:nth-child(13)")?.textContent
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { JSDOM } from "jsdom";
|
import { JSDOM } from "jsdom";
|
||||||
|
|
||||||
import parseTorrent, { toMagnetURI } from "parse-torrent";
|
|
||||||
|
|
||||||
import { Repack } from "@main/entity";
|
import { Repack } from "@main/entity";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { requestWebPage, savePage } from "./helpers";
|
import { requestWebPage, savePage } from "./helpers";
|
||||||
import type { GameRepackInput } from "./helpers";
|
|
||||||
|
|
||||||
const getTorrentBuffer = (url: string) =>
|
import createWorker from "@main/workers/torrent-parser.worker?nodeWorker";
|
||||||
fetch(url, { method: "GET" }).then((response) =>
|
import { toMagnetURI } from "parse-torrent";
|
||||||
response.arrayBuffer().then((buffer) => Buffer.from(buffer))
|
import type { Instance } from "parse-torrent";
|
||||||
);
|
|
||||||
|
const worker = createWorker({});
|
||||||
|
|
||||||
const formatXatabDate = (str: string) => {
|
const formatXatabDate = (str: string) => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
@ -28,7 +26,9 @@ const formatXatabDate = (str: string) => {
|
|||||||
const formatXatabDownloadSize = (str: string) =>
|
const formatXatabDownloadSize = (str: string) =>
|
||||||
str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB");
|
str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB");
|
||||||
|
|
||||||
const getXatabRepack = async (url: string) => {
|
const getXatabRepack = (url: string) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
(async () => {
|
||||||
const data = await requestWebPage(url);
|
const data = await requestWebPage(url);
|
||||||
const { window } = new JSDOM(data);
|
const { window } = new JSDOM(data);
|
||||||
const { document } = window;
|
const { document } = window;
|
||||||
@ -42,15 +42,20 @@ const getXatabRepack = async (url: string) => {
|
|||||||
|
|
||||||
if (!$downloadButton) throw new Error("Download button not found");
|
if (!$downloadButton) throw new Error("Download button not found");
|
||||||
|
|
||||||
const torrentBuffer = await getTorrentBuffer($downloadButton.href);
|
const onMessage = (torrent: Instance) => {
|
||||||
|
resolve({
|
||||||
return {
|
|
||||||
fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(),
|
fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(),
|
||||||
magnet: toMagnetURI({
|
magnet: toMagnetURI(torrent),
|
||||||
infoHash: parseTorrent(torrentBuffer).infoHash,
|
|
||||||
}),
|
|
||||||
uploadDate: formatXatabDate($uploadDate.textContent),
|
uploadDate: formatXatabDate($uploadDate.textContent),
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.removeListener("message", onMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
worker.on("message", onMessage);
|
||||||
|
worker.postMessage($downloadButton.href);
|
||||||
|
})();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNewRepacksFromXatab = async (
|
export const getNewRepacksFromXatab = async (
|
||||||
@ -61,7 +66,7 @@ export const getNewRepacksFromXatab = async (
|
|||||||
|
|
||||||
const { window } = new JSDOM(data);
|
const { window } = new JSDOM(data);
|
||||||
|
|
||||||
const repacks: GameRepackInput[] = [];
|
const repacks = [];
|
||||||
|
|
||||||
for (const $a of Array.from(
|
for (const $a of Array.from(
|
||||||
window.document.querySelectorAll(".entry__title a")
|
window.document.querySelectorAll(".entry__title a")
|
||||||
@ -84,7 +89,6 @@ export const getNewRepacksFromXatab = async (
|
|||||||
|
|
||||||
const newRepacks = repacks.filter(
|
const newRepacks = repacks.filter(
|
||||||
(repack) =>
|
(repack) =>
|
||||||
repack.uploadDate &&
|
|
||||||
!existingRepacks.some(
|
!existingRepacks.some(
|
||||||
(existingRepack) => existingRepack.title === repack.title
|
(existingRepack) => existingRepack.title === repack.title
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ import { BrowserWindow, Menu, Tray, app } from "electron";
|
|||||||
import { is } from "@electron-toolkit/utils";
|
import { is } from "@electron-toolkit/utils";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import icon from "../../../resources/icon.png?asset";
|
import icon from "@resources/icon.png?asset";
|
||||||
import trayIcon from "../../../resources/tray-icon.png?asset";
|
import trayIcon from "@resources/tray-icon.png?asset";
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
public static mainWindow: Electron.BrowserWindow | null = null;
|
public static mainWindow: Electron.BrowserWindow | null = null;
|
||||||
|
17
src/main/workers/torrent-parser.worker.ts
Normal file
17
src/main/workers/torrent-parser.worker.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { parentPort } from "worker_threads";
|
||||||
|
import parseTorrent from "parse-torrent";
|
||||||
|
|
||||||
|
const port = parentPort;
|
||||||
|
if (!port) throw new Error("IllegalState");
|
||||||
|
|
||||||
|
const getTorrentBuffer = (url: string) =>
|
||||||
|
fetch(url, { method: "GET" }).then((response) =>
|
||||||
|
response.arrayBuffer().then((buffer) => Buffer.from(buffer))
|
||||||
|
);
|
||||||
|
|
||||||
|
port.on("message", async (url: string) => {
|
||||||
|
const buffer = await getTorrentBuffer(url);
|
||||||
|
const torrent = await parseTorrent(buffer);
|
||||||
|
|
||||||
|
port.postMessage(torrent);
|
||||||
|
});
|
3
src/preload/index.d.ts
vendored
3
src/preload/index.d.ts
vendored
@ -71,7 +71,8 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
openGame: (gameId: number, executablePath: string) =>
|
openGame: (gameId: number, executablePath: string) =>
|
||||||
ipcRenderer.invoke("openGame", gameId, executablePath),
|
ipcRenderer.invoke("openGame", gameId, executablePath),
|
||||||
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
|
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
|
||||||
removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId),
|
removeGameFromLibrary: (gameId: number) =>
|
||||||
|
ipcRenderer.invoke("removeGameFromLibrary", gameId),
|
||||||
deleteGameFolder: (gameId: number) =>
|
deleteGameFolder: (gameId: number) =>
|
||||||
ipcRenderer.invoke("deleteGameFolder", gameId),
|
ipcRenderer.invoke("deleteGameFolder", gameId),
|
||||||
getGameByObjectID: (objectID: string) =>
|
getGameByObjectID: (objectID: string) =>
|
||||||
|
@ -80,7 +80,8 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
openGame: (gameId: number, executablePath: string) =>
|
openGame: (gameId: number, executablePath: string) =>
|
||||||
ipcRenderer.invoke("openGame", gameId, executablePath),
|
ipcRenderer.invoke("openGame", gameId, executablePath),
|
||||||
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
|
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
|
||||||
removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId),
|
removeGameFromLibrary: (gameId: number) =>
|
||||||
|
ipcRenderer.invoke("removeGameFromLibrary", gameId),
|
||||||
deleteGameFolder: (gameId: number) =>
|
deleteGameFolder: (gameId: number) =>
|
||||||
ipcRenderer.invoke("deleteGameFolder", gameId),
|
ipcRenderer.invoke("deleteGameFolder", gameId),
|
||||||
getGameByObjectID: (objectID: string) =>
|
getGameByObjectID: (objectID: string) =>
|
||||||
|
@ -9,9 +9,8 @@
|
|||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;"
|
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="background-color: #1c1c1">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<h1>hello</h1>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -22,7 +22,7 @@ export function BottomPanel() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const status = useMemo(() => {
|
const status = useMemo(() => {
|
||||||
if (isDownloading) {
|
if (isDownloading && game) {
|
||||||
if (game.status === "downloading_metadata")
|
if (game.status === "downloading_metadata")
|
||||||
return t("downloading_metadata", { title: game.title });
|
return t("downloading_metadata", { title: game.title });
|
||||||
|
|
||||||
|
@ -62,13 +62,15 @@ export function Modal({
|
|||||||
const onMouseDown = (e: MouseEvent) => {
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
if (!isTopMostModal()) return;
|
if (!isTopMostModal()) return;
|
||||||
|
|
||||||
const clickedOutsideContent = !modalContentRef.current.contains(
|
if (modalContentRef.current) {
|
||||||
|
const clickedOutsideContent = modalContentRef.current.contains(
|
||||||
e.target as Node
|
e.target as Node
|
||||||
);
|
);
|
||||||
|
|
||||||
if (clickedOutsideContent) {
|
if (clickedOutsideContent) {
|
||||||
handleCloseClick();
|
handleCloseClick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("mousedown", onMouseDown);
|
window.addEventListener("mousedown", onMouseDown);
|
||||||
|
2
src/renderer/src/declaration.d.ts
vendored
2
src/renderer/src/declaration.d.ts
vendored
@ -62,7 +62,7 @@ declare global {
|
|||||||
openGameInstaller: (gameId: number) => Promise<boolean>;
|
openGameInstaller: (gameId: number) => Promise<boolean>;
|
||||||
openGame: (gameId: number, executablePath: string) => Promise<void>;
|
openGame: (gameId: number, executablePath: string) => Promise<void>;
|
||||||
closeGame: (gameId: number) => Promise<boolean>;
|
closeGame: (gameId: number) => Promise<boolean>;
|
||||||
removeGame: (gameId: number) => Promise<void>;
|
removeGameFromLibrary: (gameId: number) => Promise<void>;
|
||||||
deleteGameFolder: (gameId: number) => Promise<unknown>;
|
deleteGameFolder: (gameId: number) => Promise<unknown>;
|
||||||
getGameByObjectID: (objectID: string) => Promise<Game | null>;
|
getGameByObjectID: (objectID: string) => Promise<Game | null>;
|
||||||
onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
||||||
|
@ -14,7 +14,10 @@ export const userPreferencesSlice = createSlice({
|
|||||||
name: "userPreferences",
|
name: "userPreferences",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setUserPreferences: (state, action: PayloadAction<UserPreferences>) => {
|
setUserPreferences: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<UserPreferences | null>
|
||||||
|
) => {
|
||||||
state.value = action.payload;
|
state.value = action.payload;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -58,17 +58,17 @@ export function useDownload() {
|
|||||||
deleteGame(gameId);
|
deleteGame(gameId);
|
||||||
});
|
});
|
||||||
|
|
||||||
const removeGame = (gameId: number) =>
|
const removeGameFromLibrary = (gameId: number) =>
|
||||||
window.electron.removeGame(gameId).then(() => {
|
window.electron.removeGameFromLibrary(gameId).then(() => {
|
||||||
updateLibrary();
|
updateLibrary();
|
||||||
});
|
});
|
||||||
|
|
||||||
const isVerifying = ["downloading_metadata", "checking_files"].includes(
|
const isVerifying = ["downloading_metadata", "checking_files"].includes(
|
||||||
lastPacket?.game.status
|
lastPacket?.game.status ?? ""
|
||||||
);
|
);
|
||||||
|
|
||||||
const getETA = () => {
|
const getETA = () => {
|
||||||
if (isVerifying || !isFinite(lastPacket?.timeRemaining)) {
|
if (isVerifying || !isFinite(lastPacket?.timeRemaining ?? 0)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ export function useDownload() {
|
|||||||
pauseDownload,
|
pauseDownload,
|
||||||
resumeDownload,
|
resumeDownload,
|
||||||
cancelDownload,
|
cancelDownload,
|
||||||
removeGame,
|
removeGameFromLibrary,
|
||||||
deleteGame,
|
deleteGame,
|
||||||
isGameDeleting,
|
isGameDeleting,
|
||||||
clearDownload: () => dispatch(clearDownload()),
|
clearDownload: () => dispatch(clearDownload()),
|
||||||
|
@ -33,6 +33,7 @@ export function Downloads() {
|
|||||||
numSeeds,
|
numSeeds,
|
||||||
pauseDownload,
|
pauseDownload,
|
||||||
resumeDownload,
|
resumeDownload,
|
||||||
|
removeGameFromLibrary,
|
||||||
cancelDownload,
|
cancelDownload,
|
||||||
deleteGame,
|
deleteGame,
|
||||||
isGameDeleting,
|
isGameDeleting,
|
||||||
@ -52,11 +53,6 @@ export function Downloads() {
|
|||||||
updateLibrary();
|
updateLibrary();
|
||||||
});
|
});
|
||||||
|
|
||||||
const removeGame = (gameId: number) =>
|
|
||||||
window.electron.removeGame(gameId).then(() => {
|
|
||||||
updateLibrary();
|
|
||||||
});
|
|
||||||
|
|
||||||
const getFinalDownloadSize = (game: Game) => {
|
const getFinalDownloadSize = (game: Game) => {
|
||||||
const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;
|
const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;
|
||||||
|
|
||||||
@ -194,7 +190,7 @@ export function Downloads() {
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => removeGame(game.id)}
|
onClick={() => removeGameFromLibrary(game.id)}
|
||||||
theme="outline"
|
theme="outline"
|
||||||
disabled={deleting}
|
disabled={deleting}
|
||||||
>
|
>
|
||||||
|
@ -32,7 +32,7 @@ export function HeroPanelActions({
|
|||||||
resumeDownload,
|
resumeDownload,
|
||||||
pauseDownload,
|
pauseDownload,
|
||||||
cancelDownload,
|
cancelDownload,
|
||||||
removeGame,
|
removeGameFromLibrary,
|
||||||
isGameDeleting,
|
isGameDeleting,
|
||||||
} = useDownload();
|
} = useDownload();
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ export function HeroPanelActions({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (game) {
|
if (game) {
|
||||||
await removeGame(game.id);
|
await removeGameFromLibrary(game.id);
|
||||||
} else {
|
} else {
|
||||||
const gameExecutablePath = await selectGameExecutable();
|
const gameExecutablePath = await selectGameExecutable();
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ export function HeroPanelActions({
|
|||||||
{t("open_download_options")}
|
{t("open_download_options")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => removeGame(game.id).then(getGame)}
|
onClick={() => removeGameFromLibrary(game.id).then(getGame)}
|
||||||
theme="outline"
|
theme="outline"
|
||||||
disabled={deleting}
|
disabled={deleting}
|
||||||
>
|
>
|
||||||
|
@ -25,7 +25,7 @@ export function SelectFolderModal({
|
|||||||
}: SelectFolderModalProps) {
|
}: SelectFolderModalProps) {
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const [diskFreeSpace, setDiskFreeSpace] = useState<DiskSpace>(null);
|
const [diskFreeSpace, setDiskFreeSpace] = useState<DiskSpace | null>(null);
|
||||||
const [selectedPath, setSelectedPath] = useState("");
|
const [selectedPath, setSelectedPath] = useState("");
|
||||||
const [downloadStarting, setDownloadStarting] = useState(false);
|
const [downloadStarting, setDownloadStarting] = useState(false);
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export function SearchResults() {
|
|||||||
const [searchResults, setSearchResults] = useState<CatalogueEntry[]>([]);
|
const [searchResults, setSearchResults] = useState<CatalogueEntry[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const debouncedFunc = useRef<DebouncedFunc<() => void | null>>(null);
|
const debouncedFunc = useRef<DebouncedFunc<() => void> | null>(null);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ export function SearchResults() {
|
|||||||
|
|
||||||
debouncedFunc.current = debounce(() => {
|
debouncedFunc.current = debounce(() => {
|
||||||
window.electron
|
window.electron
|
||||||
.searchGames(searchParams.get("query"))
|
.searchGames(searchParams.get("query") ?? "")
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
setSearchResults(results);
|
setSearchResults(results);
|
||||||
})
|
})
|
||||||
|
@ -23,10 +23,10 @@ export function Settings() {
|
|||||||
setForm({
|
setForm({
|
||||||
downloadsPath: userPreferences?.downloadsPath || path,
|
downloadsPath: userPreferences?.downloadsPath || path,
|
||||||
downloadNotificationsEnabled:
|
downloadNotificationsEnabled:
|
||||||
userPreferences?.downloadNotificationsEnabled,
|
userPreferences?.downloadNotificationsEnabled ?? false,
|
||||||
repackUpdatesNotificationsEnabled:
|
repackUpdatesNotificationsEnabled:
|
||||||
userPreferences?.repackUpdatesNotificationsEnabled,
|
userPreferences?.repackUpdatesNotificationsEnabled ?? false,
|
||||||
telemetryEnabled: userPreferences?.telemetryEnabled,
|
telemetryEnabled: userPreferences?.telemetryEnabled ?? false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"@main/*": ["src/main/*"],
|
"@main/*": ["src/main/*"],
|
||||||
"@renderer/*": ["src/renderer/*"],
|
"@renderer/*": ["src/renderer/*"],
|
||||||
"@types": ["src/types/index.ts"],
|
"@types": ["src/types/index.ts"],
|
||||||
"@locales": ["src/locales/index.ts"]
|
"@locales": ["src/locales/index.ts"],
|
||||||
|
"@resources": ["src/resources/index.ts"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user