feat: adding download method toggle

This commit is contained in:
Chubby Granny Chaser 2024-05-22 01:06:25 +01:00
commit a240c3ae24
No known key found for this signature in database
33 changed files with 564 additions and 45 deletions

View File

@ -1,4 +1,4 @@
name: Build name: Release
on: on:
push: push:

View File

@ -119,7 +119,7 @@ yarn
### Усталёўка Python 3.9 ### Усталёўка Python 3.9
Упэўніцеся, што ў вас усталяваны Python 3.9 на вашым кампутары. Вы можаце загрузіць і ўсталяваць яго з [python.org](https://www.python.org/downloads/release/python-3919/). Упэўніцеся, што ў вас усталяваны Python 3.9 на вашым кампутары. Вы можаце загрузіць і ўсталяваць яго з [python.org](https://www.python.org/downloads/release/python-3913/).
### Усталёўка залежнасцяў Python ### Усталёўка залежнасцяў Python

View File

@ -13,11 +13,11 @@
[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions) [![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions)
[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases) [![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.md)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md) [![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md) [![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md) [![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
![Hydra Catalogue](./docs/screenshot.png) ![Hydra Catalogue](./docs/screenshot.png)
@ -119,7 +119,7 @@ yarn
### Install Python 3.9 ### Install Python 3.9
Ensure you have Python 3.9 installed on your machine. You can download and install it from [python.org](https://www.python.org/downloads/release/python-3919/). Ensure you have Python 3.9 installed on your machine. You can download and install it from [python.org](https://www.python.org/downloads/release/python-3913/).
### Install Python Dependencies ### Install Python Dependencies

185
README.pl.md Normal file
View File

@ -0,0 +1,185 @@
<br>
<div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra - to program uruchamiający gry z własnym wbudowanym klientem bittorrent i samodzielnie zarządzanym repackagerem..</strong>
</p>
[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions)
[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
![Hydra Catalogue](./docs/screenshot.png)
</div>
## Zawartość.
- [O nas](#o-nas)
- [Cechy.](#cechy)
- [Instalacja](#instalacja)
- [Dokonaj wpłaty](#dokonaj-wpłaty)
- [Dołącz do naszego kanału Telegram](#dołącz-do-naszego-kanału-telegram)
- [Rozwidlenie i sklonowanie repozytorium](#rozwidlenie-i-sklonowanie-repozytorium)
- [Jak możesz wnieść swój wkład](#jak-możesz-pomóc)
- [Struktura projektu](#struktura-projektu)
- [Utwórz kompilację z kodu źródłowego](#utwórz-kompilację-z-kodu-źródłowego)
- [Instalacja Node.js](#zainstaluj-nodejs)
- [Instalacja Yarn](#zainstaluj-yarn)
- [Instalacja Node zależności](#zainstaluj-zależności-node)
- [Instalacja Python 3.9](#zainstaluj-python-39)
- [Instalacja Python zależności](#zainstaluj-zależności-pythona)
- [Zmienne środowiskowe](#zmienne-środowiskowe)
- [Uruchomienie](#utwórz-kompilację-z-kodu-źródłowego)
- [Tworzenie kompilacji](#tworzenie-kompilacji)
- [Tworzenie klienta bittorrent](#zbuduj-klienta-bittorrent)
- [Tworzenie kompilacji aplikacji Electron](#tworzenie-aplikacji-electron)
- [Współtwórcy](#współtwórcy)
## O nas
**Hydra** - jest **programem uruchamiającym gry** z wbudowanym **klientem BitTorrent** i **samozarządzającym się repackagerem**.
<br>
Ten launcher jest napisany w TypeScript (Electron) i Pythonie, który współpracuje z systemem torrent przy użyciu libtorrent.
## Cechy
- Samodzielnie zarządzany repackager wśród wszystkich najbardziej zaufanych stron na [Megathread]("https://www.reddit.com/r/Piracy/wiki/megathread/").
- Własny wbudowany klient bittorrent
- Integracja funkcji How Long To Beat (HLTB) na stronie gry
- Personalizacja folderu pobierania
- Powiadomienia o aktualizacjach listy repacków
- Wsparcie dla systemów Windows i Linux
- Stała aktualizacja
- I nie tylko ...
## Instalacja
Aby zainstalować, wykonaj poniższe czynności:
1. Pobierz najnowszą wersję programu Hydra ze strony [Wydania](https://github.com/hydralauncher/hydra/releases/latest).
- Pobierz .exe tylko, jeśli chcesz zainstalować Hydrę w systemie Windows.
- Pobierz .deb lub .rpm lub .zip, jeśli chcesz zainstalować Hydrę w systemie Linux (zależy od dystrybucji systemu Linux).
2. Uruchom pobrany plik.
3. Ciesz się Hydrą!
## <a name="contributing"> Dokonaj wpłaty
### <a name="join-our-telegram"></a> Dołącz do naszego kanału Telegram
Skupiamy nasze dyskusje na naszym kanale [Telegram](https://t.me/hydralauncher).
1. Dołącz do naszego kanału
2. Przejdź do kanału ról i wybierz rolę Pracownik.
3. Wejdź na kanał dev, komunikuj się z nami i dziel się swoimi pomysłami.
### Rozwidlenie i sklonowanie repozytorium
1. Rozwidlenie repozytorium [(kliknij tutaj, aby rozwidlić teraz)](https://github.com/hydralauncher/hydra/fork)
2. Sklonuj swój rozwidlony kod `git clone https://github.com/your_username/hydra`.
3. Utwórz nowy brunch
4. Wypchnij swoje zatwierdzenia
5. Wyślij nowy Pull Request
### Jak możesz pomóc
- Tłumaczenie: Chcemy, aby Hydra była dostępna dla jak największej liczby osób. Zachęcamy do pomocy w tłumaczeniu na nowe języki lub aktualizowaniu i ulepszaniu tych, które są już dostępne na Hydrze.
- Kod: Hydra jest zbudowana przy użyciu Typescript, Electron i odrobiny Pythona. Jeśli chcesz wnieść swój wkład, dołącz do naszego kanału Telegram!
### Struktura projektu
- Klient torrent: Używamy libtorrent, biblioteki Pythona, do zarządzania pobieraniem torrentów.
- src/renderer: interfejs aplikacji
- src/main: cała logika jest tutaj.
## Utwórz kompilację z kodu źródłowego
### Zainstaluj Node.js
Upewnij się, że masz zainstalowany Node.js na swoim komputerze. Jeśli nie, pobierz i zainstaluj go ze strony [nodejs.org](https://nodejs.org/).
### Zainstaluj Yarn
Yarn to menedżer pakietów dla Node.js. Jeśli jeszcze nie zainstalowałeś Yarn, możesz to zrobić, postępując zgodnie z instrukcjami na stronie [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Zainstaluj zależności Node
Przejdź do katalogu projektu i zainstaluj zależności Node za pomocą Yarn:
```bash
cd hydra
yarn
```
### Zainstaluj Python 3.9
Upewnij się, że masz zainstalowany Python 3.9 na swoim komputerze. Można go pobrać i zainstalować ze strony [python.org](https://www.python.org/downloads/release/python-3913/).
### Zainstaluj zależności Pythona
Zainstaluj niezbędne zależności Pythona za pomocą pip:
```bash
pip install -r requirements.txt
```
## Zmienne środowiskowe
Będziesz potrzebował klucza API SteamGridDB, aby uzyskać ikony gier podczas instalacji.
Jeśli chcesz użyć onlinefix jako repackagera, musisz dodać swoje dane uwierzytelniające do .env
Po jego uzyskaniu można skopiować plik lub zmienić jego nazwę `.env.example` na `.env` i umieść go na`STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`.
## Run
Po skonfigurowaniu wszystkiego można uruchomić następujące polecenie, aby uruchomić zarówno proces Electron, jak i klienta bittorrent:
```bash
yarn dev
```
## Tworzenie kompilacji
### Zbuduj klienta bittorrent
Zbuduj klienta bittorrent za pomocą tego poleceniaи:
```bash
python torrent-client/setup.py build
```
### Tworzenie aplikacji Electron
Zbuduj aplikację Electron za pomocą tego polecenia:
W systemie Windows:
```bash
yarn build:win
```
W systemie Linux:
```bash
yarn build:linux
```
## Współtwórcy
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## License
Hydra posiada licencję [MIT License](LICENSE).

View File

@ -13,12 +13,11 @@
[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions) [![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions)
[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases) [![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md) [![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md) [![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md) [![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
![Hydra Catalogue](./docs/screenshot.png) ![Hydra Catalogue](./docs/screenshot.png)
</div> </div>
@ -119,7 +118,7 @@ yarn
### <a name="install-python-39"></a> Instale Python 3.9 ### <a name="install-python-39"></a> Instale Python 3.9
Certifique-se de ter o Python 3.9 instalado em sua máquina. Você pode baixá-lo e instalá-lo em [python.org](https://www.python.org/downloads/release/python-3919/). Certifique-se de ter o Python 3.9 instalado em sua máquina. Você pode baixá-lo e instalá-lo em [python.org](https://www.python.org/downloads/release/python-3913/).
### <a name="install-python-dependencies"></a> Instale Python Dependencies ### <a name="install-python-dependencies"></a> Instale Python Dependencies

View File

@ -13,11 +13,11 @@
[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions) [![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions)
[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases) [![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md) [![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.md)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
![Hydra Catalogue](./docs/screenshot.png) ![Hydra Catalogue](./docs/screenshot.png)
@ -119,7 +119,7 @@ yarn
### Установка Python 3.9 ### Установка Python 3.9
Убедитесь, что у вас установлен Python 3.9 на вашем компьютере. Вы можете загрузить и установить его с [python.org](https://www.python.org/downloads/release/python-3919/). Убедитесь, что у вас установлен Python 3.9 на вашем компьютере. Вы можете загрузить и установить его с [python.org](https://www.python.org/downloads/release/python-3913/).
### Установка зависимостей Python ### Установка зависимостей Python

View File

@ -13,11 +13,11 @@
[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions) [![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions)
[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases) [![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md)
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md) [![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.md)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
![Hydra Catalogue](./docs/screenshot.png) ![Hydra Catalogue](./docs/screenshot.png)
@ -123,7 +123,7 @@ yarn
### Встановіть Python 3.9 ### Встановіть Python 3.9
Переконайтеся, що на вашому комп'ютері встановлено Python 3.9. Ви можете завантажити та встановити його з [python.org](https://www.python.org/downloads/release/python-3919/). Переконайтеся, що на вашому комп'ютері встановлено Python 3.9. Ви можете завантажити та встановити його з [python.org](https://www.python.org/downloads/release/python-3913/).
### Встановіть Python залежності ### Встановіть Python залежності

View File

@ -1,6 +1,6 @@
{ {
"name": "hydra", "name": "hydralauncher",
"version": "1.2.2", "version": "1.2.3",
"description": "Hydra", "description": "Hydra",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "Los Broxas", "author": "Los Broxas",

View File

@ -176,5 +176,11 @@
}, },
"modal": { "modal": {
"close": "Close button" "close": "Close button"
},
"splash": {
"downloading_version": "Downloading version {{version}}",
"searching_updates": "Searching for updates",
"update_found": "Update {{version}} found",
"restarting_and_applying": "Restarting and applying update"
} }
} }

View File

@ -176,5 +176,11 @@
}, },
"modal": { "modal": {
"close": "Botão de fechar" "close": "Botão de fechar"
},
"splash": {
"downloading_version": "Baixando versão {{version}}",
"searching_updates": "Buscando atualizações",
"update_found": "Versão {{version}} encontrada",
"restarting_and_applying": "Reiniciando e aplicando atualização"
} }
} }

View File

@ -22,7 +22,7 @@ export const defaultDownloadsPath = app.getPath("downloads");
export const databasePath = path.join( export const databasePath = path.join(
app.getPath("appData"), app.getPath("appData"),
app.getName(), "hydra",
"hydra.db" "hydra.db"
); );

View File

@ -0,0 +1,48 @@
import { AppUpdaterEvents } from "@types";
import { registerEvent } from "../register-event";
import updater, { ProgressInfo, UpdateInfo } from "electron-updater";
import { WindowManager } from "@main/services";
import { app } from "electron";
const { autoUpdater } = updater;
const sendEvent = (event: AppUpdaterEvents) => {
WindowManager.splashWindow?.webContents.send("autoUpdaterEvent", event);
};
const mockValuesForDebug = async () => {
sendEvent({ type: "update-downloaded" });
};
const checkForUpdates = async (_event: Electron.IpcMainInvokeEvent) => {
autoUpdater
.addListener("error", () => {
sendEvent({ type: "error" });
})
.addListener("checking-for-update", () => {
sendEvent({ type: "checking-for-updates" });
})
.addListener("update-not-available", () => {
sendEvent({ type: "update-not-available" });
})
.addListener("update-available", (info: UpdateInfo) => {
sendEvent({ type: "update-available", info });
})
.addListener("update-downloaded", () => {
sendEvent({ type: "update-downloaded" });
})
.addListener("download-progress", (info: ProgressInfo) => {
sendEvent({ type: "download-progress", info });
})
.addListener("update-cancelled", () => {
sendEvent({ type: "update-cancelled" });
});
if (app.isPackaged) {
autoUpdater.checkForUpdates();
} else {
await mockValuesForDebug();
}
};
registerEvent("checkForUpdates", checkForUpdates);

View File

@ -0,0 +1,12 @@
import { WindowManager } from "@main/services";
import { registerEvent } from "../register-event";
import updater from "electron-updater";
const { autoUpdater } = updater;
const continueToMainWindow = async (_event: Electron.IpcMainInvokeEvent) => {
autoUpdater.removeAllListeners();
WindowManager.prepareMainWindowAndCloseSplash();
};
registerEvent("continueToMainWindow", continueToMainWindow);

View File

@ -0,0 +1,17 @@
import { app } from "electron";
import { registerEvent } from "../register-event";
import updater from "electron-updater";
import { WindowManager } from "@main/services";
const { autoUpdater } = updater;
const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
if (app.isPackaged) {
autoUpdater.quitAndInstall(true, true);
} else {
autoUpdater.removeAllListeners();
WindowManager.prepareMainWindowAndCloseSplash();
}
};
registerEvent("restartAndInstallUpdate", restartAndInstallUpdate);

View File

@ -27,6 +27,9 @@ import "./torrenting/start-game-download";
import "./user-preferences/get-user-preferences"; import "./user-preferences/get-user-preferences";
import "./user-preferences/update-user-preferences"; import "./user-preferences/update-user-preferences";
import "./user-preferences/auto-launch"; import "./user-preferences/auto-launch";
import "./autoupdater/check-for-updates";
import "./autoupdater/restart-and-install-update";
import "./autoupdater/continue-to-main-window";
ipcMain.handle("ping", () => "pong"); ipcMain.handle("ping", () => "pong");
ipcMain.handle("getVersion", () => app.getVersion()); ipcMain.handle("getVersion", () => app.getVersion());

View File

@ -3,11 +3,10 @@ import updater from "electron-updater";
import i18n from "i18next"; import i18n from "i18next";
import path from "node:path"; import path from "node:path";
import { electronApp, optimizer } from "@electron-toolkit/utils"; import { electronApp, optimizer } from "@electron-toolkit/utils";
import { resolveDatabaseUpdates, WindowManager } from "@main/services"; import { logger, resolveDatabaseUpdates, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source"; import { dataSource } from "@main/data-source";
import * as resources from "@locales"; import * as resources from "@locales";
import { userPreferencesRepository } from "@main/repository"; import { userPreferencesRepository } from "@main/repository";
const { autoUpdater } = updater; const { autoUpdater } = updater;
autoUpdater.setFeedURL({ autoUpdater.setFeedURL({
@ -16,6 +15,8 @@ autoUpdater.setFeedURL({
repo: "hydra", repo: "hydra",
}); });
autoUpdater.logger = logger;
const gotTheLock = app.requestSingleInstanceLock(); const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) app.quit(); if (!gotTheLock) app.quit();
@ -63,12 +64,8 @@ app.whenReady().then(() => {
where: { id: 1 }, where: { id: 1 },
}); });
WindowManager.createMainWindow(); WindowManager.createSplashScreen();
WindowManager.createSystemTray(userPreferences?.language || "en"); WindowManager.createSystemTray(userPreferences?.language || "en");
WindowManager.mainWindow?.on("ready-to-show", () => {
autoUpdater.checkForUpdatesAndNotify();
});
}); });
}); });

View File

@ -148,9 +148,10 @@ export const getNewRepacksFromOnlineFix = async (
); );
if (!newRepacks.length) return; if (!newRepacks.length) return;
if (page === totalPages) return;
await savePage(newRepacks); await savePage(newRepacks);
if (page === totalPages) return;
return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar); return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar);
}; };

View File

@ -111,9 +111,10 @@ export const getNewRepacksFromXatab = async (
); );
if (!newRepacks.length) return; if (!newRepacks.length) return;
if (page === totalPages) return;
await savePage(newRepacks); await savePage(newRepacks);
if (page === totalPages) return;
return getNewRepacksFromXatab(existingRepacks, page + 1); return getNewRepacksFromXatab(existingRepacks, page + 1);
}; };

View File

@ -17,6 +17,8 @@ import { IsNull, Not } from "typeorm";
export class WindowManager { export class WindowManager {
public static mainWindow: Electron.BrowserWindow | null = null; public static mainWindow: Electron.BrowserWindow | null = null;
public static splashWindow: Electron.BrowserWindow | null = null;
public static isReadyToShowMainWindow = false;
private static loadURL(hash = "") { private static loadURL(hash = "") {
// HMR for renderer base on electron-vite cli. // HMR for renderer base on electron-vite cli.
@ -35,13 +37,51 @@ export class WindowManager {
} }
} }
private static loadSplashURL() {
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
this.splashWindow?.loadURL(
`${process.env["ELECTRON_RENDERER_URL"]}#/splash`
);
} else {
this.splashWindow?.loadFile(
path.join(__dirname, "../renderer/index.html"),
{
hash: "splash",
}
);
}
}
public static createSplashScreen() {
if (this.splashWindow) return;
this.splashWindow = new BrowserWindow({
width: 380,
height: 380,
frame: false,
resizable: false,
backgroundColor: "#1c1c1c",
webPreferences: {
preload: path.join(__dirname, "../preload/index.mjs"),
sandbox: false,
},
});
this.loadSplashURL();
this.splashWindow.removeMenu();
}
public static createMainWindow() { public static createMainWindow() {
// Create the browser window. if (this.mainWindow || !this.isReadyToShowMainWindow) return;
this.mainWindow = new BrowserWindow({ this.mainWindow = new BrowserWindow({
width: 1200, width: 1200,
height: 720, height: 720,
minWidth: 1024, minWidth: 1024,
minHeight: 540, minHeight: 540,
backgroundColor: "#1c1c1c",
titleBarStyle: "hidden", titleBarStyle: "hidden",
...(process.platform === "linux" ? { icon } : {}), ...(process.platform === "linux" ? { icon } : {}),
trafficLightPosition: { x: 16, y: 16 }, trafficLightPosition: { x: 16, y: 16 },
@ -75,6 +115,12 @@ export class WindowManager {
}); });
} }
public static prepareMainWindowAndCloseSplash() {
this.isReadyToShowMainWindow = true;
this.splashWindow?.close();
this.createMainWindow();
}
public static redirect(hash: string) { public static redirect(hash: string) {
if (!this.mainWindow) this.createMainWindow(); if (!this.mainWindow) this.createMainWindow();
this.loadURL(hash); this.loadURL(hash);

View File

@ -7,6 +7,7 @@ import type {
GameShop, GameShop,
DownloadProgress, DownloadProgress,
UserPreferences, UserPreferences,
AppUpdaterEvents,
} from "@types"; } from "@types";
contextBridge.exposeInMainWorld("electron", { contextBridge.exposeInMainWorld("electron", {
@ -112,4 +113,21 @@ contextBridge.exposeInMainWorld("electron", {
showOpenDialog: (options: Electron.OpenDialogOptions) => showOpenDialog: (options: Electron.OpenDialogOptions) =>
ipcRenderer.invoke("showOpenDialog", options), ipcRenderer.invoke("showOpenDialog", options),
platform: process.platform, platform: process.platform,
/* Splash */
onAutoUpdaterEvent: (cb: (value: AppUpdaterEvents) => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
value: AppUpdaterEvents
) => cb(value);
ipcRenderer.on("autoUpdaterEvent", listener);
return () => {
ipcRenderer.removeListener("autoUpdaterEvent", listener);
};
},
checkForUpdates: () => ipcRenderer.invoke("checkForUpdates"),
restartAndInstallUpdate: () => ipcRenderer.invoke("restartAndInstallUpdate"),
continueToMainWindow: () => ipcRenderer.invoke("continueToMainWindow"),
}); });

View File

@ -12,7 +12,7 @@ import {
import * as styles from "./app.css"; import * as styles from "./app.css";
import { themeClass } from "./theme.css"; import { themeClass } from "./theme.css";
import { useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { import {
setSearch, setSearch,
clearSearch, clearSearch,
@ -26,7 +26,7 @@ export interface AppProps {
children: React.ReactNode; children: React.ReactNode;
} }
export function App({ children }: AppProps) { export function App() {
const contentRef = useRef<HTMLDivElement>(null); const contentRef = useRef<HTMLDivElement>(null);
const { updateLibrary } = useLibrary(); const { updateLibrary } = useLibrary();
@ -127,7 +127,7 @@ export function App({ children }: AppProps) {
/> />
<section ref={contentRef} className={styles.content}> <section ref={contentRef} className={styles.content}>
{children} <Outlet />
</section> </section>
</article> </article>
</main> </main>

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -3,7 +3,7 @@ import { keyframes, style } from "@vanilla-extract/css";
import { SPACING_UNIT, vars } from "../../theme.css"; import { SPACING_UNIT, vars } from "../../theme.css";
import { recipe } from "@vanilla-extract/recipes"; import { recipe } from "@vanilla-extract/recipes";
const TOAST_HEIGHT = 60; const TOAST_HEIGHT = 55;
export const slideIn = keyframes({ export const slideIn = keyframes({
"0%": { transform: `translateY(${TOAST_HEIGHT + SPACING_UNIT * 2}px)` }, "0%": { transform: `translateY(${TOAST_HEIGHT + SPACING_UNIT * 2}px)` },

View File

@ -85,6 +85,7 @@ export function Toast({ visible, message, type, onClose }: ToastProps) {
type="button" type="button"
className={styles.closeButton} className={styles.closeButton}
onClick={startAnimateClosing} onClick={startAnimateClosing}
aria-label="Close toast"
> >
<XCircleIcon /> <XCircleIcon />
</button> </button>

View File

@ -1,4 +1,5 @@
import type { import type {
AppUpdaterEvents,
CatalogueCategory, CatalogueCategory,
CatalogueEntry, CatalogueEntry,
Game, Game,
@ -90,6 +91,14 @@ declare global {
options: Electron.OpenDialogOptions options: Electron.OpenDialogOptions
) => Promise<Electron.OpenDialogReturnValue>; ) => Promise<Electron.OpenDialogReturnValue>;
platform: NodeJS.Platform; platform: NodeJS.Platform;
/* Splash */
onAutoUpdaterEvent: (
cb: (event: AppUpdaterEvents) => void
) => () => Electron.IpcRenderer;
checkForUpdates: () => Promise<void>;
restartAndInstallUpdate: () => Promise<void>;
continueToMainWindow: () => Promise<void>;
} }
interface Window { interface Window {

View File

@ -27,6 +27,7 @@ import {
import { store } from "./store"; import { store } from "./store";
import * as resources from "@locales"; import * as resources from "@locales";
import Splash from "./pages/splash/splash";
i18n i18n
.use(LanguageDetector) .use(LanguageDetector)
@ -46,16 +47,17 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode> <React.StrictMode>
<Provider store={store}> <Provider store={store}>
<HashRouter> <HashRouter>
<App> <Routes>
<Routes> <Route path="/splash" Component={Splash} />
<Route element={<App />}>
<Route path="/" Component={Home} /> <Route path="/" Component={Home} />
<Route path="/catalogue" Component={Catalogue} /> <Route path="/catalogue" Component={Catalogue} />
<Route path="/downloads" Component={Downloads} /> <Route path="/downloads" Component={Downloads} />
<Route path="/game/:shop/:objectID" Component={GameDetails} /> <Route path="/game/:shop/:objectID" Component={GameDetails} />
<Route path="/search" Component={SearchResults} /> <Route path="/search" Component={SearchResults} />
<Route path="/settings" Component={Settings} /> <Route path="/settings" Component={Settings} />
</Routes> </Route>
</App> </Routes>
</HashRouter> </HashRouter>
</Provider> </Provider>
</React.StrictMode> </React.StrictMode>

View File

@ -57,9 +57,7 @@ export function GallerySlider() {
if (hasMovies && mediaContainerRef.current) { if (hasMovies && mediaContainerRef.current) {
mediaContainerRef.current.childNodes.forEach((node, index) => { mediaContainerRef.current.childNodes.forEach((node, index) => {
if (node instanceof HTMLVideoElement) { if (node instanceof HTMLVideoElement) {
if (index == mediaIndex) { if (index !== mediaIndex) {
node.play();
} else {
node.pause(); node.pause();
} }
} }

View File

@ -17,3 +17,12 @@ export const hintText = style({
fontSize: "12px", fontSize: "12px",
color: vars.color.bodyText, color: vars.color.bodyText,
}); });
export const downloaders = style({
display: "flex",
gap: `${SPACING_UNIT}px`,
});
export const downloaderOption = style({
flex: "1",
});

View File

@ -80,8 +80,24 @@ export function SelectFolderModal({
onClose={onClose} onClose={onClose}
> >
<div className={styles.container}> <div className={styles.container}>
<div>
<label style={{ marginBottom: 0, padding: 0 }}>Download method</label>
<div className={styles.downloaders}>
<Button className={styles.downloaderOption} theme="outline">
Torrent
</Button>
<Button className={styles.downloaderOption}>Real Debrid</Button>
</div>
</div>
<div className={styles.downloadsPathField}> <div className={styles.downloadsPathField}>
<TextField value={selectedPath} readOnly disabled /> <TextField
value={selectedPath}
readOnly
disabled
label="Download path"
/>
<Button <Button
style={{ alignSelf: "flex-end" }} style={{ alignSelf: "flex-end" }}
@ -92,11 +108,13 @@ export function SelectFolderModal({
{t("change")} {t("change")}
</Button> </Button>
</div> </div>
<p className={styles.hintText}> <p className={styles.hintText}>
<Trans i18nKey="select_folder_hint" ns="game_details"> <Trans i18nKey="select_folder_hint" ns="game_details">
<Link to="/settings" /> <Link to="/settings" />
</Trans> </Trans>
</p> </p>
<Button onClick={handleStartClick} disabled={downloadStarting}> <Button onClick={handleStartClick} disabled={downloadStarting}>
<DownloadIcon /> <DownloadIcon />
{t("download_now")} {t("download_now")}

View File

@ -28,6 +28,8 @@ export function Settings() {
const handleUpdateUserPreferences = async ( const handleUpdateUserPreferences = async (
values: Partial<UserPreferences> values: Partial<UserPreferences>
) => { ) => {
setIsToastVisible(false);
await window.electron.updateUserPreferences(values); await window.electron.updateUserPreferences(values);
window.electron.getUserPreferences().then((userPreferences) => { window.electron.getUserPreferences().then((userPreferences) => {
setUserPreferences(userPreferences); setUserPreferences(userPreferences);

View File

@ -0,0 +1,49 @@
import { style } from "@vanilla-extract/css";
import { SPACING_UNIT, vars } from "../../theme.css";
export const main = style({
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
padding: `${SPACING_UNIT * 3}px`,
flex: "1",
overflowY: "auto",
alignItems: "center",
});
export const splashIcon = style({
width: "75%",
});
export const updateInfoSection = style({
width: "100%",
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
flex: "1",
overflowY: "auto",
alignItems: "center",
justifyContent: "center",
});
export const progressBar = style({
WebkitAppearance: "none",
appearance: "none",
borderRadius: "4px",
width: "100%",
border: `solid 1px ${vars.color.border}`,
overflow: "hidden",
height: "18px",
"::-webkit-progress-value": {
backgroundColor: vars.color.muted,
transition: "width 0.2s",
},
"::-webkit-progress-bar": {
backgroundColor: vars.color.darkBackground,
},
});
export const progressBarText = style({
zIndex: 2,
});

View File

@ -0,0 +1,82 @@
import icon from "@renderer/assets/icon.png";
import * as styles from "./splash.css";
import { themeClass } from "../../theme.css";
import "../../app.css";
import { useEffect, useState } from "react";
import { AppUpdaterEvents } from "@types";
import { useTranslation } from "react-i18next";
document.body.classList.add(themeClass);
export default function Splash() {
const [status, setStatus] = useState<AppUpdaterEvents | null>(null);
const [newVersion, setNewVersion] = useState("");
const { t } = useTranslation("splash");
useEffect(() => {
const unsubscribe = window.electron.onAutoUpdaterEvent(
(event: AppUpdaterEvents) => {
setStatus(event);
switch (event.type) {
case "error":
window.electron.continueToMainWindow();
break;
case "update-available":
setNewVersion(event.info.version);
break;
case "update-cancelled":
window.electron.continueToMainWindow();
break;
case "update-downloaded":
window.electron.restartAndInstallUpdate();
break;
case "update-not-available":
window.electron.continueToMainWindow();
break;
}
}
);
window.electron.checkForUpdates();
return () => {
unsubscribe();
};
}, []);
const renderUpdateInfo = () => {
switch (status?.type) {
case "download-progress":
return (
<>
<p>{t("downloading_version", { version: newVersion })}</p>
<progress
className={styles.progressBar}
max="100"
value={status.info.percent}
/>
</>
);
case "checking-for-updates":
return <p>{t("searching_updates")}</p>;
case "update-available":
return <p>{t("update_found", { version: newVersion })}</p>;
case "update-downloaded":
return <p>{t("restarting_and_applying")}</p>;
default:
return <></>;
}
};
return (
<main className={styles.main}>
<img src={icon} className={styles.splashIcon} alt="Hydra Launcher Logo" />
<section className={styles.updateInfoSection}>
{renderUpdateInfo()}
</section>
</main>
);
}

View File

@ -1,5 +1,6 @@
import type { Downloader } from "@shared";
import type { Aria2Status } from "aria2"; import type { Aria2Status } from "aria2";
import type { Downloader } from "@shared";
import { ProgressInfo, UpdateInfo } from "electron-updater";
export type GameShop = "steam" | "epic"; export type GameShop = "steam" | "epic";
export type CatalogueCategory = "recently_added" | "trending"; export type CatalogueCategory = "recently_added" | "trending";
@ -146,3 +147,12 @@ export interface SteamGame {
name: string; name: string;
clientIcon: string | null; clientIcon: string | null;
} }
export type AppUpdaterEvents =
| { type: "error" }
| { type: "checking-for-updates" }
| { type: "update-not-available" }
| { type: "update-available"; info: UpdateInfo }
| { type: "update-downloaded" }
| { type: "download-progress"; info: ProgressInfo }
| { type: "update-cancelled" };