chore: sync with main

This commit is contained in:
Hydra 2024-04-20 00:42:31 +01:00
commit 84155ad19d
No known key found for this signature in database
22 changed files with 182 additions and 61 deletions

View File

@ -47,20 +47,8 @@ jobs:
- name: Install dependencies
run: pip install -r requirements.txt
- name: Build with Nuitka
uses: Nuitka/Nuitka-Action@main
with:
nuitka-version: main
working-directory: torrent-client
script-name: main.py
standalone: true
enable-plugins: libtorrent
output-dir: resources/dist
output-file: hydra-download-manager
windows-icon-from-ico: images/icon.ico
# - name: Build with pyinstaller
# run: pyinstaller torrent-client/main.py --distpath resources/dist --icon=images/icon.ico -n hydra-download-manager
- name: Build with cx_Freeze
run: python torrent-client/setup.py build
- name: Publish
run: yarn run publish

View File

@ -66,7 +66,7 @@ yarn start
Build the bittorrent client by using this command:
```bash
pyinstaller torrent-client/main.py --distpath resources/dist --icon=images/icon.ico -n hydra-download-manager
python torrent-client/setup.py build
```
### Build the Electron application

View File

@ -13,6 +13,16 @@ import { ElectronegativityPlugin } from "@electron-forge/plugin-electronegativit
import { mainConfig } from "./webpack.main.config";
import { rendererConfig } from "./webpack.renderer.config";
const linuxPkgConfig = {
mimeType: ["x-scheme-handler/hydralauncher"],
bin: "./Hydra",
desktopTemplate: "./hydra-launcher.desktop",
icon: "images/icon.png",
genericName: "Games Launcher",
name: "hydra-launcher",
productName: "Hydra"
};
const config: ForgeConfig = {
packagerConfig: {
asar: true,
@ -40,16 +50,10 @@ const config: ForgeConfig = {
}),
new MakerZIP({}, ["darwin", "linux"]),
new MakerRpm({
options: {
mimeType: ["x-scheme-handler/hydralauncher"],
bin: "./Hydra",
},
options: linuxPkgConfig
}),
new MakerDeb({
options: {
mimeType: ["x-scheme-handler/hydralauncher"],
bin: "./Hydra",
},
options: linuxPkgConfig
}),
],
publishers: [

11
hydra-launcher.desktop Normal file
View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=Hydra
Comment=No bullshit. Just play.
GenericName=Games Launcher
Exec=hydra-launcher %U
Icon=hydra-launcher
Type=Application
StartupNotify=true
Categories=GNOME;GTK;Utility;
MimeType=x-scheme-handler/hydralauncher;
StartupWMClass=Hydra

View File

@ -1,2 +1,5 @@
libtorrent
cx_Freeze
cx_Logging; sys_platform == 'win32'
lief; sys_platform == 'win32'
pywin32; sys_platform == 'win32'

View File

@ -7,7 +7,6 @@
"no_results": "No results found"
},
"sidebar": {
"home": "Home",
"catalogue": "Catalogue",
"downloads": "Downloads",
"settings": "Settings",
@ -16,7 +15,9 @@
"checking_files": "{{title}} ({{percentage}} - Checking files…)",
"paused": "{{title}} (Paused)",
"downloading": "{{title}} ({{percentage}} - Downloading…)",
"filter": "Filter library"
"filter": "Filter library",
"follow_us": "Follow us",
"home": "Home"
},
"header": {
"search": "Search",

View File

@ -16,7 +16,8 @@
"paused": "{{title}} (Pausado)",
"downloading": "{{title}} ({{percentage}} - Descargando…)",
"filter": "Filtrar biblioteca",
"home": "Hogar"
"home": "Hogar",
"follow_us": "Síganos"
},
"header": {
"search": "Buscar",

View File

@ -16,7 +16,8 @@
"paused": "{{title}} (En pause)",
"downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)",
"filter": "Filtrer la bibliothèque",
"home": "Maison"
"home": "Maison",
"follow_us": "Suivez-nous"
},
"header": {
"search": "Recherche",

View File

@ -16,7 +16,8 @@
"paused": "{{title}} (Pausado)",
"downloading": "{{title}} ({{percentage}} - Baixando…)",
"filter": "Filtrar biblioteca",
"home": "Início"
"home": "Início",
"follow_us": "Acompanhe-nos"
},
"header": {
"search": "Buscar",

View File

@ -1,30 +1,31 @@
import { app, ipcMain } from "electron";
import { defaultDownloadsPath } from "@main/constants";
import { app, ipcMain } from "electron";
import "./library/add-game-to-library";
import "./catalogue/search-games";
import "./catalogue/get-game-shop-details";
import "./catalogue/get-catalogue";
import "./library/get-library";
import "./catalogue/get-game-shop-details";
import "./catalogue/get-games";
import "./catalogue/get-how-long-to-beat";
import "./catalogue/get-random-game";
import "./catalogue/search-games";
import "./hardware/get-disk-free-space";
import "./library/add-game-to-library";
import "./library/close-game";
import "./library/delete-game-folder";
import "./library/get-game-by-object-id";
import "./library/get-library";
import "./library/get-repackers-friendly-names";
import "./library/open-game";
import "./library/open-game-installer";
import "./library/remove-game";
import "./misc/get-or-cache-image";
import "./misc/open-external";
import "./misc/show-open-dialog";
import "./torrenting/cancel-game-download";
import "./torrenting/pause-game-download";
import "./torrenting/resume-game-download";
import "./torrenting/start-game-download";
import "./misc/get-or-cache-image";
import "./user-preferences/update-user-preferences";
import "./user-preferences/get-user-preferences";
import "./library/get-repackers-friendly-names";
import "./library/get-game-by-object-id";
import "./library/open-game-installer";
import "./library/open-game";
import "./library/close-game";
import "./misc/show-open-dialog";
import "./library/remove-game";
import "./library/delete-game-folder";
import "./catalogue/get-random-game";
import "./catalogue/get-how-long-to-beat";
import "./catalogue/get-games";
import "./user-preferences/update-user-preferences";
ipcMain.handle("ping", () => "pong");
ipcMain.handle("getVersion", () => app.getVersion());

View File

@ -0,0 +1,9 @@
import { shell } from "electron";
import { registerEvent } from "../register-event";
const openExternal = async (_event: Electron.IpcMainInvokeEvent, src: string) =>
shell.openExternal(src);
registerEvent(openExternal, {
name: "openExternal",
});

View File

@ -1,7 +1,8 @@
import path from "node:path";
import cp from "node:child_process";
import fs from "node:fs";
import * as Sentry from "@sentry/electron/main";
import { Notification, app } from "electron";
import { Notification, app, dialog } from "electron";
import type { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
import { Game } from "@main/entity";
@ -54,6 +55,15 @@ export class TorrentClient {
binaryName
);
if (!fs.existsSync(binaryPath)) {
dialog.showErrorBox(
"Fatal",
"Hydra download manager binary not found. Please check if it has been removed by Windows Defender."
);
app.quit();
}
cp.spawn(binaryPath, commonArgs, {
stdio: "inherit",
windowsHide: true,
@ -155,7 +165,6 @@ export class TorrentClient {
}
} catch (err) {
Sentry.captureException(err);
Sentry.captureMessage(message, "error");
}
}
}

View File

@ -86,6 +86,7 @@ contextBridge.exposeInMainWorld("electron", {
ping: () => ipcRenderer.invoke("ping"),
getVersion: () => ipcRenderer.invoke("getVersion"),
getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"),
openExternal: (src: string) => ipcRenderer.invoke("openExternal", src),
showOpenDialog: (options: Electron.OpenDialogOptions) =>
ipcRenderer.invoke("showOpenDialog", options),
platform: process.platform,

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="16px" height="16px"><g fill="#8e919b" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M41.625,10.76953c-3.98047,-3.20313 -10.27734,-3.74609 -10.54687,-3.76563c-0.41797,-0.03516 -0.81641,0.19922 -0.98828,0.58594c-0.01562,0.02344 -0.15234,0.33984 -0.30469,0.83203c2.63281,0.44531 5.86719,1.33984 8.79297,3.15625c0.46875,0.28906 0.61328,0.90625 0.32422,1.375c-0.19141,0.30859 -0.51562,0.47656 -0.85156,0.47656c-0.17969,0 -0.36328,-0.05078 -0.52734,-0.15234c-5.03125,-3.12109 -11.3125,-3.27734 -12.52344,-3.27734c-1.21094,0 -7.49609,0.15625 -12.52344,3.27734c-0.46875,0.29297 -1.08594,0.14844 -1.375,-0.32031c-0.29297,-0.47266 -0.14844,-1.08594 0.32031,-1.37891c2.92578,-1.8125 6.16016,-2.71094 8.79297,-3.15234c-0.15234,-0.49609 -0.28906,-0.80859 -0.30078,-0.83594c-0.17578,-0.38672 -0.57031,-0.62891 -0.99219,-0.58594c-0.26953,0.01953 -6.56641,0.5625 -10.60156,3.80859c-2.10547,1.94922 -6.32031,13.33984 -6.32031,23.1875c0,0.17578 0.04688,0.34375 0.13281,0.49609c2.90625,5.10938 10.83984,6.44531 12.64844,6.50391c0.00781,0 0.01953,0 0.03125,0c0.32031,0 0.62109,-0.15234 0.80859,-0.41016l1.82813,-2.51562c-4.93359,-1.27344 -7.45312,-3.4375 -7.59766,-3.56641c-0.41406,-0.36328 -0.45312,-0.99609 -0.08594,-1.41016c0.36328,-0.41406 0.99609,-0.45312 1.41016,-0.08984c0.05859,0.05469 4.69922,3.99219 13.82422,3.99219c9.14063,0 13.78125,-3.95312 13.82813,-3.99219c0.41406,-0.35937 1.04297,-0.32422 1.41016,0.09375c0.36328,0.41406 0.32422,1.04297 -0.08984,1.40625c-0.14453,0.12891 -2.66406,2.29297 -7.59766,3.56641l1.82813,2.51563c0.1875,0.25781 0.48828,0.41016 0.80859,0.41016c0.01172,0 0.02344,0 0.03125,0c1.80859,-0.05859 9.74219,-1.39453 12.64844,-6.50391c0.08594,-0.15234 0.13281,-0.32031 0.13281,-0.49609c0,-9.84766 -4.21484,-21.23828 -6.375,-23.23047zM18.5,30c-1.93359,0 -3.5,-1.78906 -3.5,-4c0,-2.21094 1.56641,-4 3.5,-4c1.93359,0 3.5,1.78906 3.5,4c0,2.21094 -1.56641,4 -3.5,4zM31.5,30c-1.93359,0 -3.5,-1.78906 -3.5,-4c0,-2.21094 1.56641,-4 3.5,-4c1.93359,0 3.5,1.78906 3.5,4c0,2.21094 -1.56641,4 -3.5,4z"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="16px" height="16px"><g fill="#8e919b" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M5.91992,6l14.66211,21.375l-14.35156,16.625h3.17969l12.57617,-14.57812l10,14.57813h12.01367l-15.31836,-22.33008l13.51758,-15.66992h-3.16992l-11.75391,13.61719l-9.3418,-13.61719zM9.7168,8h7.16406l23.32227,34h-7.16406z"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@ -1,5 +1,5 @@
import { SPACING_UNIT, vars } from "../../theme.css";
import { style } from "@vanilla-extract/css";
import { SPACING_UNIT, vars } from "../../theme.css";
export const bottomPanel = style({
width: "100%",
@ -14,9 +14,10 @@ export const bottomPanel = style({
});
export const downloadsButton = style({
cursor: "pointer",
color: vars.color.bodyText,
borderBottom: "1px solid transparent",
":hover": {
textDecoration: "underline",
borderBottom: `1px solid ${vars.color.bodyText}`,
cursor: "pointer",
},
});

View File

@ -6,6 +6,7 @@ export const sidebar = recipe({
base: {
backgroundColor: vars.color.darkBackground,
color: "#c0c1c7",
flexDirection: "column",
display: "flex",
transition: "opacity ease 0.2s",
borderRight: `solid 1px ${vars.color.borderColor}`,
@ -134,3 +135,36 @@ export const section = recipe({
},
},
});
export const sidebarFooter = style({
marginTop: "auto",
padding: `${SPACING_UNIT * 2}px`,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
});
export const footerSocialsContainer = style({
display: "flex",
alignItems: "center",
gap: `${SPACING_UNIT * 1.5}px`,
});
export const footerSocialsItem = style({
color: vars.color.bodyText,
backgroundColor: vars.color.darkBackground,
width: "16px",
height: "16px",
display: "flex",
alignItems: "center",
transition: "all ease 0.15s",
":hover": {
opacity: 0.75,
cursor: "pointer",
},
});
export const footerText = style({
color: vars.color.bodyText,
fontSize: "12px",
});

View File

@ -4,13 +4,32 @@ import { useLocation, useNavigate } from "react-router-dom";
import type { Game } from "@types";
import { useDownload, useLibrary } from "@renderer/hooks";
import { AsyncImage, TextField } from "@renderer/components";
import { useDownload, useLibrary } from "@renderer/hooks";
import { SPACING_UNIT } from "@renderer/theme.css";
import * as styles from "./sidebar.css";
import { routes } from "./routes";
import { MarkGithubIcon } from "@primer/octicons-react";
import DiscordLogo from "@renderer/assets/discord-icon.svg";
import XLogo from "@renderer/assets/x-icon.svg";
import * as styles from "./sidebar.css";
const socials = [
{
url: "https://discord.gg/hydralauncher",
icon: <DiscordLogo />,
},
{
url: "https://twitter.com/hydralauncher",
icon: <XLogo />,
},
{
url: "https://github.com/hydralauncher/hydra",
icon: <MarkGithubIcon size={16} />,
},
];
const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_INITIAL_WIDTH = 250;
const SIDEBAR_MAX_WIDTH = 450;
@ -212,6 +231,24 @@ export function Sidebar() {
className={styles.handle}
onMouseDown={handleMouseDown}
/>
<footer className={styles.sidebarFooter}>
<div className={styles.footerText}>{t("follow_us")}</div>
<span className={styles.footerSocialsContainer}>
{socials.map((item) => {
return (
<button
key={item.url}
className={styles.footerSocialsItem}
onClick={() => window.electron.openExternal(item.url)}
>
{item.icon}
</button>
);
})}
</span>
</footer>
</aside>
);
}

View File

@ -1,12 +1,12 @@
import type {
CatalogueEntry,
GameShop,
Game,
CatalogueCategory,
TorrentProgress,
ShopDetails,
UserPreferences,
CatalogueEntry,
Game,
GameShop,
HowLongToBeatCategory,
ShopDetails,
TorrentProgress,
UserPreferences,
} from "@types";
import type { DiskSpace } from "check-disk-space";
@ -78,6 +78,7 @@ declare global {
/* Misc */
getOrCacheImage: (url: string) => Promise<string>;
openExternal: (src: string) => Promise<void>;
getVersion: () => Promise<string>;
ping: () => string;
getDefaultDownloadsPath: () => Promise<string>;

View File

@ -1,8 +1,8 @@
const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
export const formatBytes = (bytes: number): string => {
if (!Number.isFinite(bytes) || isNaN(bytes) || bytes < 0) {
return `N/A ${FORMAT[0]}`;
if (!Number.isFinite(bytes) || isNaN(bytes) || bytes <= 0) {
return `0 ${FORMAT[0]}`;
}
const byteKBase = 1024;

16
torrent-client/setup.py Normal file
View File

@ -0,0 +1,16 @@
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {
"packages": ["libtorrent"],
"build_exe": "resources/dist/hydra-download-manager",
"include_msvcr": True
}
setup(
name="hydra-download-manager",
version="0.1",
description="Hydra Torrent Client",
options={"build_exe": build_exe_options},
executables=[Executable("torrent-client/main.py", target_name="hydra-download-manager")]
)