Compare commits

...

52 Commits

Author SHA1 Message Date
Chubby Granny Chaser
6c55d667bd chore: bump version 2025-05-10 19:38:03 +01:00
Chubby Granny Chaser
a4bdc3b5f0 Merge pull request #1671 from hydralauncher/feat/HYD-781
Feat/hyd 781
2025-05-10 19:06:30 +01:00
Chubby Granny Chaser
44dc8f73e8 feat: adding staging urls for ws 2025-05-10 18:54:55 +01:00
Chubby Granny Chaser
8b8ead531d ci: updating build process 2025-05-10 18:08:47 +01:00
Chubby Granny Chaser
ec40dfdb0b ci: adding sonar ignore 2025-05-10 17:49:03 +01:00
Chubby Granny Chaser
74d93da9b3 chore: adding protobuf ts as dev dependency 2025-05-10 17:46:17 +01:00
Chubby Granny Chaser
216f813771 feat: adding new friend session notification 2025-05-10 17:43:09 +01:00
Chubby Granny Chaser
fee9cfb3e8 Merge branch 'main' of github.com:hydralauncher/hydra into feat/HYD-781 2025-05-10 17:20:57 +01:00
Chubby Granny Chaser
dcd00cda98 Merge pull request #1662 from hydralauncher/feat/get-image-assets-from-api
feat: getting image assets from api [HYD-811]
2025-05-10 17:20:36 +01:00
Zamitto
c9135715fa feat: refactor 2025-05-09 18:59:32 -03:00
Zamitto
64cea7ff85 feat: simplify get user event 2025-05-09 18:50:14 -03:00
Zamitto
382a618c3f feat: refactor assets in game details page 2025-05-09 18:30:45 -03:00
Chubby Granny Chaser
d906e3f145 ci: updating build to support ws url 2025-05-09 20:55:29 +01:00
Chubby Granny Chaser
e987b27aec ci: updating build to support ws url 2025-05-09 20:53:53 +01:00
Chubby Granny Chaser
18815a027f ci: updating build to support ws url 2025-05-09 20:53:21 +01:00
Chubby Granny Chaser
6c44cc0cc4 ci: updating build to support ws url 2025-05-09 20:52:03 +01:00
Zamitto
171c728616 Merge branch 'main' into feat/get-image-assets-from-api 2025-05-09 16:03:37 -03:00
Chubby Granny Chaser
b4ff16cfa4 Merge pull request #1667 from hydralauncher/fix/HYD-807
fix: storing rpc encrypted password
2025-05-09 15:06:23 +01:00
Chubby Granny Chaser
eb6317e659 fix: adding fallback to language 2025-05-09 13:18:38 +01:00
Zamitto
8377f85f0b fix: undo isStaging change 2025-05-09 08:49:58 -03:00
Zamitto
10504cdaf8 Merge branch 'main' into feat/get-image-assets-from-api 2025-05-09 08:49:37 -03:00
Chubby Granny Chaser
a01c77b424 fix: storing rpc encrypted password 2025-05-09 10:29:25 +01:00
Zamitto
408adb566c fix: add game to library failing 2025-05-08 21:24:52 -03:00
Zamitto
311d4658bc chore: bump version 2025-05-08 15:31:20 -03:00
Zamitto
30601df677 Merge pull request #1663 from hydralauncher/fix/resetting-game-playtime-when-starting-download
fix: reseting game playtime when starting download [HYD-812]
2025-05-08 15:30:50 -03:00
Zamitto
4daead6b72 fix: resseting game playtime when starting download 2025-05-08 15:08:21 -03:00
Zamitto
cf818d0f4f feat: get game assets from stats 2025-05-08 09:19:12 -03:00
Zamitto
48e9536169 feat: adjust isLoading on game details context 2025-05-08 09:05:49 -03:00
Zamitto
00c589a138 feat: get image assets from api 2025-05-08 08:53:51 -03:00
Zamitto
30584492af feat: get image assets from api 2025-05-07 21:05:50 -03:00
Zamitto
b6c433dea9 chore: remove tag from release CI 2025-05-06 12:04:26 -03:00
Zamitto
cca94376c8 Merge pull request #1657 from hydralauncher/fix/game-not-being-readded-to-library-on-api
fix: game not being re added to library on api
2025-05-06 06:05:46 -03:00
Zamitto
30e7fe0e21 fix: game not being added to library on api 2025-05-05 18:58:54 -03:00
Zamitto
c4698d951b Merge pull request #1645 from hydralauncher/feat/create-start-menu-shortcut-on-windows
feat: create start menu shortcut on windows
2025-05-02 11:44:29 -03:00
Zamitto
3fe61ef36b feat: increase default toast duration 2025-05-02 11:40:08 -03:00
Zamitto
9d601fa1c8 chore: bump version 2025-05-02 08:09:29 -03:00
Zamitto
8cf4656d6d feat: update redist script 2025-05-02 08:09:10 -03:00
Zamitto
a922d9a166 feat: create start menu shortcut on Windows 2025-05-02 08:08:58 -03:00
Zamitto
f20314de57 Merge pull request #1632 from hydralauncher/feat/update-redist-files
feat: update redist files
2025-05-01 16:48:05 -03:00
Chubby Granny Chaser
762c8377b3 Merge branch 'main' into feat/update-redist-files 2025-05-01 20:10:34 +01:00
Zamitto
938364de47 docs: update README 2025-05-01 07:20:55 -03:00
Zamitto
590cea70e1 docs: update README and screenshot 2025-05-01 07:18:20 -03:00
Zamitto
f52f0ca614 feat: create SystemPath class to prevent Hydra not opening when some user folders are not correctly set on windows 2025-04-30 12:11:04 -03:00
Zamitto
6068d70aeb feat: update redist files 2025-04-30 12:08:47 -03:00
Chubby Granny Chaser
aa18b57ada feat: adding ws 2025-04-29 10:05:27 +01:00
Zamitto
aef309de25 Merge pull request #1629 from hydralauncher/fix/order-of-games-on-system-tray
fix: correctly sorting games on System Tray
2025-04-28 22:38:40 -03:00
Zamitto
6dea51c8e9 chore: bump version 2025-04-28 22:10:12 -03:00
Zamitto
aa83d38b85 chore: add tag_name to release ci 2025-04-28 22:09:58 -03:00
Zamitto
66094c0aa2 chore: test get package.json version 2025-04-28 22:01:55 -03:00
Zamitto
f2bd9df77f fix: use orderBy instead of sortBy to correctly sort games on system tray 2025-04-28 21:58:32 -03:00
Zamitto
7f4b5557de Merge pull request #1626 from Moyasee/main
Fix: Added Missing Translations to UK Language and fixed other strings
2025-04-28 14:11:04 -03:00
Moyase
cc4c9a352c Fix: Added Missing Translations to UK Language
Added missing translation for new option in settings. Added Translation for "Upload Banner", fixed translation for "Delete All Sources", Fixed translation for friend requests received and sent
2025-04-27 18:25:23 +03:00
96 changed files with 1171 additions and 3271 deletions

View File

@@ -1,4 +1,5 @@
MAIN_VITE_API_URL=API_URL
MAIN_VITE_AUTH_URL=AUTH_URL
MAIN_VITE_WS_URL=
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID=
RENDERER_VITE_TORBOX_REFERRAL_CODE=

View File

@@ -4,3 +4,4 @@ out
.gitignore
migration.stub
hydra-python-rpc/
src/main/generated/

View File

@@ -48,6 +48,7 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
MAIN_VITE_WS_URL: ${{ vars.MAIN_VITE_WS_STAGING_URL }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -63,6 +64,7 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
MAIN_VITE_WS_URL: ${{ vars.MAIN_VITE_WS_STAGING_URL }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -51,6 +51,7 @@ jobs:
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_WS_URL: ${{ vars.MAIN_VITE_WS_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
@@ -66,6 +67,7 @@ jobs:
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_WS_URL: ${{ vars.MAIN_VITE_WS_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "proto"]
path = proto
url = https://github.com/hydralauncher/hydra-protos.git

174
README.md
View File

@@ -1,5 +1,3 @@
<br>
<div align="center">
[<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
@@ -7,180 +5,28 @@
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra is a game launcher with its own embedded bittorrent client.</strong>
<strong>Hydra Launcher is an open-source gaming platform created to be the single tool that you need in order to manage your gaming library. Hydra is written in Node.js (Electron, React, Typescript) and Python.</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)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](./docs/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)](./docs/README.ru.md)
[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](./docs/README.uk-UA.md)
[![be](https://img.shields.io/badge/lang-be-orange)](./docs/README.be.md)
[![es](https://img.shields.io/badge/lang-es-red)](./docs/README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](./docs/README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](./docs/README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](./docs/README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](./docs/README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](./docs/README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](./docs/README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](./docs/README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](./docs/README.tr.md)
![Hydra Catalogue](./docs/screenshot.png)
![Hydra Launcher Home Page](./docs/screenshot.png)
</div>
## Table of Contents
- [Table of Contents](#table-of-contents)
- [About](#about)
- [Features](#features)
- [Installation](#installation)
- [Contributing](#-contributing)
- [Join our Telegram](#-join-our-telegram)
- [Fork and clone your repository](#fork-and-clone-your-repository)
- [Ways you can contribute](#ways-you-can-contribute)
- [Project Structure](#project-structure)
- [Build from source](#build-from-source)
- [Install Node.js](#install-nodejs)
- [Install Yarn](#install-yarn)
- [Install Node Dependencies](#install-node-dependencies)
- [Install Python 3.9](#install-python-39)
- [Install Python Dependencies](#install-python-dependencies)
- [Environment variables](#environment-variables)
- [Running](#running)
- [Build](#build)
- [Build the bittorrent client](#build-the-bittorrent-client)
- [Build the Electron application](#build-the-electron-application)
- [Contributors](#contributors)
- [License](#license)
## About
**Hydra** is a **Game Launcher** with its own embedded **BitTorrent Client**.
<br>
The launcher is written in TypeScript (Electron) and Python, which handles the torrenting system by using libtorrent.
## Features
- Own embedded bittorrent client
- How Long To Beat (HLTB) integration on game page
- Downloads path customization
- Windows and Linux support
- Constantly updated
- And more ...
- Add games that you own to your library
- Have a nice profile that shows what you are playing to your friends
- Save your game progress in the cloud with Hydra Cloud
- Unlock achievements
- Navigate through a rich catalogue with a powerful suggestion algorithm
- Discover new games that you haven't played before
## Installation
## Build from source and contributing
Follow the steps below to install:
1. Download the latest version of Hydra from the [Releases](https://github.com/hydralauncher/hydra/releases/latest) page.
- Download only .exe if you want to install Hydra on Windows.
- Download .deb or .rpm or .zip if you want to install Hydra on Linux. (depends on your Linux distro)
2. Run the downloaded file.
3. Enjoy Hydra!
## <a name="contributing"> Contributing
### <a name="join-our-telegram"></a> Join our Telegram
We concentrate our discussions on our [Telegram](https://t.me/hydralauncher) channel.
### Fork and clone your repository
1. Fork the repository [(click here to fork now)](https://github.com/hydralauncher/hydra/fork)
2. Clone your forked code `git clone https://github.com/your_username/hydra`
3. Create a new branch
4. Push your commits
5. Submit a new Pull Request
### Ways you can contribute
- Translation: We want Hydra to be available to as many people as possible. Feel free to help translate to new languages or update and improve the ones that are already available on Hydra.
- Code: Hydra is built with Typescript, Electron and a little bit of Python. If you want to contribute, join our [Telegram](https://t.me/hydralauncher)!
### Project Structure
- torrent-client: We use libtorrent, a Python library, to manage torrent downloads
- src/renderer: the UI of the application
- src/main: all the logic rests here.
## Build from source
### Install Node.js
Ensure you have Node.js installed on your machine. If not, download and install it from [nodejs.org](https://nodejs.org/).
### Install Yarn
Yarn is a package manager for Node.js. If you haven't installed Yarn yet, you can do so by following the instructions on [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Install Node Dependencies
Navigate to the project directory and install the Node dependencies using Yarn:
```bash
cd hydra
yarn
```
### Install OpenSSL 1.1
[OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) is required by libtorrent in Windows environments.
### 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-3913/).
### Install Python Dependencies
Install the required Python dependencies using pip:
```bash
pip install -r requirements.txt
```
## Environment variables
You'll need an SteamGridDB API Key in order to fetch the game icons on installation.
Once you have it, you can copy or rename the `.env.example` file to `.env` and put it on`STEAMGRIDDB_API_KEY`.
## Running
Once you've got all things set up, you can run the following command to start both the Electron process and the bittorrent client:
```bash
yarn dev
```
## Build
### Build the bittorrent client
Build the bittorrent client by using this command:
```bash
python torrent-client/setup.py build
```
### Build the Electron application
Build the Electron application by using this command:
On Windows:
```bash
yarn build:win
```
On Linux:
```bash
yarn build:linux
```
Please, refer to our Documentation pages: [docs.hydralauncher.gg](https://docs.hydralauncher.gg/getting-started)
## Contributors

View File

@@ -1,191 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra - гэта гульнявы лаўнчар з уласным убудаваным кліентам BitTorrent.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## Змест
- [Змест](#змест)
- [Апісанне](#апісанне)
- [Асаблівасці](#асаблівасці)
- [Усталёўка](#усталёўка)
- [Уклад](#-уклад)
- [Далучайцеся да нашага Telegram](#-далучайцеся-да-нашага-telegram)
- [Форк і кланаванне рэпазітара](#форк-і-кланаванне-рэпазітара)
- [Спосабы ўнесці свой уклад](#спосабы-ўнесці-свой-уклад)
- [Структура праекту](#структура-праекту)
- [Зборка з зыходнага коду](#зборка-з-зыходнага-коду)
- [Усталёўка Node.js](#усталёўка-nodejs)
- [Усталёўка Yarn](#усталёўка-yarn)
- [Усталёўка залежнасцяў Node](#усталёўка-залежнасцяў-node)
- [Усталёўка Python 3.9](#усталёўка-python-39)
- [Усталёўка залежнасцяў Python](#усталёўка-залежнасцяў-python)
- [Пераменныя асяроддзі](#пераменныя-асяроддзі)
- [Запуск](#запуск)
- [Зборка](#зборка)
- [Зборка кліента BitTorrent](#зборка-кліента-bittorrent)
- [Зборка прыкладання Electron](#зборка-прыкладання-electron)
- [Удзельнікі](#удзельнікі)
- [Ліцэнзія](#ліцэнзія)
## Апісанне
**Hydra** - гэта **гульнявы лаўнчар** з уласным убудаваным **кліентам BitTorrent** і **самастойным scraper`ом для рэпакаў**.
<br>
Лаўнчар напісаны на TypeScript (Electron) і Python, які кіруе сістэмай торэнтаў з дапамогай libtorrent.
## Асаблівасці
- Самастойны scraper рэпакаў сярод усіх найбольш надзейных вэб-сайтаў у [Megathread](https://www.reddit.com/r/Piracy/wiki/megathread/)
- Убудаваны кліент BitTorrent
- Інтэграцыя How Long To Beat (HLTB) на старонцы гульні
- Настройка шляху сцягвання
- Паведамленні аб абнаўленні спісу рэпакаў
- Падтрымка Windows і Linux
- Рэгулярныя абнаўленні
- І многае іншае...
## Усталёўка
Каб усталяваць, выканайце наступныя крокі:
1. Спампуйце апошнюю версію Hydra з [старонкі рэлізаў](https://github.com/hydralauncher/hydra/releases/latest).
- Загрузіце толькі .exe, калі жадаеце ўсталяваць Hydra на Windows.
- Загрузіце .deb ці .rpm ці .zip, калі жадаеце ўсталяваць Hydra на Linux (у залежнасці ад вашага дыстрыбутыва Linux).
2. Запусціце спампаваны файл.
3. Атрымлівайце асалоду ад Hydra!
## <a name="contributing"> Уклад
### <a name="join-our-telegram"></a> Далучайцеся да нашага Telegram
Мы засяроджваем нашы абмеркаванні ў нашым канале [Telegram](https://t.me/hydralauncher).
### Форк і кланаванне рэпазітара
1. Форкніце рэпазітар [(націсніце тут, каб зрабіць форк зараз)](https://github.com/hydralauncher/hydra/fork)
2. Склануйце свой форкнуты код `git clone https://github.com/ваше_имя_пользователя/hydra`
3. Стварыце новую галіну
4. Адпраўце свае каміты
5. Адпраўце Pull Request
### Спосабы ўнесці свой уклад
- Пераклад: Мы хочам, каб Hydra была даступная як мага большай колькасці людзей. Не саромейцеся дапамагаць перакладаць на новыя мовы ці абнаўляць і паляпшаць тыя, якія ўжо даступныя ў Hydra.
- Код: Hydra створаны з выкарыстаннем TypeScript, Electron і крыху Python. Калі хочаце ўнесці свой уклад, далучайцеся да нашага канала [Telegram](https://t.me/hydralauncher)!
### Структура праекту
- torrent-client: Мы выкарыстоўваем libtorrent, бібліятэку Python, для кіравання торэнт-загрузкамі.
- src/renderer: Карыстацкі інтэрфейс прыкладання.
- src/main: Увесь асноўны функцыянал тут.
## Зборка з зыходнага коду
### Усталёўка Node.js
Упэўніцеся, што ў вас усталяваны Node.js на вашым кампутары. Калі не, загрузіце і ўсталюйце яго з [nodejs.org](https://nodejs.org/).
### Усталёўка Yarn
Yarn - мэнэджэр пакетаў для Node.js. Калі вы яшчэ не ўсталявалі Yarn, зрабіце гэта, прытрымліваючыся інструкцыям на [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Усталёўка залежнасцяў Node
Перайдзіце ў каталог праекта і ўсталюйце залежнасці Node, выкарыстоўваючы Yarn:
```bash
cd hydra
yarn
```
### Усталёўка Python 3.9
Упэўніцеся, што ў вас усталяваны Python 3.9 на вашым кампутары. Вы можаце загрузіць і ўсталяваць яго з [python.org](https://www.python.org/downloads/release/python-3913/).
### Усталёўка залежнасцяў Python
Усталюйце неабходныя залежнасці Python, выкарыстоўваючы pip:
```bash
pip install -r requirements.txt
```
## Пераменныя асяроддзі
Вам спатрэбіцца ключ API SteamGridDB, каб атрымаць значкі гульняў пры ўсталёўкі.
Як толькі вы атрымаеце ключ, вы зможаце скапіяваць або пераназваць файл `.env.example` у `.env` і змясціць у яго `STEAMGRIDDB_API_KEY`.
## Запуск
Пасля таго як усё наладжана, вы можаце выканаць наступную каманду, каб запусціць працэс Electron і кліента BitTorrent:
```bash
yarn dev
```
## Зборка
### Зборка кліента BitTorrent
Збярыце кліент BitTorrent з дапамогай гэтай каманды:
```bash
python torrent-client/setup.py build
```
### Зборка прыкладання Electron
Збярыце прыкладанне Electron, выкарыстоўваючы гэтую каманду:
На Windows:
```bash
yarn build:win
```
На Linux:
```bash
yarn build:linux
```
## Удзельнікі
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Ліцэнзія
Hydra ліцэнзавана ў адпаведнасці з [MIT License](LICENSE).

View File

@@ -1,189 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra je herní launcher s vlastním vestavěným Bittorrent klientem.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Katalog](screenshot.png)
</div>
## Seznam obsahu
- [Seznam obsahu](#seznam-obsahu)
- [O projektu](#o-projektu)
- [Funkce](#funkce)
- [Instalace](#instalace)
- [Přispívání](#přispívání)
- [Připoj se na náš telegram](#připoj-se-na-náš-telegram)
- [Vytvořte fork a naklonujte svůj repozitář](#vytvořte-fork-a-naklonujte-svůj-repozitář)
- [Způsoby jak můžete přispět](#způsoby-jak-můžete-přispět)
- [Struktura projektu](#struktura-projektu)
- [Sestavení ze zdroje](#sestavení-ze-zdroje)
- [Instalace Node.js](#instalace-nodejs)
- [Instalace Yarn](#instalace-yarn)
- [Instalace Požadavků pro Node.js](#instalace-požadavků-pro-nodejs)
- [Instalace Pythonu 3.9](#instalace-pythonu-39)
- [Instalace Požadavků pro Python](#instalace-požadavků-pro-python)
- [Proměnné prostředí](#proměnné-prostředí)
- [Spuštění](#spuštění)
- [Sestavení](#sestavení)
- [Sestavení bittorrent klientu](#sestavení-bittorrent-klientu)
- [Sestavení electron aplikace](#sestavení-electron-aplikace)
- [Přispěvatelé](#přispěvatelé)
- [Licence](#licence)
## O projektu
**Hydra** je **Herní Launcher** s jeho vlastním vestavěným **BitTorrent Klientem**.
<br>
Launcher je napsán v TypeScriptu (Electron) a Pythonu, který má na starosti torrentovací systém za pomocí knihovny libtorrent.
## Funkce
- Vlastní vestavěný BitTorrent klient
- How Long To Beat (HLTB) integrace na stránce hry
- Vlastní místa pro uložení hry
- Windows a Linux podpora
- Časté aktualizace
- A další ...
## Instalace
Následuj kroky:
1. Stáhni nejnovější verzi Hydry ze stránky [Vydání](https://github.com/hydralauncher/hydra/releases/latest).
- Stáhni .exe, pokud chceš instalovat Hydru na Windows.
- Stáhni .deb nebo .rpm nebo .zip, pokud chceš instalovat Hydru na Linux. (záleží na tvé Linux distribuci)
2. Spusť stažený instalační soubor.
3. Užívej Hydru!
## <a name="contributing"> Přispívání
### <a name="join-our-telegram"></a> Připoj se na náš telegram
Vedeme diskuzi v našem [Telegramovém](https://t.me/hydralauncher) kanálu.
### Vytvořte fork a naklonujte svůj repozitář
1. Vytvoř fork repozitáře [(klikni sem pro vytvoření forku)](https://github.com/hydralauncher/hydra/fork)
2. Naklonuj kód forku `git clone https://github.com/tvoje_jméno/hydra`
3. Vytvoř nové odvětví (branch)
4. Odešli svoje změny
5. Odešli nový Pull Request
### Způsoby jak můžete přispět
- Překládání: Chceme, aby Hydra byla co nejvíce dostupná. Můžete přispět novým jazykem, nebo úpravou současného!
- Kód: Hydra je postavena na Typescriptu, Electronu a trochou Pythonu. Pokud chceš přispět, připoj se na náš [Telegram](https://t.me/hydralauncher)!
### Struktura projektu
- torrent-client: Používáme libtorrent, Pythonovou knihovnu, pro správu torrent stahování
- src/renderer: uživatelské rozhraní aplikace (UI)
- src/main: celá logika projektu
## Sestavení ze zdroje
### Instalace Node.js
Ujistěte se, že máte Node.js nainstalován na svém zařízení. Pokud ne, stáhněte ho, a nainstalujte z [nodejs.org](https://nodejs.org/).
### Instalace Yarn
Yarn je balíčkový správce pro Node.js. Pokud ještě nemáte yarn, můžete ho stáhnout za pomoci pokynů na [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Instalace Požadavků pro Node.js
Jděte do složky projektu, otevřte v ní konzole a nainstalujte požadavky pro Node pomocí Yarn:
```bash
cd hydra
yarn
```
### Instalace Pythonu 3.9
Ujistěte se, že máte Python 3.9 nainstalován na svém zařízení. Můžete ho stáhnout z [python.org](https://www.python.org/downloads/release/python-3913/).
### Instalace Požadavků pro Python
Nainstalujte požadavky pro Python za pomoci pip:
```bash
pip install -r requirements.txt
```
## Proměnné prostředí
Budete potřebovat SteamGridDB API klíč, abyste mohli načítat ikony u her.
Jakmile ho máte, můžete zkopírovat, nebo přejmenovat `.env.example` soubor na `.env` a dát ho do `STEAMGRIDDB_API_KEY`.
## Spuštění
Jakmile máte vše nastaveno, můžete spustit jak Electron proces tak bittorrent client:
```bash
yarn dev
```
## Sestavení
### Sestavení bittorrent klientu
Sestavit bittorrent klient můžete pomocí:
```bash
python torrent-client/setup.py build
```
### Sestavení electron aplikace
Sestavit Electron aplikaci můžete pomocí následujících kroků:
Na Windows:
```bash
yarn build:win
```
Na Linux:
```bash
yarn build:linux
```
## Přispěvatelé
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Licence
Hydra je licencována pod [MIT Licencí](LICENSE).

View File

@@ -1,188 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra er en spil launcher med sin egen indbyggede bittorrent klient.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## Indholdsfortegnelse
- [Indholdsfortegnelse](#indholdsfortegnelse)
- [Om](#om)
- [Funktioner](#funktioner)
- [Installation](#installation)
- [Bidrag](#-bidrag)
- [Bliv medlem af vores Telegram kanal](#-join-our-telegram)
- [Fork og klon dit repo](#fork-and-clone-your-repository)
- [Måder du kan bidrage](#ways-you-can-contribute)
- [Projekt Struktur](#project-structure)
- [Byg fra kildekode](#build-from-source)
- [Installér Node.js](#install-nodejs)
- [Installér Yarn](#install-yarn)
- [Installér Node Afhængigheder](#install-node-dependencies)
- [Installér Python 3.9](#install-python-39)
- [Installér Python Afhængigheder](#install-python-dependencies)
- [Miljøvariabler](#environment-variables)
- [Køre](#running)
- [Bygge](#build)
- [Bygge bittorrent klienten](#build-the-bittorrent-client)
- [Bygge Electron applikationen](#build-the-electron-application)
- [Bidragere](#contributors)
- [Licens](#license)
## Om
**Hydra** er en **Spil Launcher** med sin egen indbyggede **BitTorrent Klient**.
<br>
Launcheren er skrevet i TypeScript (Electron) og Python, som håndterer torrenting system ved brug af libtorrent.
## Funktioner
- Sin egen indbyggede bittorrent klient
- How Long To Beat (HLTB) integration på spil siden
- Downloadsti tilpasning
- Windows og Linux understøttelse
- Konstant opdateret
- Og mere ...
## Installation
Følg trinene her under for at installere:
1. Download den seneste version af Hydra fra [Releases](https://github.com/hydralauncher/hydra/releases/latest) siden.
- Download kun .exe hvis du vil installere Hydra på Windows.
- Download .deb, .rpm eller .zip hvis du vil installere Hydra på Linux. (afhænger af din Linux distro)
2. Kør den downloadede fil.
3. Nyd Hydra!
## <a name="bidrag"> Bidrag
### <a name="join-our-telegram"></a> Bliv medlem af vores Telegram kanal
Vi holder vores diskusioner i vores [Telegram](https://t.me/hydralauncher) kanal.
### Fork og klon dit repo
1. Fork repoet [(klik her for at forke nu)](https://github.com/hydralauncher/hydra/fork)
2. Klon din forkede kode `git clone https://github.com/dit_brugernavn/hydra`
3. Lav en ny branch
4. Skub dine commits
5. Indsend en ny Pull Request
### Måder du kan bidrage
- Oversættelse: Vi vil gerne have at Hydra er tilgængeligt for så mange folk som overhovedet muligt. Du er velkommen til at hjælpe med at oversætte til nye sprog eller at opdatere og forbedre de sprog som allerede er tilgængelige i Hydra.
- Kode: Hydra er lavet med Typescript, Electron og en lille smule Python. Hvis du har lyst til at bidrage, kan du blive medlem af vores [Telegram](https://t.me/hydralauncher) kanal! (Alt kommunikation foregår hovedsageligt på Engelsk, Brasiliansk eller Russisk)
### Projekt struktur
- torrent-client: Vi bruger libtorrent, et Python bibliotek, til at administrere torrent downloads
- src/renderer: UI'en i applikationen
- src/main: her har vi al logikken
## Byg fra kildekode
### Installér Node.js
Vær sikker på at du har Node.js installeret på din maskine. Hvis ikke, kan du downloade og installere det fra [nodejs.org](https://nodejs.org/).
### Installér Yarn
Yarn er et pakkehåndteringsprogram til Node.js. Hvis du ikke har installeret Yarn endnu, så kan du gøre det ved at følge instruktionerne på [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Installér Node Afhængigheder
Navigér til projekt mappen og installér Node afhængighederne ved bruge af Yarn:
```bash
cd hydra
yarn
```
### Installér Python 3.9
Vær sikker på at du har Python 3.9 installeret på din maskine. Du kan downloade og installere det her: [python.org](https://www.python.org/downloads/release/python-3913/).
### Installér Python Afhængigheder
Installér de påkrævede Python afhængigheder ved brug af pip:
```bash
pip install -r requirements.txt
```
## Miljøvariabler
Du får brug for en SteamGridDB API nøgle for at kunne hente spil ikonerne under installationen.
Når du har det, kan du kopiere og omdøbe `.env.example` filen til `.env` og indsætte nøglen som `STEAMGRIDDB_API_KEY`.
## Køre
Når alt er sat op, kan du køre den følgende kommando for at starte både Electron processen og bittorrent klienten:
```bash
yarn dev
```
## Bygge
### Byg bittorrent klienten
Byg bittorrent klienten ved brug af følgende kommando:
```bash
python torrent-client/setup.py build
```
### Byg Electron applikationen
Byg Electron applikationen ved brug af følgende kommando:
På Windows:
```bash
yarn build:win
```
På Linux:
```bash
yarn build:linux
```
## Bidragere
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Licens
Hydra benytter sig af [MIT Licensen](LICENSE).

View File

@@ -1,187 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra ist ein Launcher für Spiele mit einem eigenen eingebetteten BitTorrent-Client.</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)
[![en](https://img.shields.io/badge/lang-en-red.svg)](../README.md)
[![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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Katalog](screenshot.png)
</div>
## Inhaltsverzeichnis
- [Über Hydra](#über-hydra)
- [Eigenschaften](#eigenschaften)
- [Installation](#installation)
- [Mitwirken](#mitwirken)
- [Tritt uns auf Telegram bei](#tritt-uns-auf-telegram-bei)
- [Forke und klone dein Repo](#forke-und-klone-dein-repo)
- [Wie du mitwirken kannst](#wie-du-mitwirken-kannst)
- [Projektstruktur](#projektstruktur)
- [Den Quellcode kompilieren](#den-quellcode-kompilieren)
- [Installiere Node.js](#installiere-nodejs)
- [Installiere Yarn](#installiere-yarn)
- [Installiere Node-Abhängigkeiten](#installiere-node-abhängigkeiten)
- [Installiere Python 3.9](#installiere-python-39)
- [Installiere Python-Abhängigkeiten](#installiere-python-abhängigkeiten)
- [Umgebungsvariablen](#umgebungsvariablen)
- [Ausführung](#ausführung)
- [Kompilation](#kompilation)
- [Kompiliere den BitTorrent-Client](#kompiliere-den-bittorrent-client)
- [Kompiliere die Electron-Applikation](#kompiliere-die-electron-applikation)
- [Mitwirkende](#mitwirkende)
## Über Hydra
**Hydra** ist ein **Launcher für Spiele** mit einem eigenen eingebetteten **BitTorrent-Client**.
<br>
Der Launcher ist in TypeScript (Electron) und Python, womit das Torrentingsystem durch Einsatz von libtorrent geregelt ist, geschrieben.
## Eigenschaften
- Eigener eingebetteter BitTorrent-Client
- How Long to Beat (HLTB) Integration auf der Spielseite
- Anpassbarkeit des Downloadverzeichnisses
- Unterstützung von Windows und Linux
- Regelmäßig aktualisiert
- Und mehr ...
## Installation
Die folgenden Schritte beschreiben den Installationsprozess:
1. Lade die neueste Version von Hydra von der [Releases](https://github.com/hydralauncher/hydra/releases/latest) Seite herunter.
- Für die Installation von Hydra auf Windows, wähle die .exe Datei.
- Für die Installation von Hydra auf Linux, wähle die .deb, .rpm oder .zip Datei. (Abhängig von deiner Linux-Distribution)
2. Führe die heruntergeladene Datei aus.
3. Genieße Hydra!
## Mitwirken
### Tritt uns auf Telegram bei
Wir konzentrieren unsere Diskussionen in unserem [Telegram](https://t.me/hydralauncher) Kanal.
### Forke und klone dein Repo
1. Forke das Repo [(Klicke hier, um direkt zu forken)](https://github.com/hydralauncher/hydra/fork)
2. Klone deinen geforketen Code `git clone https://github.com/dein_nutzername/hydra`
3. Erstelle einen neuen Branch
4. Pushe deine Commits
5. Stelle eine neue Pull-Anfrage
### Wie du mitwirken kannst
- Übersetzung: Wir wollen Hydra so vielen Menschen wie möglich zugänglich machen. Gerne kannst du uns helfen neue Sprachen zu übersetzen oder für Hydra bereits verfügbare Sprachen zu aktualisieren und verbessern.
- Code: Hydra ist mit TypeScript, Electron und etwas Python gebaut. Wenn du mitwirken möchtest, tritt unserem [Telegram](https://t.me/hydralauncher) bei!
### Projektstruktur
- torrent-client: Wir verwenden die Python-Bibliothek libtorrent zur Verwaltung von Torrent-Downloads.
- src/renderer: die UI der Applikation.
- src/main: sämtliche Logik liegt hier.
## Den Quellcode kompilieren
### Installiere Node.js
Stelle sicher, dass du Node.js auf deinem System installiert hast. Falls nicht, installiere es von [nodejs.org](https://nodejs.org/).
### Installiere Yarn
Yarn ist ein Packetmanager für Node.js. Sollte er dir fehlen, installiere ihn mithilfe der Anleitung auf [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Installiere Node-Abhängigkeiten
Navigiere zum Projektverzeichnis und installiere die Node-Abhängigkeiten mit Yarn:
```bash
cd hydra
yarn
```
### Installiere Python 3.9
Stelle sicher, dass du Python 3.9 auf deinem System installiert hast. Ansonsten kannst du es von [python.org](https://www.python.org/downloads/release/python-3913/) herunterladen und installieren.
### Installiere Python-Abhängigkeiten
Installiere die benötigten Python-Abhängigkeiten mit pip:
```bash
pip install -r requirements.txt
```
## Umgebungsvariablen
Du wirst einen SteamGridDB API Schlüssel benötigen, um die Spielicons bei Installation abzurufen.
Sobald du einen hast, kannst du die .env.example Datei zu .env kopieren oder umbenennen und den Schlüssel bei STEAMGRIDDB_API_KEY einfügen.
## Ausführung
Sobald du alles eingerichtet hast, kannst du den folgenden Befehl nutzen, um sowohl den Electron-Prozess als auch den BitTorrent-Client zu starten:
```bash
yarn dev
```
## Kompilation
### Kompiliere den BitTorrent-Client
Kompiliere den BitTorrent-Client mit folgendem Befehl:
```bash
python torrent-client/setup.py build
```
### Kompiliere die Electron-Applikation
Kompiliere die Electron-Applikation mit folgendem Befehl:
Auf Windows:
```bash
yarn build:win
```
Auf Linux:
```bash
yarn build:linux
```
## Mitwirkende
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Lizenz
Hydra ist unter der [MIT Lizenz](LICENSE) lizensiert.

View File

@@ -1,189 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra es un launcher de juegos con su propio cliente de bittorrent.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## Tabla de Contenidos
- [Tabla de Contenidos](#tabla-de-contenidos)
- [Acerca de](#acerca-de)
- [Caracteristicas](#caracteristicas)
- [Instalacion](#instalacion)
- [Contribuir](#-contribuir)
- [Unete a nuestro Telegram](#-unete-a-nuestro-telegram)
- [Haz un fork y clona tu repositorio](#haz-un-fork-y-clona-tu-repositorio)
- [Maneras en las que puedes contribuir](#maneras-en-las-que-puedes-contribuir)
- [Estructura del proyecto](#estructura-del-proyecto)
- [Compilar desde el código fuente](#compilar-desde-el-código-fuente)
- [Instalar Node.js](#instalar-nodejs)
- [Instalar Yarn](#instalar-yarn)
- [Instalar Dependencias de Node](#instalar-dependencias-de-node)
- [Instalar Python 3.9](#instalar-python-39)
- [Instalar Dependencias de Python](#instalar-dependencias-de-python)
- [Variables del Entorno](#variables-del-entorno)
- [Ejecucion](#ejecucion)
- [Compilacion](#compilacion)
- [Compilar el cliente de bittorrent](#compilar-el-cliente-de-bittorrent)
- [Compilar la aplicacion Electron](#compilar-la-aplicacion-electron)
- [Colaboradores](#colaboradores)
- [Licencia](#licencia)
## Acerca de
**Hydra** es un **Launcher de Juegos** con su propio **Cliente Bittorrent**.
<br>
El launcher está escrito en TypeScript (Electron) y Python, el cuál se encarga del sistema de torrent usando libtorrent.
## Caracteristicas
- Cliente propio de bittorrent integrado
- Integración de How Long To Beat (HLTB) en la página del juego
- Customización de rutas de descargas
- Soporte a Windows y Linux
- En constante actualización
- Y mucho más ...
## Instalacion
Sigue los pasos de abajo para instalar:
1. Descarga la última versión de Hydra desde la página de [Releases](https://github.com/hydralauncher/hydra/releases/latest).
- Descarga solo el .exe si quieres instalar Hydra en Windows.
- Descarga el .deb o .rpm o .zip si quieres instalar Hydra en Linux. (Depende de tu distro de Linux)
2. Ejecuta el archivo descargado.
3. ¡Disfruta de Hydra!
## <a name="contribuir"> Contribuir
### <a name="unete-a-nuestro-telegram"></a> Unete a nuestro Telegram
Puedes unirte a nuestra conversación y discusiones en nuestro canal de [Telegram](https://t.me/hydralauncher).
### Haz un fork y clona tu repositorio
1. Realiza un fork del repositorio [(Haz click acá para hacer un fork ahora)](https://github.com/hydralauncher/hydra/fork)
2. Clona el código forkeado `git clone https://github.com/tu_nombredeusuario/hydra`
3. Crea una nueva rama
4. Sube tus commits
5. Envía nuevas solicitudes de pull
### Maneras en las que puedes contribuir
- Traducción: Queremos que Hydra esté disponible para todas las personas que sean posible. Siéntete libre de ayudarnos a traducirlo a nuevos lenguajes o actualizar y mejorar las ya disponibles en Hydra.
- Código: Hydra está hecho con Typescript, Electron y un poquito de Python. Si quieres contribuir, ¡únete a nuestro [Telegram](https://t.me/hydralauncher)!
### Estructura del proyecto
- torrent-client: Usamos libtorrent, una librería de Python que se encarga de manejar las descargas torrent
- src/renderer: El UI de la aplicación
- src/main: El resto de la lógica va acá.
## Compilar desde el código fuente
### Instalar Node.js
Asegúrate que tienes Node.js instalado en tú máquina. Si no es así, puedes descargarlo e instalarlo desde [nodejs.org](https://nodejs.org/).
### Instalar Yarn
Yarn es un gestor de paquetes para Node.js. Si no tienes aún instalado Yarn todavía, puedes hacerlo siguiendo las instrucciones en [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Instalar Dependencias de Node
Dirígete hasta el directorio del proyecto e instala las dependencias de Node usando Yarn:
```bash
cd hydra
yarn
```
### Instalar Python 3.9
Asegúrate que tienes Python 3.9 instalado en tu máquina. Puedes descargarlo e instalarlo desde [python.org](https://www.python.org/downloads/release/python-3913/).
### Instalar Dependencias de Python
Instala las dependencias de Python requeridas usando pip:
```bash
pip install -r requirements.txt
```
## Variables del Entorno
Necesitas una llave API de SteamGridDB para así poder obtener los íconos de los juegos en la instalación.
Una vez que los tengas, puedes copiar o renombrar el archivo `.env.example` cómo `.env` y colocarlo en `STEAMGRIDDB_API_KEY`.
## Ejecucion
Una vez que tengas todas las cosas listas, puedes ejecutar el siguiente comando para así iniciar el proceso de Electron y el cliente de bittorrent:
```bash
yarn dev
```
## Compilacion
### Compilar el cliente de bittorrent
Crea el cliente bittorrent usando este comando:
```bash
python torrent-client/setup.py build
```
### Compilar la aplicacion Electron
Crea la aplicación de Electron usando este comando:
En Windows:
```bash
yarn build:win
```
En Linux:
```bash
yarn build:linux
```
## Colaboradores
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Licencia
Hydra está licenciado bajo la [MIT License](LICENSE).

View File

@@ -1,187 +0,0 @@
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra on mängulauncher oma sisseehitatud bittorrenti kliendiga.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Kataloog](screenshot.png)
</div>
## Sisukord
- [Sisukord](#sisukord)
- [Tutvustus](#tutvustus)
- [Funktsioonid](#funktsioonid)
- [Paigaldamine](#paigaldamine)
- [Panustamine](#panustamine)
- [Liitu meie Telegramiga](#liitu-meie-telegramiga)
- [Forki ja klooni oma repositoorium](#forki-ja-klooni-oma-repositoorium)
- [Viisid panustamiseks](#viisid-panustamiseks)
- [Projekti Struktuur](#projekti-struktuur)
- [Lähtekoodi kompileerimine](#lähtekoodi-kompileerimine)
- [Node.js paigaldamine](#nodejs-paigaldamine)
- [Yarn'i paigaldamine](#yarni-paigaldamine)
- [Node sõltuvuste paigaldamine](#node-sõltuvuste-paigaldamine)
- [Python 3.9 paigaldamine](#python-39-paigaldamine)
- [Python'i sõltuvuste paigaldamine](#pythoni-sõltuvuste-paigaldamine)
- [Keskkonna muutujad](#keskkonna-muutujad)
- [Käivitamine](#käivitamine)
- [Kompileerimine](#kompileerimine)
- [Bittorrenti kliendi kompileerimine](#bittorrenti-kliendi-kompileerimine)
- [Electron rakenduse kompileerimine](#electron-rakenduse-kompileerimine)
- [Panustajad](#panustajad)
- [Litsents](#litsents)
## Tutvustus
**Hydra** on **Mängulauncher** oma sisseehitatud **BitTorrent Kliendiga**.
<br>
Launcher on kirjutatud TypeScriptis (Electron) ja Pythonis, mis haldab torrentide süsteemi kasutades libtorrenti.
## Funktsioonid
- Sisseehitatud bittorrenti klient
- How Long To Beat (HLTB) integratsioon mängu lehel
- Allalaadimiste kausta kohandamine
- Windowsi ja Linuxi tugi
- Pidevad uuendused
- Ja palju muud ...
## Paigaldamine
Järgi paigaldamiseks järgmisi samme:
1. Lae alla Hydra uusim versioon [Releases](https://github.com/hydralauncher/hydra/releases/latest) lehelt.
- Lae alla ainult .exe fail, kui soovid paigaldada Hydrat Windowsile.
- Lae alla .deb või .rpm või .zip fail, kui soovid paigaldada Hydrat Linuxile. (sõltub sinu Linuxi distrost)
2. Käivita allalaaditud fail.
3. Naudi Hydrat!
## Panustamine
### Liitu meie Telegramiga
Me keskendume aruteludele meie [Telegrami](https://t.me/hydralauncher) kanalis.
### Forki ja klooni oma repositoorium
1. Forki repositoorium [(klõpsa siia forkimiseks)](https://github.com/hydralauncher/hydra/fork)
2. Klooni oma forkitud kood `git clone https://github.com/your_username/hydra`
3. Loo uus haru
4. Pushi oma commitid
5. Esita uus Pull Request
### Viisid panustamiseks
- Tõlkimine: Me soovime, et Hydra oleks kättesaadav võimalikult paljudele inimestele. Võid aidata tõlkida uutesse keeltesse või uuendada ja parandada juba olemasolevaid tõlkeid Hydras.
- Kood: Hydra on ehitatud kasutades TypeScripti, Electroni ja natuke Pythonit. Kui soovid panustada, liitu meie [Telegramiga](https://t.me/hydralauncher)!
### Projekti Struktuur
- torrent-client: Kasutame libtorrenti, Pythoni teeki, torrentide allalaadimiste haldamiseks
- src/renderer: rakenduse kasutajaliides
- src/main: kogu loogika asub siin.
## Lähtekoodi kompileerimine
### Node.js paigaldamine
Veendu, et Node.js on sinu arvutisse paigaldatud. Kui ei ole, lae alla ja paigalda see [nodejs.org](https://nodejs.org/) lehelt.
### Yarn'i paigaldamine
Yarn on Node.js paketihaldur. Kui sa pole Yarni veel paigaldanud, saad seda teha järgides juhiseid [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/) lehel.
### Node sõltuvuste paigaldamine
Liigu projekti kausta ja paigalda Node sõltuvused kasutades Yarni:
```bash
cd hydra
yarn
```
### Python 3.9 paigaldamine
Veendu, et Python 3.9 on sinu arvutisse paigaldatud. Saad selle alla laadida ja paigaldada [python.org](https://www.python.org/downloads/release/python-3913/) lehelt.
### Python'i sõltuvuste paigaldamine
Paigalda vajalikud Pythoni sõltuvused kasutades pip'i:
```bash
pip install -r requirements.txt
```
## Keskkonna muutujad
Sul on vaja SteamGridDB API võtit, et laadida alla mängude ikoone paigaldamisel.
Kui sul on see olemas, saad kopeerida või ümber nimetada `.env.example` faili `.env` failiks ja lisada sinna `STEAMGRIDDB_API_KEY`.
## Käivitamine
Kui kõik on seadistatud, saad käivitada järgmise käsu, et käivitada nii Electroni protsess kui ka bittorrenti klient:
```bash
yarn dev
```
## Kompileerimine
### Bittorrenti kliendi kompileerimine
Kompileeri bittorrenti klient kasutades järgmist käsku:
```bash
python torrent-client/setup.py build
```
### Electron rakenduse kompileerimine
Kompileeri Electron rakendus kasutades järgmist käsku:
Windowsil:
```bash
yarn build:win
```
Linuxil:
```bash
yarn build:linux
```
## Panustajad
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Litsents
Hydra on litsentseeritud [MIT Litsentsi](LICENSE) all.

View File

@@ -1,189 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra est un lanceur de jeux avec son propre client bittorrent intégré.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Catalogue Hydra](screenshot.png)
</div>
## Table des Matières
- [Table des Matières](#table-des-matières)
- [À propos](#à-propos)
- [Fonctionnalités](#fonctionnalités)
- [Installation](#installation)
- [Contribuer](#contribuer)
- [Rejoindre notre Telegram](#rejoindre-notre-telegram)
- [Fork et Cloner votre dépôt](#fork-et-cloner-votre-dépôt)
- [Manières de contribuer](#manières-de-contribuer)
- [Structure du projet](#structure-du-projet)
- [Compiler depuis les sources](#compiler-depuis-les-sources)
- [Installer Node.js](#installer-nodejs)
- [Installer Yarn](#installer-yarn)
- [Installer les dépendances Node](#installer-les-dépendances-node)
- [Installer Python 3.9](#installer-python-39)
- [Installer les dépendances Python](#installer-les-dépendances-python)
- [Variables d'environnement](#variables-denvironnement)
- [Lancement](#lancement)
- [Compilation](#compilation)
- [Compiler le client bittorrent](#compiler-le-client-bittorrent)
- [Compiler l'application Electron](#compiler-lapplication-electron)
- [Contributeurs](#contributeurs)
- [License](#license)
## À propos
**Hydra** est un **lanceur de jeux** avec son propre **client BitTorrent** intégré.
<br>
Le lanceur est écrit en TypeScript (Electron) et Python, qui gère le système de torrent en utilisant libtorrent.
## Fonctionnalités
- Client bittorrent intégré
- Intégration How Long To Beat (HLTB) sur la page du jeu
- Personnalisation des chemins de téléchargement
- Support pour Windows et Linux
- Constamment mis à jour
- Et plus encore ...
## Installation
Suivez les étapes ci-dessous pour installer :
1. Téléchargez la dernière version de Hydra depuis la page [Releases](https://github.com/hydralauncher/hydra/releases/latest).
- Téléchargez uniquement le .exe si vous voulez installer Hydra sur Windows.
- Téléchargez .deb ou .rpm ou .zip si vous voulez installer Hydra sur Linux (cela dépend de votre distribution Linux).
2. Exécutez le fichier téléchargé.
3. Profitez de Hydra !
## Contribuer
### Rejoindre notre Telegram
Nous concentrons nos discussions sur notre [Telegram](https://t.me/hydralauncher).
### Fork et Cloner votre dépôt
1. Forkez le dépôt [(cliquez ici pour forker maintenant)](https://github.com/hydralauncher/hydra/fork)
2. Clonez votre code forké `git clone https://github.com/votre_nom_utilisateur/hydra`
3. Créez une nouvelle branche
4. Pushez vos commits
5. Créez une nouvelle Pull Request
### Manières de contribuer
- Traduction : Nous voulons que Hydra soit disponible pour le plus grand nombre de personnes possible. N'hésitez pas à aider à traduire dans de nouvelles langues ou à mettre à jour et améliorer celles qui sont déjà disponibles sur Hydra.
- Code : Hydra est construit avec Typescript, Electron et un peu de Python. Si vous voulez contribuer, rejoignez notre [Telegram](https://t.me/hydralauncher) !
### Structure du projet
- torrent-client : Nous utilisons libtorrent, une bibliothèque Python, pour gérer les téléchargements torrent.
- src/renderer : l'interface utilisateur de l'application.
- src/main : toute la logique repose ici.
## Compiler depuis les sources
### Installer Node.js
Assurez-vous que Node.js est installé sur votre machine. Sinon, téléchargez et installez-le depuis [nodejs.org](https://nodejs.org/).
### Installer Yarn
Yarn est un gestionnaire de paquets pour Node.js. Si vous n'avez pas encore installé Yarn, vous pouvez le faire en suivant les instructions sur [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Installer les dépendances Node
Naviguez vers le répertoire du projet et installez les dépendances Node en utilisant Yarn :
```bash
cd hydra
yarn
```
### Installer Python 3.9
Assurez-vous que Python 3.9 est installé sur votre machine. Vous pouvez le télécharger et l'installer depuis [python.org](https://www.python.org/downloads/release/python-3913/).
### Installer les dépendances Python
Installez les dépendances Python requises en utilisant pip :
```bash
pip install -r requirements.txt
```
## Variables d'environnement
Vous aurez besoin d'une clé API SteamGridDB pour récupérer les icônes de jeux lors de l'installation.
Une fois que vous l'avez, vous pouvez copier ou renommer le fichier `.env.example` en `.env` et y mettre `STEAMGRIDDB_API_KEY`.
## Lancement
Une fois que vous avez tout configuré, vous pouvez exécuter la commande suivante pour démarrer à la fois le processus Electron et le client bittorrent :
```bash
yarn dev
```
## Compilation
### Compiler le client bittorrent
Compilez le client bittorrent en utilisant cette commande :
```bash
python torrent-client/setup.py build
```
### Compiler l'application Electron
Compilez l'application Electron en utilisant cette commande :
Sur Windows :
```bash
yarn build:win
```
Sur Linux :
```bash
yarn build:linux
```
## Contributeurs
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## License
Hydra est sous [License MIT](LICENSE).

View File

@@ -1,189 +0,0 @@
<br>
<div align="center">
[<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra è un game launcher con il proprio client bittorrent.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png)
</div>
## Table of Contents
- [Table of Contents](#table-of-contents)
- [A proposito](#a-proposito)
- [Caratteristiche](#caratteristiche)
- [Installazione](#installazione)
- [Contribuire](#-contribuire)
- [Unisciti su Telegram](#-unisciti-su-telegram)
- [Forka e Clona la repository](#forka-e-clona-la-repository)
- [Modi in cui contribuire](#modi-in-cui-contribuire)
- [Struttura del Progetto](#struttura-del-progetto)
- [Compilazione](#compilazione)
- [Installa Node.js](#installa-nodejs)
- [Installa Yarn](#installa-yarn)
- [Installa le dipendenze Node](#installa-le-dipendenze-node)
- [Installa Python 3.9](#installa-python-39)
- [Installa le Dipendenze Python](#installa-le-dipendenze-python)
- [Variabili d'ambiente](#variabili-dambiente)
- [Esecuzione](#esecuzione)
- [Compilazione](#compilazione-1)
- [Compila il bittorrent](#compila-il-bittorrent)
- [Compila l'applicazione Electron](#compila-lapplicazione-electron)
- [Collaboratori](#collaboratori)
- [Licenza](#licenza)
## A proposito
**Hydra** è un **Game Launcher** con il proprio **Client BitTorrent**.
<br>
Il launcher è scritto in TypeScript (Electron) and Python, che gestisce il sistema di torrenting appoggiandosi a libtorrent.
## Caratteristiche
- Client Bittorrent integrato
- Integrazione How Long To Beat (HLTB) nella pagina del gioco
- Percorso del download Personalizzato
- Supporto Windows e Linux
- Costantemente Aggiornato
- E molto altro ...
## Installazione
Segui i seguenti passi:
1. Scarica l'ultima versione di Hydra dalla pagina [Releases](https://github.com/hydralauncher/hydra/releases/latest).
- Scarica solo il file .exe per installare Hydra su Windows.
- Scarica il file .deb o .rpm o .zip per Linux. (Dipende dalla tua distro Linux)
2. Esegui il file scaricato.
3. Goditi Hydra!
## <a name="contribuire"> Contribuire
### <a name="unisciti-su-telegram"></a> Unisciti su Telegram
Puoi unirti alle nostre conversazioni sul canale [Telegram](https://t.me/hydralauncher).
### Forka e Clona la repository
1. Forka la repository [(clicca qui per forkare)](https://github.com/hydralauncher/hydra/fork)
2. Clona il tuo codice forkato `git clone https://github.com/your_username/hydra`
3. Crea un nuovo branch
4. Aggiungi le modifiche (push)
5. Invia la richiesta di pull
### Modi in cui contribuire
- Traduzione: Vogliamo rendere Hydra disponibile a più persone possibile. Sentiti libero di tradurre in altre lingue o aggiornare e migliorare quelle già disponibili su Hydra.
- Programmazione: Hydra è programmato in TypeScript, Electron e un po' di Python. Se intendi contribuire unisciti al nostro [Telegram](https://t.me/hydralauncher)!
### Struttura del Progetto
- client-torrent: Usiamo libtorrent, una libreria Python, per gestire i download dei torrent
- src/renderer: l'UI dell'applicazione
- src/main: tutta la logica qui.
## Compilazione
### Installa Node.js
Assicurati di avere Node.js installato sulla tua macchina. Scaricalo e installalo da [nodejs.org](https://nodejs.org/).
### Installa Yarn
Yarn è un gestore di pacchetti per Node.js. Se non hai ancora installato Yarn segui le istruzioni su [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Installa le dipendenze Node
Naviga alla cartella del progetto e installa le dipendenze Node con Yarn:
```bash
cd hydra
yarn
```
### Installa Python 3.9
Assicurati di avere Python 3.9 installato. Puoi scaricarlo da [python.org](https://www.python.org/downloads/release/python-3913/).
### Installa le Dipendenze Python
Installa le dipendenze con pip:
```bash
pip install -r requirements.txt
```
## Variabili d'ambiente
Avrai bisogno di una chiave API SteamGridDB per poter caricare le icone di gioco.
Una volta ottenuta, puoi copiare e rinominare il file `.env.example` a `.env` e metterlo in `STEAMGRIDDB_API_KEY`.
## Esecuzione
Una volta impostato tutto, puoi eseguire il seguente comando per avviare il processo Electron e il client bittorrent:
```bash
yarn dev
```
## Compilazione
### Compila il bittorrent
Usa il comando:
```bash
python torrent-client/setup.py build
```
### Compila l'applicazione Electron
Usa il comando:
Per Windows:
```bash
yarn build:win
```
Per Linux:
```bash
yarn build:linux
```
## Collaboratori
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Licenza
Hydra è concesso in licenza secondo la [MIT License](LICENSE).

View File

@@ -1,189 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra er en spill launcher sin egen innebygt bittorrent klient.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## Innhold
- [Innhold](#innhold)
- [Om](#om)
- [Funksjoner](#funksjoner)
- [Installasjon](#installasjon)
- [Bidra](#-bidra)
- [Bli med i Telegram kanalen vår](#-join-our-telegram)
- [Forke og klone repoet ditt](#fork-and-clone-your-repository)
- [Måter du kan bidra](#ways-you-can-contribute)
- [Prosjekt Struktur](#project-structure)
- [Bygg fra kilden](#build-from-source)
- [Installere Node.js](#install-nodejs)
- [Installere Yarn](#install-yarn)
- [Installere Node-avhengigheter](#install-node-dependencies)
- [Installere Python 3.9](#install-python-39)
- [Installere Python-avhengigheter](#install-python-dependencies)
- [Miljøvariabler](#environment-variables)
- [Kjøre](#running)
- [Bygge](#build)
- [Bygg bittorrent klienten](#build-the-bittorrent-client)
- [Bygg Electron applikationen](#build-the-electron-application)
- [Bidragsytere](#contributors)
- [Lisens](#license)
## Om
**Hydra** er en **Spill Launcher** sin egne innbygte **BitTorrent Klient**.
<br>
Launcheren er skrevet i TypeScript (Electron) og Python, som håndterer torrent systemet ved bruk av libtorrent.
## Funksjoner
- Sin egen innebyggte bittorrent klient
- How Long To Beat (HLTB) integrasjon på spillsiden
- Nedlastingssti tilpasning
- Windows og Linux understøttelse
- Konstant oppdatert
- Og mer ...
## Installasjon
Følg trinnene her under for å innstallere:
1. Last ned den seneste versjonen av Hydra fra [Releases](https://github.com/hydralauncher/hydra/releases/latest) siden.
- Last kun .exe filen ned om du vil installere Hydra på Windows.
- Last kun .deb, .rpm eller .zip ned om du vil installere Hydra på Linux. (kommer an på Linux distroen din)
2. Kjør den nedlastede filen.
3. Nyt Hydra!
## <a name="contributing"> Bidra
### <a name="join-our-telegram"></a> Bli med i Telegram kanalen vår
Vi holder diskusjonene våres i [Telegram](https://t.me/hydralauncher) kanalen.
### Forke og klone repoet ditt
1. Fork repoet [(trykk her for å forke nå)](https://github.com/hydralauncher/hydra/fork)
2. Klon den forkede koden `git clone https://github.com/brukernavnet_ditt/hydra`
3. Lag en ny branch
4. Skyv committene dine
5. Send inn en ny Pull-forespørsel.
### Måter du kan bidra
- Oversetting: Vi har lyst at Hydra skal bli tilgjengelig for så mange som mulig. Hjelp gjerne med å oversette til nye språk eller oppdater og forbedre de som allerede er tilgjengelige i Hydra.
- Code: Hydra is built with Typescript, Electron and a little bit of Python. If you want to contribute, join our [Telegram](https://t.me/hydralauncher)!
- Kode: Hydra er laget med Typescript, Electron og lite gran Pythong. Hvis du har lyst på å bidra, bli med i [Telegram](https://t.me/hydralauncher) kanalen vår!
### Prosjektstruktur
- torrent-client: Vi bruker libtorrent, et Python-bibliotek, til å håndtere torrent nedlastinger.
- src/renderer: UIen til applikasjonen
- src/main: all logikken er her.
## Bygg fra kildekoden
### Installere Node.js
Vær sikker på at du har installert Node.js på maskinen din. Hvis du ikke har det, må du laste ned og installere det fra [nodejs.org](https://nodejs.org/).
### Installere Yarn
Yarn er et pakkehåndteringsverktøy til Node.js. Hvis du ikke allerede har installert Yarn, da kan du gjøre det ved å følge instruksjonene på [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Installere Node-avhengigheter
Naviger til prosjektmappen og installer Node-avhengighetene ved bruk av Yarn:
```bash
cd hydra
yarn
```
### Installere Python 3.9
Vær sikker på at du har installert Python 3.9 på maskinen din. Du kan laste ned og installere det på [python.org](https://www.python.org/downloads/release/python-3913/).
### Installere Python-avhengigheter
Installer de nødvendige Python-avhengigheter ved bruk av pip:
```bash
pip install -r requirements.txt
```
## Miljøvariabler
Du trenger en SteamGridDB API nøkkel for å kunne hente spillikonene ved installasjon.
Når du har det, kan du kopiere eller endre navnet på `.env.example` filen til å være `.env` og lagre nøkkelen som `STEAMGRIDDB_API_KEY`.
## Kjøre
Når alt er satt op, kan du kjøre følgende kommando for å start både Electron prosessen og bittorrent klienten.
```bash
yarn dev
```
## Bygge
### Bygge bittorrent klienten
Bygg bittorrent klienten ved å bruke denne kommandoen:
```bash
python torrent-client/setup.py build
```
### Bygge Electron applikasjonen
Bygg Electron applikasjonen ved å bruke denne kommandoen:
På Windows:
```bash
yarn build:win
```
På Linux:
```bash
yarn build:linux
```
## Bidragsytere
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## Lisens
Hydra bruker [MIT Lisensen](LICENSE).

View File

@@ -1,193 +0,0 @@
<br>
<div align="center">
[<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra - to program uruchamiający gry z własnym wbudowanym klientem bittorrent.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png)
</div>
## Zawartość.
- [Zawartość.](#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 pomóc](#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)
- [Zainstaluj Node.js](#zainstaluj-nodejs)
- [Zainstaluj Yarn](#zainstaluj-yarn)
- [Zainstaluj zależności Node](#zainstaluj-zależności-node)
- [Zainstaluj Python 3.9](#zainstaluj-python-39)
- [Zainstaluj zależności Pythona](#zainstaluj-zależności-pythona)
- [Zmienne środowiskowe](#zmienne-środowiskowe)
- [Run](#run)
- [Tworzenie kompilacji](#tworzenie-kompilacji)
- [Zbuduj klienta bittorrent](#zbuduj-klienta-bittorrent)
- [Tworzenie aplikacji Electron](#tworzenie-aplikacji-electron)
- [Współtwórcy](#współtwórcy)
- [License](#license)
## O nas
**Hydra** - jest **programem uruchamiającym gry** z wbudowanym **klientem BitTorrent**.
<br>
Ten launcher jest napisany w TypeScript (Electron) i Pythonie, który współpracuje z systemem torrent przy użyciu libtorrent.
## Cechy
- Własny wbudowany klient bittorrent
- Integracja funkcji How Long To Beat (HLTB) na stronie gry
- Personalizacja folderu pobierania
- 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.
Po jego uzyskaniu można skopiować plik lub zmienić jego nazwę `.env.example` na `.env` i umieść go na`STEAMGRIDDB_API_KEY`.
## 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

@@ -1,193 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra é um Launcher de Jogos com seu próprio cliente de bittorrent integrado.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png)
</div>
## Índice
- [Índice](#índice)
- [Sobre](#-sobre)
- [Recursos](#-recursos)
- [Instalação](#-instalação)
- [Contribuindo](#-contribuindo)
- [Junte-se ao nosso Telegram](#-junte-se-ao-nosso-telegram)
- [Fork e clone o seu repositório](#-fork-e-clone-o-seu-repositório)
- [Formas de contribuir](#-formas-de-contribuir)
- [Estrutura do Projeto](#-estrutura-do-projeto)
- [Compile a partir do código-fonte](#-compile-a-partir-do-código-fonte)
- [Instale Node.js](#-instale-nodejs)
- [Instale Yarn](#-instale-yarn)
- [Instale Dependencias do Node](#-instale-dependencias-do-node)
- [Instale Python 3.9](#-instale-python-39)
- [Instale Python Dependencies](#-instale-python-dependencies)
- [Environment variables](#-environment-variables)
- [Running](#-running)
- [Build](#-build)
- [ Criar o cliente bittorrent](#-build-the-bittorrent-client)
- [Criar a aplicação Electron](#-build-the-electron-application)
- [Contribuidores](#-contributors)
- [Licença](#-licença)
## <a name="about"> Sobre
**Hydra** é um **Launcher de Jogos** com seu próprio **Cliente BitTorrent incorporado**.
<br>
O launcher é escrito em TypeScript (Electron) e Python, que lida com o sistema de torrent usando libtorrent.
## <a name="features"> Recursos
- Cliente BitTorrent incorporado próprio
- Integração com [How Long To Beat (HLTB)](https://howlongtobeat.com/) na página do jogo
- Personalização do caminho de downloads
- Suporte para Windows e Linux
- Constantemente atualizado
- E mais ...
## <a name="installation"> Instalação
Siga os passos abaixo para instalar:
1. Baixe a versão mais recente do Hydra na página de [Releases](https://github.com/hydralauncher/hydra/releases/latest).
- Baixe apenas o .exe se quiser instalar o Hydra no Windows.
- Baixe .deb ou .rpm ou .zip se quiser instalar o Hydra no Linux. (depende da sua distribuição Linux)
2. Execute o arquivo baixado.
3. Aproveite o Hydra!
## <a name="contributing"> Contribuindo
### <a name="join-our-telegram"></a> Junte-se ao nosso Telegram
Concentramos nossas discussões no nosso canal do [Telegram](https://t.me/hydralauncher).
### <a name="fork-and-clone-your-repository"></a> Fork e clone o seu repositório
1. Faça um fork do repositório [(clique aqui para fazer o fork agora)](https://github.com/hydralauncher/hydra/fork)
2. Clone o código do seu fork `git clone https://github.com/seu_nome_de_usuário/hydra`
3. Crie uma nova branch
4. Faça o push dos seus commits
5. Envie um novo Pull Request
### <a name="ways-you-can-contribute"></a> Formas de contribuir
- **Tradução**: Queremos que o Hydra esteja disponível para o maior número possível de pessoas. Sinta-se à vontade para ajudar a traduzir para novos idiomas ou atualizar e melhorar aqueles que já estão disponíveis no Hydra.
- **Código**: O Hydra é construído com Typescript, Electron e um pouco de Python. Se você deseja contribuir, junte-se ao nosso [Telegram](https://t.me/hydralauncher)!
### <a name="project-structure"></a> Estrutura do Projeto
- torrent-client: Utilizamos o libtorrent, uma biblioteca Python, para gerenciar downloads via torrent.
- src/renderer: A interface de usuário (UI) da aplicação.
- src/main: Toda a lógica da aplicação reside aqui.
## <a name="build-from-source"></a> Compile a partir do código-fonte
### <a name="install-nodejs"></a> Instale Node.js
Certifique-se de ter o Node.js instalado em sua máquina. Se não, faça o download e instale-o em [nodejs.org](https://nodejs.org/).
### <a name="install-yarn"></a> Instale Yarn
Yarn é um gerenciador de pacotes para Node.js. Se você ainda não o instalou, pode fazê-lo seguindo as instruções em [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### <a name="install-node-dependencies"></a> Instale Dependencias do Node
Navegue até o diretório do projeto e instale as dependências do Node usando o Yarn:
```bash
cd hydra
yarn
```
### <a name="install-openssl-11"></a> Instale OpenSSL 1.1
[OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows.
### <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-3913/).
### <a name="install-python-dependencies"></a> Instale Python Dependencies
Instale as dependências Python necessárias usando o pip:
```bash
pip install -r requirements.txt
```
## <a name="environment-variables"></a> Variáveis de ambiente
Você precisará de uma chave da API SteamGridDB para buscar os ícones do jogo durante a instalação.
Depois de obtê-lo, você pode copiar ou renomear o arquivo `.env.example` para `.env` e inserir `STEAMGRIDDB_API_KEY`.
## <a name="running"></a> Executando
Uma vez que você tenha configurado tudo, você pode executar o seguinte comando para iniciar tanto o processo Electron quanto o cliente BitTorrent:
```bash
yarn dev
```
## <a name="build"></a> Build
### <a name="build-the-bittorrent-client"></a> Criar o cliente bittorrent
Compile o cliente BitTorrent usando este comando
```bash
python torrent-client/setup.py build
```
### <a name="build-the-electron-application"></a> Criar a aplicação Electron
Compile a aplicação Electron usando este comando:
No Windows:
```bash
yarn build:win
```
No Linux:
```bash
yarn build:linux
```
## <a name="contributors"></a> Contribuidores
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## <a name="license"></a> Licença
O Hydra é licenciado sob a [Licença MIT](LICENSE).

View File

@@ -1,191 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra - это игровой лаунчер с собственным встроенным клиентом BitTorrent.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png)
</div>
## Содержание
- [Содержание](#содержание)
- [Описание](#описание)
- [Особенности](#особенности)
- [Установка](#установка)
- [Вклад](#-вклад)
- [Присоединяйтесь к нашему Telegram](#-присоединяйтесь-к-нашему-telegram)
- [Форк и клонирование репозитория](#форк-и-клонирование-репозитория)
- [Способы внести свой вклад](#способы-внести-свой-вклад)
- [Структура проекта](#структура-проекта)
- [Сборка из исходного кода](#сборка-из-исходного-кода)
- [Установка Node.js](#установка-nodejs)
- [Установка Yarn](#установка-yarn)
- [Установка зависимостей Node](#установка-зависимостей-node)
- [Установка Python 3.9](#установка-python-39)
- [Установка зависимостей Python](#установка-зависимостей-python)
- [Переменные среды](#переменные-среды)
- [Запуск](#запуск)
- [Сборка](#сборка)
- [Сборка клиента BitTorrent](#сборка-клиента-bittorrent)
- [Сборка приложения Electron](#сборка-приложения-electron)
- [Участники](#участники)
- [License](#license)
## Описание
**Hydra** - это **Игровой Лаунчер** с собственным встроенным **Клиентом BitTorrent** и **самостоятельным scraper`ом для репаков**.
<br>
Лаунчер написан на TypeScript (Electron) и Python, который управляет системой торрентов с помощью libtorrent.
## Особенности
- Самостоятельный scraper репаков среди всех наиболее надежных веб-сайтов в [Megathread](https://www.reddit.com/r/Piracy/wiki/megathread/)
- Встроенный клиент BitTorrent
- Интеграция How Long To Beat (HLTB) на странице игры
- Настройка пути загрузки
- Уведомления об обновлении списка репаков
- Поддержка Windows и Linux
- Постоянно обновляется
- И многое другое...
## Установка
Чтобы установить, выполните следующие шаги:
1. Скачайте последнюю версию Hydra с [страницы релизов](https://github.com/hydralauncher/hydra/releases/latest).
- Загрузите только .exe, если хотите установить Hydra на Windows.
- Загрузите .deb или .rpm или .zip, если хотите установить Hydra на Linux (в зависимости от вашего дистрибутива Linux).
2. Запустите скачанный файл.
3. Наслаждайтесь Hydra!
## <a name="contributing"> Вклад
### <a name="join-our-telegram"></a> Присоединяйтесь к нашему Telegram
Мы сосредотачиваем наши обсуждения в нашем канале [Telegram](https://t.me/hydralauncher).
### Форк и клонирование репозитория
1. Форкните репозиторий [(нажмите здесь, чтобы сделать форк сейчас)](https://github.com/hydralauncher/hydra/fork)
2. Склонируйте свой форкнутый код `git clone https://github.com/ваше_имя_пользователя/hydra`
3. Создайте новую ветку
4. Отправьте свои коммиты
5. Отправьте Pull Request
### Способы внести свой вклад
- Перевод: Мы хотим, чтобы Hydra была доступна как можно большему количеству людей. Не стесняйтесь помогать переводить на новые языки или обновлять и улучшать те, которые уже доступны в Hydra.
- Код: Hydra создан с использованием TypeScript, Electron и немного Python. Если хотите внести свой вклад, присоединяйтесь к нашему каналу [Telegram](https://t.me/hydralauncher)!
### Структура проекта
- torrent-client: Мы используем libtorrent, библиотеку Python, для управления загрузками торрентов.
- src/renderer: пользовательский интерфейс приложения.
- src/main: весь основной функционал здесь.
## Сборка из исходного кода
### Установка Node.js
Убедитесь, что у вас установлен Node.js на вашем компьютере. Если нет, загрузите и установите его с [nodejs.org](https://nodejs.org/).
### Установка Yarn
Yarn - менеджер пакетов для Node.js. Если вы еще не установили Yarn, сделайте это, следуя инструкциям на [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Установка зависимостей Node
Перейдите в каталог проекта и установите зависимости Node, используя Yarn:
```bash
cd hydra
yarn
```
### Установка Python 3.9
Убедитесь, что у вас установлен Python 3.9 на вашем компьютере. Вы можете загрузить и установить его с [python.org](https://www.python.org/downloads/release/python-3913/).
### Установка зависимостей Python
Установите необходимые зависимости Python, используя pip:
```bash
pip install -r requirements.txt
```
## Переменные среды
Вам понадобится ключ API SteamGridDB, чтобы получить значки игр при установке.
Как только у вас будет ключ, вы можете скопировать или переименовать файл `.env.example` в `.env` и поместить в него `STEAMGRIDDB_API_KEY`.
## Запуск
После того как все настроено, вы можете выполнить следующую команду, чтобы запустить процесс Electron и клиента BitTorrent:
```bash
yarn dev
```
## Сборка
### Сборка клиента BitTorrent
Соберите клиент BitTorrent с помощью этой команды:
```bash
python torrent-client/setup.py build
```
### Сборка приложения Electron
Соберите приложение Electron, используя эту команду:
На Windows:
```bash
yarn build:win
```
На Linux:
```bash
yarn build:linux
```
## Участники
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## License
Hydra лицензирована в соответствии с [MIT License](LICENSE).

View File

@@ -1,194 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra, kendi gömülü BitTorrent istemcisine sahip bir oyun başlatıcısıdır.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## <a name="içindekiler"></a> İçindekiler
- [İçindekiler](#içindekiler)
- [Hakkında](#hakkında)
- [Özellikler](#özellikler)
- [Kurulum](#kurulum)
- [Katkıda bulunma](#katkıda-bulunma)
- [Telegram grubumuza katılın](#telegram-katıl)
- [Repoyu forklayın ve klonlayın](#repo-fork-klon)
- [Katkıda bulunabileceğin yollar](#katkı-yolları)
- [Proje yapısı](#proje-yapısı)
- [Kaynak kodundan derleme](#kaynak-kodundan-derleme)
- [Node.js'i yükleme](#nodejs-yükle)
- [Yarn'ı yükleme](#yarn-yükle)
- [Node bağımlılıklarını yükleme](#node-bağımlılık-yükle)
- [OpenSSL 1.1'i yükleme](#openssl-1-1-yükle)
- [Python 3.9'u yükleme](#python-3-9-yükle)
- [Python bağımlılıklarını yükleme](#python-bağımlılık-yükle)
- [Ortam değişkenleri](#ortam-değişkenleri)
- [Çalıştırma](#çalıştırma)
- [Derleme](#derleme)
- [BitTorrent istemcisini derleme](#bittorrent-istemci-derle)
- [Electron uygulamasını derleme](#electron-uygulama-derle)
- [Katkıda bulunanlar](#katkıda-bulunanlar)
- [Lisans](#lisans)
## <a name="hakkında"></a> Hakkında
**Hydra**, kendi gömülü **BitTorrent istemci**sine sahip bir **oyun başlatıcısı**dır.
<br>
Başlatıcı, torrent sistemini libtorrent kullanarak yöneten Python ve TypeScript (Electron) ile yazılmıştır.
## <a name="özellikler"></a> Özellikler
- Kendi gömülü BitTorrent istemcisi
- Oyun sayfasında How Long To Beat (HLTB) entegrasyonu
- İndirme yolu özelleştirmesi
- Windows ve Linux desteği
- Sürekli güncelleme
- Ve daha fazlası...
## <a name="kurulum"></a> Kurulum
Aşağıdaki adımları izleyerek Hydra'yı kurun:
1. Hydra'nın en son sürümünü [Releases](https://github.com/hydralauncher/hydra/releases/latest) sayfasından indirin.
- Hydra'yı Windows'a kurmak istiyorsanız sadece .exe dosyasını indirin.
- Hydra'yı Linux'a kurmak istiyorsanız .deb, .rpm veya .zip dosyasını indirin (kullandığınız Linux dağıtımına bağlı olarak).
2. İndirilen dosyayı çalıştırın.
3. Hydra'nın keyfini çıkarın!
## <a name="katkıda-bulunma"></a> Katkıda Bulunma
### <a name="telegram-katıl"></a> Telegram grubumuza katılın
Tartışmalarımızı [Telegram](https://t.me/hydralauncher) kanalımız üzerinde yürütüyoruz.
### <a name="repo-fork-klon"></a> Repoyu forklayın ve klonlayın
1. Depoyu fork'layın [(şimdi forklamak için tıklayın)](https://github.com/hydralauncher/hydra/fork)
2. Forkladığınız kodu klonlayın `git clone https://github.com/kullanıcı_adınız/hydra`
3. Yeni bir branch oluşturun
4. Commitlerinizi gönderin (push)
5. Yeni bir Pull Request gönderin
### <a name="katkı-yolları"></a> Katkıda bulunabileceğin yollar
- Çeviri: Hydra'nın mümkün olduğunca fazla kişiye ulaşmasını istiyoruz. Yeni dillere çeviri yapmak ya da mevcut dillere güncelleme ve iyileştirme yapmak için yardımcı olmaktan çekinmeyin.
- Kod: Hydra, Typescript, Electron ve biraz Python ile inşa edilmiştir. Katkıda bulunmak isterseniz, [Telegram](https://t.me/hydralauncher) kanalımıza katılın!
### <a name="proje-yapısı"></a> Proje yapısı
- torrent-client: Torrent indirmelerini yönetmek için libtorrent adlı bir Python kütüphanesini kullanıyoruz.
- src/renderer: Uygulamanın kullanıcı arayüzü burada bulunur.
- src/main: Uygulamanın tüm işleyişi ve iş mantığı bu bölümde bulunur.
## <a name="kaynak-kodundan-derleme"></a> Kaynak kodundan derleme
### <a name="nodejs-yükle"></a> Node.js'i yükleme
Makinenizde Node.js'in yüklü olduğundan emin olun. Yüklü değilse, [nodejs.org](https://nodejs.org/) adresinden indirip kurun.
### <a name="yarn-yükle"></a> Yarn'ı yükleme
Yarn, Node.js için bir paket yöneticisidir. Eğer Yarn'ı henüz kurmadıysanız, [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/) adresindeki talimatları izleyerek kurabilirsiniz.
### <a name="node-bağımlılık-yükle"></a> Node bağımlılıklarını yükleme
Proje dizinine gidin ve Yarn kullanarak Node bağımlılıklarını yükleyin:
```bash
cd hydra
yarn
```
### <a name="openssl-1-1-yükle"></a> OpenSSL 1.1'i yükleme
Windows ortamlarında libtorrent tarafından gerekli olan [OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe)'i indirip yükleyin.
### <a name="python-3-9-yükle"></a> Python 3.9'u yükleme
Makinenizde Python 3.9'un yüklü olduğundan emin olun. Bunu [python.org](https://www.python.org/downloads/release/python-3913/) adresinden indirip kurarak yapabilirsiniz.
### <a name="python-bağımlılık-yükle"></a> Python bağımlılıklarını yükleme
Gerekli Python bağımlılıklarını pip kullanarak yükleyin:
```bash
pip install -r requirements.txt
```
## <a name="ortam-değişkenleri"></a> Ortam değişkenleri
Oyun simgelerini yüklemek için bir SteamGridDB API Anahtarına ihtiyacınız olacak.
Bu anahtara sahip olduktan sonra, `.env.example` dosyasını kopyalayabilir veya adını `.env` olarak değiştirebilir ve `STEAMGRIDDB_API_KEY` değerini buraya ekleyebilirsiniz.
## <a name="çalıştırma"></a> Çalıştırma
Tüm ayarları tamamladıktan sonra, hem Electron sürecini hem de bittorrent istemcisini başlatmak için aşağıdaki komutu çalıştırabilirsiniz:
```bash
yarn dev
```
## <a name="derleme"></a> Derleme
### <a name="bittorrent-istemci-derle"></a> BitTorrent istemcisini derleme
Bittorrent istemcisini aşağıdaki komutla derleyin:
```bash
python torrent-client/setup.py build
```
### <a name="electron-uygulama-derle"></a> Electron uygulamasını derleme
Electron uygulamasını aşağıdaki komutlarla derleyebilirsiniz:
Windows'ta:
```bash
yarn build:win
```
Linux'ta:
```bash
yarn build:linux
```
## <a name="katkıda-bulunanlar"></a> Katkıda bulunanlar
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## <a name="lisans"></a> Lisans
Hydra, [MIT Lisansı](https://github.com/hydralauncher/hydra/blob/main/LICENSE) altında lisanlanmıştır.

View File

@@ -1,195 +0,0 @@
<br>
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1>
<p align="center">
<strong>Hydra - це ігровий лаунчер з власним вбудованим bittorrent-клієнтом.</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)
[![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)
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.md)
[![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png)
</div>
## Зміст
- [Зміст](#зміст)
- [Про нас](#про-нас)
- [Функції](#функції)
- [Встановлення](#встановлення)
- [Зробити свій внесок](#-зробити-свій-внесок)
- [Приєднуйтесь до нашого Telegram](#-приєднуйтесь-до-нашого-telegram)
- [Форк і клонування вашого репозиторію](#форк-і-клонування-вашого-репозиторію)
- [Як ви можете зробити свій внесок](#як-ви-можете-зробити-свій-внесок)
- [Структура проекту](#структура-проекту)
- [Зробити білд з вихідного коду](#зробити-білд-з-вихідного-коду)
- [Встановіть Node.js](#встановіть-nodejs)
- [Встановіть Yarn](#встановіть-yarn)
- [Встановіть Node залежності](#встановіть-node-залежності)
- [Встановіть Python 3.9](#встановіть-python-39)
- [Встановіть Python залежності](#встановіть-python-залежності)
- [Змінні середовища](#змінні-середовища)
- [Запустіть](#запустіть)
- [Зробіть білд](#зробіть-білд)
- [Зробіть білд bittorrent client](#зробіть-білд-bittorrent-client)
- [Зробіть білд Electron застосунку](#зробіть-білд-electron-застосунку)
- [Контриб'ютори](#контрибютори)
- [License](#license)
## Про нас
**Hydra** - це **ігровий лаунчер** з власним вбудованим **BitTorrent-клієнтом** і **самокерованим збирачем репаків**.
<br>
Цей лаунчер написано мовами TypeScript (Electron) та Python, який працює з торрент-системою за допомогою libtorrent.
## Функції
- Самокерований збирач репаків серед усіх найнадійніших сайтів на [Megathread]("https://www.reddit.com/r/Piracy/wiki/megathread/")
- Власний вбудований клієнт bittorrent
- Інтеграція How Long To Beat (HLTB) на сторінці гри
- Налаштування теки завантаження
- Сповіщення про оновлення списку репаків
- Підтримка Windows і Linux
- Постійно оновлюється
- І не тільки ...
## Встановлення
Щоб встановити, виконайте наведені нижче кроки:
1. Завантажте останню версію Hydra зі сторінки [Releases](https://github.com/hydralauncher/hydra/releases/latest).
- Завантажте лише .exe, якщо ви хочете встановити Hydra на Windows.
- Завантажте .deb або .rpm або .zip, якщо ви хочете встановити Hydra на Linux. (залежить від вашого дистрибутива Linux)
2. Запустіть завантажений файл.
3. Насолоджуйтесь Гідрою!
## <a name="contributing"> Зробити свій внесок
### <a name="join-our-telegram"></a> Приєднуйтесь до нашого Telegram
Ми зосереджуємо наші дискусії на нашому каналі [Telegram](https://t.me/hydralauncher).
1. Приєднуйтесь до нашого канала
2. Перейдіть на канал ролей і виберіть роль Співробітник
3. Заходьте на dev-канал, спілкуйтеся з нами та діліться своїми ідеями.
### Форк і клонування вашого репозиторію
1. Зробіть форк репозиторію [(натисніть тут, щоб зробити форк зараз)](https://github.com/hydralauncher/hydra/fork)
2. Клонуйте ваш форк-код `git clone https://github.com/your_username/hydra`
3. Створіть новий бранч
4. Зробіть пуш своїх комітів
5. Надішліть новий Pull Request
### Як ви можете зробити свій внесок
- Переклад: Ми хочемо, щоб Hydra була доступна якомога більшій кількості людей. Не соромтеся допомагати перекладати на нові мови або оновлювати і покращувати ті, які вже доступні на Hydra.
- Код: Hydra створена за допомогою Typescript, Electron і трохи Python. Якщо ви хочете зробити свій внесок, приєднуйтесь до нашого Telegram!
### Структура проекту
- torrent-client: Ми використовуємо libtorrent, бібліотеку Python, для керування завантаженнями з торрентів
- src/renderer: інтерфейс програми
- src/main: вся логіка тут.
## Зробити білд з вихідного коду
### Встановіть Node.js
Переконайтеся, що на вашому комп'ютері встановлено Node.js. Якщо ні, завантажте та встановіть його з [nodejs.org](https://nodejs.org/).
### Встановіть Yarn
Yarn - це менеджер пакетів для Node.js. Якщо ви ще не встановили Yarn, ви можете зробити це, дотримуючись інструкцій на сторінці [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
### Встановіть Node залежності
Перейдіть до каталогу проекту і встановіть Node залежності за допомогою Yarn:
```bash
cd hydra
yarn
```
### Встановіть Python 3.9
Переконайтеся, що на вашому комп'ютері встановлено Python 3.9. Ви можете завантажити та встановити його з [python.org](https://www.python.org/downloads/release/python-3913/).
### Встановіть Python залежності
Встановіть необхідні залежності Python за допомогою pip:
```bash
pip install -r requirements.txt
```
## Змінні середовища
Вам знадобиться ключ API SteamGridDB, щоб отримати іконки ігор під час встановлення.
Отримавши його, ви можете скопіювати або перейменувати файл `.env.example` на `.env`і помістити його на`STEAMGRIDDB_API_KEY`.
## Запустіть
Після того, як ви все налаштували, ви можете запустити наступну команду, щоб запустити як процес Electron, так і клієнт bittorrent:
```bash
yarn dev
```
## Зробіть білд
### Зробіть білд bittorrent client
Зробіть білд bittorrent client за допомогою цієї команди:
```bash
python torrent-client/setup.py build
```
### Зробіть білд Electron застосунку
Зробіть білд Electron застосунку за допомогою цієї команди:
На Windows:
```bash
yarn build:win
```
На Linux:
```bash
yarn build:linux
```
## Контриб'ютори
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
</a>
## License
Hydra має ліцензію [MIT License](LICENSE).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

After

Width:  |  Height:  |  Size: 868 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "hydralauncher",
"version": "3.4.5",
"version": "3.4.10",
"description": "Hydra",
"main": "./out/main/index.js",
"author": "Los Broxas",
@@ -28,7 +28,8 @@
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
"prepare": "husky"
"prepare": "husky",
"protoc": "npx protoc --ts_out src/main/generated --proto_path proto proto/*.proto"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
@@ -74,6 +75,7 @@
"tar": "^7.4.3",
"tough-cookie": "^5.1.1",
"user-agents": "^1.1.387",
"ws": "^8.18.1",
"yaml": "^2.6.1",
"yup": "^1.5.0",
"zod": "^3.24.1"
@@ -85,6 +87,7 @@
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^2.0.0",
"@electron-toolkit/tsconfig": "^1.0.1",
"@protobuf-ts/plugin": "^2.10.0",
"@swc/core": "^1.4.16",
"@types/auto-launch": "^5.0.5",
"@types/color": "^3.0.6",
@@ -97,6 +100,7 @@
"@types/react-dom": "^18.2.18",
"@types/sound-play": "^1.1.3",
"@types/user-agents": "^1.0.4",
"@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.2.1",
"electron": "^31.7.7",
"electron-builder": "^26.0.12",

1
proto Submodule

Submodule proto added at 7a23620f93

File diff suppressed because one or more lines are too long

1
sonar-project.properties Normal file
View File

@@ -0,0 +1 @@
sonar.exclusions=src/main/generated/**

View File

@@ -198,7 +198,8 @@
"download_error_not_cached_on_hydra": "This download is not available on Nimbus.",
"game_removed_from_favorites": "Game removed from favorites",
"game_added_to_favorites": "Game added to favorites",
"automatically_extract_downloaded_files": "Automatically extract downloaded files"
"automatically_extract_downloaded_files": "Automatically extract downloaded files",
"create_start_menu_shortcut": "Create Start Menu shortcut"
},
"activation": {
"title": "Activate Hydra",
@@ -369,10 +370,11 @@
"restart_to_install_update": "Restart Hydra to install the update",
"notification_achievement_unlocked_title": "Achievement unlocked for {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked",
"new_friend_request_description": "You have received a new friend request",
"new_friend_request_description": "{{displayName}} sent you a friend request",
"new_friend_request_title": "New friend request",
"extraction_complete": "Extraction complete",
"game_extracted": "{{title}} extracted successfully"
"game_extracted": "{{title}} extracted successfully",
"friend_started_playing_game": "{{displayName}} started playing a game"
},
"system_tray": {
"open": "Open Hydra",

View File

@@ -371,7 +371,8 @@
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados",
"new_friend_request_title": "Nueva solicitud de amistad",
"new_friend_request_description": "Has recibido una nueva solicitud de amistad"
"new_friend_request_description": "{{displayName}} te envió una solicitud de amistad",
"friend_started_playing_game": "{{displayName}} está jugando"
},
"system_tray": {
"open": "Abrir Hydra",

View File

@@ -187,7 +187,8 @@
"download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.",
"game_removed_from_favorites": "Jogo removido dos favoritos",
"game_added_to_favorites": "Jogo adicionado aos favoritos",
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados"
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados",
"create_start_menu_shortcut": "Criar atalho no Menu Iniciar"
},
"activation": {
"title": "Ativação",
@@ -355,9 +356,10 @@
"new_update_available": "Versão {{version}} disponível",
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão",
"new_friend_request_title": "Novo pedido de amizade",
"new_friend_request_description": "Você recebeu um novo pedido de amizade",
"new_friend_request_description": "{{displayName}} te enviou um pedido de amizade",
"extraction_complete": "Extração concluída",
"game_extracted": "{{title}} extraído com sucesso"
"game_extracted": "{{title}} extraído com sucesso",
"friend_started_playing_game": "{{displayName}} começou a jogar"
},
"system_tray": {
"open": "Abrir Hydra",

View File

@@ -178,7 +178,8 @@
"download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
"download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.",
"game_removed_from_favorites": "Jogo removido dos favoritos",
"game_added_to_favorites": "Jogo adicionado aos favoritos"
"game_added_to_favorites": "Jogo adicionado aos favoritos",
"create_start_menu_shortcut": "Criar atalho no Menu Iniciar"
},
"activation": {
"title": "Ativação",
@@ -340,7 +341,8 @@
"new_update_available": "Versão {{version}} disponível",
"restart_to_install_update": "Reinicia o Hydra para instalar a nova versão",
"new_friend_request_title": "Novo pedido de amizade",
"new_friend_request_description": "Recebeste um novo pedido de amizade"
"new_friend_request_description": "{{displayName}} te enviou um pedido de amizade",
"friend_started_playing_game": "{{displayName}} começou a jogar"
},
"system_tray": {
"open": "Abrir o Hydra",

View File

@@ -226,7 +226,7 @@
"install": "Встановити",
"download_in_progress": "В процесі",
"downloads_completed": "Завершено",
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати.",
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати!",
"no_downloads_title": "Тут так пусто...",
"queued": "В черзі",
"queued_downloads": "Завантаження в черзі",
@@ -274,7 +274,7 @@
"description_confirmation_delete_all_sources": "Ви видалите всі джерела завантаження",
"title_confirmation_delete_all_sources": "Видалити всі джерела завантаження",
"removed_download_sources": "Джерела завантажень видалено",
"button_delete_all_sources": "Видаліть усі джерела завантаження",
"button_delete_all_sources": "Видалити усі джерела завантаження",
"api_token": "API-токен",
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
"real_debrid_api_token_label": "Real-Debrid API-токен",
@@ -323,14 +323,14 @@
"editor_tab_code": "Код",
"editor_tab_info": "Інформація",
"editor_tab_save": "Зберегти",
"web_store": "Інтернет-магазин",
"web_store": "Інтернет-магазин тем",
"clear_themes": "Очистити",
"create_theme": "Створити",
"create_theme_modal_title": "Створити користувацьку тему",
"create_theme_modal_description": "Створіть нову тему для налаштування зовнішнього вигляду Hydra",
"theme_name": "Назва",
"insert_theme_name": "Введіть назву теми",
"set_theme": "Включити тему",
"set_theme": "Застосувати тему",
"unset_theme": "Виключити тему",
"delete_theme": "Видалити тему",
"edit_theme": "Редагувати тему",
@@ -356,7 +356,8 @@
"common_redist_description": "Загальні розповсюджувані компоненти необхідні для запуску деяких ігор. Рекомендується їх встановити, щоб уникнути проблем.",
"install_common_redist": "Встановити",
"installing_common_redist": "Встановлюється…",
"show_download_speed_in_megabytes": "Показувати швидкість завантаження в мегабайтах на секунду"
"show_download_speed_in_megabytes": "Показувати швидкість завантаження в мегабайтах на секунду",
"extract_files_by_default": "Розпаковувати файли після завантаження"
},
"notifications": {
"download_complete": "Завантаження завершено",
@@ -425,8 +426,8 @@
"user_not_found": "Користувача не найдено",
"block_user": "Заблокувати користувача",
"add_friend": "Добавити друга",
"request_sent": "Запит надіслано",
"request_received": "Запит отримано",
"request_sent": "надіслано запит на дружбу",
"request_received": "Отримано запит на дружбу",
"accept_request": "Прийняти запит",
"ignore_request": "Ігнорувати запит",
"cancel_request": "Скасувати запит",
@@ -460,8 +461,8 @@
"report_reason_other": "Інше",
"profile_reported": "Профіль повідомлено",
"your_friend_code": "Ваш код друга:",
"upload_banner": "Upload banner",
"uploading_banner": "Завантажити банер...",
"upload_banner": "Завантажити банер",
"uploading_banner": "Завантаження банеру...",
"background_image_updated": "Фонове зображення оновлено",
"stats": "Статистика",
"achievements": "Досягнення",

View File

@@ -1,23 +1,32 @@
import { app } from "electron";
import path from "node:path";
import { SystemPath } from "./services/system-path";
export const LUDUSAVI_MANIFEST_URL = "https://cdn.losbroxas.org/manifest.yaml";
export const defaultDownloadsPath = app.getPath("downloads");
export const defaultDownloadsPath = SystemPath.getPath("downloads");
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
export const windowsStartMenuPath = path.join(
SystemPath.getPath("appData"),
"Microsoft",
"Windows",
"Start Menu",
"Programs"
);
export const levelDatabasePath = path.join(
app.getPath("userData"),
SystemPath.getPath("userData"),
`hydra-db${isStaging ? "-staging" : ""}`
);
export const commonRedistPath = path.join(
app.getPath("userData"),
SystemPath.getPath("userData"),
"CommonRedist"
);
export const logsPath = path.join(app.getPath("userData"), "logs");
export const logsPath = path.join(SystemPath.getPath("userData"), "logs");
export const seedsPath = app.isPackaged
? path.join(process.resourcesPath, "seeds")
@@ -27,7 +36,7 @@ export const achievementSoundPath = app.isPackaged
? path.join(process.resourcesPath, "achievement.wav")
: path.join(__dirname, "..", "..", "resources", "achievement.wav");
export const backupsPath = path.join(app.getPath("userData"), "Backups");
export const backupsPath = path.join(SystemPath.getPath("userData"), "Backups");
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");

View File

@@ -1,5 +1,10 @@
import { registerEvent } from "../register-event";
import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services";
import {
DownloadManager,
HydraApi,
WSClient,
gamesPlaytime,
} from "@main/services";
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
@@ -30,6 +35,8 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
databaseOperations,
HydraApi.post("/auth/logout").catch(() => {}),
]);
WSClient.close();
};
registerEvent("signOut", signOut);

View File

@@ -1,6 +1,7 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { CatalogueCategory } from "@shared";
import { ShopAssets } from "@types";
const getCatalogue = async (
_event: Electron.IpcMainInvokeEvent,
@@ -11,7 +12,7 @@ const getCatalogue = async (
skip: "0",
});
return HydraApi.get(
return HydraApi.get<ShopAssets[]>(
`/catalogue/${category}?${params.toString()}`,
{},
{ needsAuth: false }

View File

@@ -1,10 +1,13 @@
import { getSteamAppDetails, logger } from "@main/services";
import type { ShopDetails, GameShop } from "@types";
import type { ShopDetails, GameShop, ShopDetailsWithAssets } from "@types";
import { registerEvent } from "../register-event";
import { steamGamesWorker } from "@main/workers";
import { gamesShopCacheSublevel, levelKeys } from "@main/level";
import {
gamesShopAssetsSublevel,
gamesShopCacheSublevel,
levelKeys,
} from "@main/level";
const getLocalizedSteamAppDetails = async (
objectId: string,
@@ -14,22 +17,7 @@ const getLocalizedSteamAppDetails = async (
return getSteamAppDetails(objectId, language);
}
return getSteamAppDetails(objectId, language).then(
async (localizedAppDetails) => {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
if (steamGame && localizedAppDetails) {
return {
...localizedAppDetails,
name: steamGame.name,
};
}
return null;
}
);
return getSteamAppDetails(objectId, language);
};
const getGameShopDetails = async (
@@ -37,34 +25,44 @@ const getGameShopDetails = async (
objectId: string,
shop: GameShop,
language: string
): Promise<ShopDetails | null> => {
): Promise<ShopDetailsWithAssets | null> => {
if (shop === "steam") {
const cachedData = await gamesShopCacheSublevel.get(
levelKeys.gameShopCacheItem(shop, objectId, language)
);
const [cachedData, cachedAssets] = await Promise.all([
gamesShopCacheSublevel.get(
levelKeys.gameShopCacheItem(shop, objectId, language)
),
gamesShopAssetsSublevel.get(levelKeys.game(shop, objectId)),
]);
const appDetails = getLocalizedSteamAppDetails(objectId, language).then(
(result) => {
if (result) {
result.name = cachedAssets?.title ?? result.name;
gamesShopCacheSublevel
.put(levelKeys.gameShopCacheItem(shop, objectId, language), result)
.catch((err) => {
logger.error("Could not cache game details", err);
});
return {
...result,
assets: cachedAssets ?? null,
};
}
return result;
return null;
}
);
if (cachedData) {
return {
...cachedData,
objectId,
} as ShopDetails;
assets: cachedAssets ?? null,
};
}
return Promise.resolve(appDetails);
return appDetails;
}
throw new Error("Not implemented");

View File

@@ -6,17 +6,17 @@ import type { TrendingGame } from "@types";
const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => {
const language = await db
.get<string, string>(levelKeys.language, {
valueEncoding: "utf-8",
valueEncoding: "utf8",
})
.then((language) => language || "en");
const trendingGames = await HydraApi.get<TrendingGame[]>(
"/games/trending",
"/games/featured",
{ language },
{ needsAuth: false }
).catch(() => []);
return trendingGames;
return trendingGames.slice(0, 1);
};
registerEvent("getTrendingGames", getTrendingGames);

View File

@@ -0,0 +1,14 @@
import type { GameShop, ShopAssets } from "@types";
import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
import { registerEvent } from "../register-event";
const saveGameShopAssets = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
assets: ShopAssets
): Promise<void> => {
return gamesShopAssetsSublevel.put(levelKeys.game(shop, objectId), assets);
};
registerEvent("saveGameShopAssets", saveGameShopAssets);

View File

@@ -4,13 +4,13 @@ import * as tar from "tar";
import { registerEvent } from "../register-event";
import axios from "axios";
import os from "node:os";
import { app } from "electron";
import path from "node:path";
import { backupsPath } from "@main/constants";
import type { GameShop } from "@types";
import YAML from "yaml";
import { normalizePath } from "@main/helpers";
import { SystemPath } from "@main/services/system-path";
export interface LudusaviBackup {
files: {
@@ -35,7 +35,7 @@ const replaceLudusaviBackupWithCurrentUser = (
drives: Record<string, string>;
};
const currentHomeDir = normalizePath(app.getPath("home"));
const currentHomeDir = normalizePath(SystemPath.getPath("home"));
/* Renaming logic */
if (os.platform() === "win32") {
@@ -84,7 +84,7 @@ const downloadGameArtifact = async (
homeDir: string;
}>(`/profile/games/artifacts/${gameArtifactId}/download`);
const zipLocation = path.join(app.getPath("userData"), objectKey);
const zipLocation = path.join(SystemPath.getPath("userData"), objectKey);
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
if (fs.existsSync(backupPath)) {

View File

@@ -3,6 +3,7 @@ import { ipcMain } from "electron";
import "./catalogue/get-catalogue";
import "./catalogue/get-game-shop-details";
import "./catalogue/save-game-shop-assets";
import "./catalogue/get-how-long-to-beat";
import "./catalogue/get-random-game";
import "./catalogue/search-games";

View File

@@ -1,12 +1,13 @@
import { registerEvent } from "../register-event";
import type { Game, GameShop } from "@types";
import { steamGamesWorker } from "@main/workers";
import type { GameShop } from "@types";
import { createGame } from "@main/services/library-sync";
import { steamUrlBuilder } from "@shared";
import { updateLocalUnlockedAchievements } from "@main/services/achievements/update-local-unlocked-achivements";
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
import {
downloadsSublevel,
gamesShopAssetsSublevel,
gamesSublevel,
levelKeys,
} from "@main/level";
const addGameToLibrary = async (
_event: Electron.IpcMainInvokeEvent,
@@ -15,27 +16,20 @@ const addGameToLibrary = async (
title: string
) => {
const gameKey = levelKeys.game(shop, objectId);
const game = await gamesSublevel.get(gameKey);
let game = await gamesSublevel.get(gameKey);
const gameAssets = await gamesShopAssetsSublevel.get(gameKey);
if (game) {
await downloadsSublevel.del(gameKey);
await gamesSublevel.put(gameKey, {
...game,
isDeleted: false,
});
game.isDeleted = false;
await gamesSublevel.put(gameKey, game);
} else {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
: null;
const game: Game = {
game = {
title,
iconUrl,
iconUrl: gameAssets?.iconUrl ?? null,
objectId,
shop,
remoteId: null,
@@ -44,12 +38,12 @@ const addGameToLibrary = async (
lastTimePlayed: null,
};
await gamesSublevel.put(levelKeys.game(shop, objectId), game);
await createGame(game).catch(() => {});
updateLocalUnlockedAchievements(game);
await gamesSublevel.put(gameKey, game);
}
await createGame(game).catch(() => {});
updateLocalUnlockedAchievements(game);
};
registerEvent("addGameToLibrary", addGameToLibrary);

View File

@@ -3,13 +3,16 @@ import createDesktopShortcut from "create-desktop-shortcuts";
import path from "node:path";
import { app } from "electron";
import { removeSymbolsFromName } from "@shared";
import { GameShop } from "@types";
import { GameShop, ShortcutLocation } from "@types";
import { gamesSublevel, levelKeys } from "@main/level";
import { SystemPath } from "@main/services/system-path";
import { windowsStartMenuPath } from "@main/constants";
const createGameShortcut = async (
_event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string
objectId: string,
location: ShortcutLocation
): Promise<boolean> => {
const gameKey = levelKeys.game(shop, objectId);
const game = await gamesSublevel.get(gameKey);
@@ -24,7 +27,10 @@ const createGameShortcut = async (
const options = {
filePath,
name: removeSymbolsFromName(game.title),
outputPath: app.getPath("desktop"),
outputPath:
location === "desktop"
? SystemPath.getPath("desktop")
: windowsStartMenuPath,
};
return createDesktopShortcut({

View File

@@ -1,6 +1,10 @@
import type { LibraryGame } from "@types";
import { registerEvent } from "../register-event";
import { downloadsSublevel, gamesSublevel } from "@main/level";
import {
downloadsSublevel,
gamesShopAssetsSublevel,
gamesSublevel,
} from "@main/level";
const getLibrary = async (): Promise<LibraryGame[]> => {
return gamesSublevel
@@ -12,11 +16,13 @@ const getLibrary = async (): Promise<LibraryGame[]> => {
.filter(([_key, game]) => game.isDeleted === false)
.map(async ([key, game]) => {
const download = await downloadsSublevel.get(key);
const gameAssets = await gamesShopAssetsSublevel.get(key);
return {
id: key,
...game,
download: download ?? null,
...gameAssets,
};
})
);

View File

@@ -6,7 +6,7 @@ import { db, levelKeys } from "@main/level";
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
const language = await db
.get<string, string>(levelKeys.language, {
valueEncoding: "utf-8",
valueEncoding: "utf8",
})
.then((language) => language || "en");

View File

@@ -1,34 +1,11 @@
import { MAIN_LOOP_INTERVAL } from "@main/constants";
import { registerEvent } from "../register-event";
import { HydraApi, WindowManager } from "@main/services";
import { publishNewFriendRequestNotification } from "@main/services/notifications";
import { UserNotLoggedInError } from "@shared";
import type { FriendRequestSync } from "@types";
interface SyncState {
friendRequestCount: number | null;
tick: number;
}
const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes
const syncState: SyncState = {
friendRequestCount: null,
tick: 0,
};
const syncFriendRequests = async () => {
export const syncFriendRequests = async () => {
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`)
.then((res) => {
if (
syncState.friendRequestCount != null &&
syncState.friendRequestCount < res.friendRequestCount
) {
publishNewFriendRequestNotification();
}
syncState.friendRequestCount = res.friendRequestCount;
WindowManager.mainWindow?.webContents.send(
"on-sync-friend-requests",
res
@@ -44,16 +21,4 @@ const syncFriendRequests = async () => {
});
};
const syncFriendRequestsEvent = async (_event: Electron.IpcMainInvokeEvent) => {
return syncFriendRequests();
};
export const watchFriendRequests = async () => {
if (syncState.tick % ticksToUpdate === 0) {
await syncFriendRequests();
}
syncState.tick++;
};
registerEvent("syncFriendRequests", syncFriendRequestsEvent);
registerEvent("syncFriendRequests", syncFriendRequests);

View File

@@ -1,11 +1,14 @@
import { registerEvent } from "../register-event";
import type { Download, StartGameDownloadPayload } from "@types";
import { DownloadManager, HydraApi, logger } from "@main/services";
import { steamGamesWorker } from "@main/workers";
import { createGame } from "@main/services/library-sync";
import { Downloader, DownloadError, steamUrlBuilder } from "@shared";
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
import { Downloader, DownloadError } from "@shared";
import {
downloadsSublevel,
gamesShopAssetsSublevel,
gamesSublevel,
levelKeys,
} from "@main/level";
import { AxiosError } from "axios";
const startGameDownload = async (
@@ -36,27 +39,20 @@ const startGameDownload = async (
}
const game = await gamesSublevel.get(gameKey);
const gameAssets = await gamesShopAssetsSublevel.get(gameKey);
/* Delete any previous download */
await downloadsSublevel.del(gameKey);
if (game?.isDeleted) {
if (game) {
await gamesSublevel.put(gameKey, {
...game,
isDeleted: false,
});
} else {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
: null;
await gamesSublevel.put(gameKey, {
title,
iconUrl,
iconUrl: gameAssets?.iconUrl ?? null,
objectId,
shop,
remoteId: null,

View File

@@ -1,19 +1,8 @@
import { registerEvent } from "../register-event";
import AutoLaunch from "auto-launch";
import { app } from "electron";
import path from "path";
import fs from "node:fs";
import { logger } from "@main/services";
const windowsStartupPath = path.join(
app.getPath("appData"),
"Microsoft",
"Windows",
"Start Menu",
"Programs",
"Startup"
);
const autoLaunch = async (
_event: Electron.IpcMainInvokeEvent,
autoLaunchProps: { enabled: boolean; minimized: boolean }
@@ -30,10 +19,6 @@ const autoLaunch = async (
logger.error(err);
});
} else {
if (process.platform == "win32") {
fs.rm(path.join(windowsStartupPath, "Hydra.vbs"), () => {});
}
appLauncher.disable().catch((err) => {
logger.error(err);
});

View File

@@ -16,7 +16,7 @@ const updateUserPreferences = async (
if (preferences.language) {
await db.put<string, string>(levelKeys.language, preferences.language, {
valueEncoding: "utf-8",
valueEncoding: "utf8",
});
i18next.changeLanguage(preferences.language);

View File

@@ -1,97 +1,12 @@
import { registerEvent } from "../register-event";
import { HydraApi, logger } from "@main/services";
import { steamGamesWorker } from "@main/workers";
import { HydraApi } from "@main/services";
import type { UserProfile } from "@types";
import { steamUrlBuilder } from "@shared";
const getSteamGame = async (objectId: string) => {
try {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
return {
title: steamGame.name as string,
iconUrl: steamUrlBuilder.icon(objectId, steamGame.clientIcon),
};
} catch (err) {
logger.error("Failed to get Steam game", err);
return null;
}
};
const getUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
): Promise<UserProfile | null> => {
try {
const profile = await HydraApi.get<UserProfile | null>(`/users/${userId}`);
if (!profile) return null;
const recentGames = await Promise.all(
profile.recentGames
.map(async (game) => {
const steamGame = await getSteamGame(game.objectId);
return {
...game,
...steamGame,
};
})
.filter((game) => game)
);
const libraryGames = await Promise.all(
profile.libraryGames
.map(async (game) => {
const steamGame = await getSteamGame(game.objectId);
return {
...game,
...steamGame,
};
})
.filter((game) => game)
);
if (profile.currentGame) {
const steamGame = await getSteamGame(profile.currentGame.objectId);
if (steamGame) {
profile.currentGame = {
...profile.currentGame,
...steamGame,
};
}
}
const friends = await Promise.all(
profile.friends.map(async (friend) => {
if (!friend.currentGame) return friend;
const currentGame = await getSteamGame(friend.currentGame.objectId);
return {
...friend,
currentGame: {
...friend.currentGame,
...currentGame,
},
};
})
);
return {
...profile,
friends,
libraryGames,
recentGames,
};
} catch (err) {
return null;
}
return HydraApi.get<UserProfile>(`/users/${userId}`).catch(() => null);
};
registerEvent("getUser", getUser);

View File

@@ -0,0 +1,352 @@
// @generated by protobuf-ts 2.10.0
// @generated from protobuf file "envelope.proto" (syntax proto3)
// tslint:disable
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { IBinaryWriter } from "@protobuf-ts/runtime";
import { WireType } from "@protobuf-ts/runtime";
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
/**
* @generated from protobuf message FriendRequest
*/
export interface FriendRequest {
/**
* @generated from protobuf field: int32 friend_request_count = 1;
*/
friendRequestCount: number;
/**
* @generated from protobuf field: optional string sender_id = 2;
*/
senderId?: string;
}
/**
* @generated from protobuf message FriendGameSession
*/
export interface FriendGameSession {
/**
* @generated from protobuf field: string object_id = 1;
*/
objectId: string;
/**
* @generated from protobuf field: string shop = 2;
*/
shop: string;
/**
* @generated from protobuf field: string friend_id = 3;
*/
friendId: string;
}
/**
* @generated from protobuf message Envelope
*/
export interface Envelope {
/**
* @generated from protobuf oneof: payload
*/
payload:
| {
oneofKind: "friendRequest";
/**
* @generated from protobuf field: FriendRequest friend_request = 1;
*/
friendRequest: FriendRequest;
}
| {
oneofKind: "friendGameSession";
/**
* @generated from protobuf field: FriendGameSession friend_game_session = 2;
*/
friendGameSession: FriendGameSession;
}
| {
oneofKind: undefined;
};
}
// @generated message type with reflection information, may provide speed optimized methods
class FriendRequest$Type extends MessageType<FriendRequest> {
constructor() {
super("FriendRequest", [
{
no: 1,
name: "friend_request_count",
kind: "scalar",
T: 5 /*ScalarType.INT32*/,
},
{
no: 2,
name: "sender_id",
kind: "scalar",
opt: true,
T: 9 /*ScalarType.STRING*/,
},
]);
}
create(value?: PartialMessage<FriendRequest>): FriendRequest {
const message = globalThis.Object.create(this.messagePrototype!);
message.friendRequestCount = 0;
if (value !== undefined)
reflectionMergePartial<FriendRequest>(this, message, value);
return message;
}
internalBinaryRead(
reader: IBinaryReader,
length: number,
options: BinaryReadOptions,
target?: FriendRequest
): FriendRequest {
let message = target ?? this.create(),
end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* int32 friend_request_count */ 1:
message.friendRequestCount = reader.int32();
break;
case /* optional string sender_id */ 2:
message.senderId = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(
`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`
);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(
this.typeName,
message,
fieldNo,
wireType,
d
);
}
}
return message;
}
internalBinaryWrite(
message: FriendRequest,
writer: IBinaryWriter,
options: BinaryWriteOptions
): IBinaryWriter {
/* int32 friend_request_count = 1; */
if (message.friendRequestCount !== 0)
writer.tag(1, WireType.Varint).int32(message.friendRequestCount);
/* optional string sender_id = 2; */
if (message.senderId !== undefined)
writer.tag(2, WireType.LengthDelimited).string(message.senderId);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(
this.typeName,
message,
writer
);
return writer;
}
}
/**
* @generated MessageType for protobuf message FriendRequest
*/
export const FriendRequest = new FriendRequest$Type();
// @generated message type with reflection information, may provide speed optimized methods
class FriendGameSession$Type extends MessageType<FriendGameSession> {
constructor() {
super("FriendGameSession", [
{ no: 1, name: "object_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 2, name: "shop", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 3, name: "friend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
]);
}
create(value?: PartialMessage<FriendGameSession>): FriendGameSession {
const message = globalThis.Object.create(this.messagePrototype!);
message.objectId = "";
message.shop = "";
message.friendId = "";
if (value !== undefined)
reflectionMergePartial<FriendGameSession>(this, message, value);
return message;
}
internalBinaryRead(
reader: IBinaryReader,
length: number,
options: BinaryReadOptions,
target?: FriendGameSession
): FriendGameSession {
let message = target ?? this.create(),
end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string object_id */ 1:
message.objectId = reader.string();
break;
case /* string shop */ 2:
message.shop = reader.string();
break;
case /* string friend_id */ 3:
message.friendId = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(
`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`
);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(
this.typeName,
message,
fieldNo,
wireType,
d
);
}
}
return message;
}
internalBinaryWrite(
message: FriendGameSession,
writer: IBinaryWriter,
options: BinaryWriteOptions
): IBinaryWriter {
/* string object_id = 1; */
if (message.objectId !== "")
writer.tag(1, WireType.LengthDelimited).string(message.objectId);
/* string shop = 2; */
if (message.shop !== "")
writer.tag(2, WireType.LengthDelimited).string(message.shop);
/* string friend_id = 3; */
if (message.friendId !== "")
writer.tag(3, WireType.LengthDelimited).string(message.friendId);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(
this.typeName,
message,
writer
);
return writer;
}
}
/**
* @generated MessageType for protobuf message FriendGameSession
*/
export const FriendGameSession = new FriendGameSession$Type();
// @generated message type with reflection information, may provide speed optimized methods
class Envelope$Type extends MessageType<Envelope> {
constructor() {
super("Envelope", [
{
no: 1,
name: "friend_request",
kind: "message",
oneof: "payload",
T: () => FriendRequest,
},
{
no: 2,
name: "friend_game_session",
kind: "message",
oneof: "payload",
T: () => FriendGameSession,
},
]);
}
create(value?: PartialMessage<Envelope>): Envelope {
const message = globalThis.Object.create(this.messagePrototype!);
message.payload = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<Envelope>(this, message, value);
return message;
}
internalBinaryRead(
reader: IBinaryReader,
length: number,
options: BinaryReadOptions,
target?: Envelope
): Envelope {
let message = target ?? this.create(),
end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* FriendRequest friend_request */ 1:
message.payload = {
oneofKind: "friendRequest",
friendRequest: FriendRequest.internalBinaryRead(
reader,
reader.uint32(),
options,
(message.payload as any).friendRequest
),
};
break;
case /* FriendGameSession friend_game_session */ 2:
message.payload = {
oneofKind: "friendGameSession",
friendGameSession: FriendGameSession.internalBinaryRead(
reader,
reader.uint32(),
options,
(message.payload as any).friendGameSession
),
};
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(
`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`
);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(
this.typeName,
message,
fieldNo,
wireType,
d
);
}
}
return message;
}
internalBinaryWrite(
message: Envelope,
writer: IBinaryWriter,
options: BinaryWriteOptions
): IBinaryWriter {
/* FriendRequest friend_request = 1; */
if (message.payload.oneofKind === "friendRequest")
FriendRequest.internalBinaryWrite(
message.payload.friendRequest,
writer.tag(1, WireType.LengthDelimited).fork(),
options
).join();
/* FriendGameSession friend_game_session = 2; */
if (message.payload.oneofKind === "friendGameSession")
FriendGameSession.internalBinaryWrite(
message.payload.friendGameSession,
writer.tag(2, WireType.LengthDelimited).fork(),
options
).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(
this.typeName,
message,
writer
);
return writer;
}
}
/**
* @generated MessageType for protobuf message Envelope
*/
export const Envelope = new Envelope$Type();

View File

@@ -59,9 +59,11 @@ app.whenReady().then(async () => {
await loadState();
const language = await db.get<string, string>(levelKeys.language, {
valueEncoding: "utf-8",
});
const language = await db
.get<string, string>(levelKeys.language, {
valueEncoding: "utf8",
})
.catch(() => "en");
if (language) i18n.changeLanguage(language);

View File

@@ -0,0 +1,11 @@
import type { ShopAssets } from "@types";
import { db } from "../level";
import { levelKeys } from "./keys";
export const gamesShopAssetsSublevel = db.sublevel<string, ShopAssets>(
levelKeys.gameShopAssets,
{
valueEncoding: "json",
}
);

View File

@@ -1,5 +1,6 @@
export * from "./downloads";
export * from "./games";
export * from "./game-shop-assets";
export * from "./game-shop-cache";
export * from "./game-achievements";
export * from "./keys";

View File

@@ -6,6 +6,7 @@ export const levelKeys = {
user: "user",
auth: "auth",
themes: "themes",
gameShopAssets: "gameShopAssets",
gameShopCache: "gameShopCache",
gameShopCacheItem: (shop: GameShop, objectId: string, language: string) =>
`${shop}:${objectId}:${language}`,
@@ -14,4 +15,5 @@ export const levelKeys = {
userPreferences: "userPreferences",
language: "language",
screenState: "screenState",
rpcPassword: "rpcPassword",
};

View File

@@ -1,16 +1,25 @@
import { Aria2, DownloadManager, Ludusavi, startMainLoop } from "./services";
import { RealDebridClient } from "./services/download/real-debrid";
import { HydraApi } from "./services/hydra-api";
import { uploadGamesBatch } from "./services/library-sync";
import { downloadsSublevel } from "./level/sublevels/downloads";
import { sortBy } from "lodash-es";
import { Downloader } from "@shared";
import { levelKeys, db } from "./level";
import type { UserPreferences } from "@types";
import { TorBoxClient } from "./services/download/torbox";
import { CommonRedistManager } from "./services/common-redist-manager";
import {
WSClient,
SystemPath,
CommonRedistManager,
TorBoxClient,
RealDebridClient,
Aria2,
DownloadManager,
Ludusavi,
HydraApi,
uploadGamesBatch,
startMainLoop,
} from "@main/services";
export const loadState = async () => {
SystemPath.checkIfPathsAreAvailable();
const userPreferences = await db.get<string, UserPreferences | null>(
levelKeys.userPreferences,
{
@@ -34,6 +43,7 @@ export const loadState = async () => {
await HydraApi.setupApi().then(() => {
uploadGamesBatch();
WSClient.connect();
});
const downloads = await downloadsSublevel

View File

@@ -1,26 +1,26 @@
import path from "node:path";
import fs from "node:fs";
import { app } from "electron";
import type { Game, AchievementFile } from "@types";
import { Cracker } from "@shared";
import { achievementsLogger } from "../logger";
import { SystemPath } from "../system-path";
const getAppDataPath = () => {
if (process.platform === "win32") {
return app.getPath("appData");
return SystemPath.getPath("appData");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "AppData", "Roaming");
};
const getDocumentsPath = () => {
if (process.platform === "win32") {
return app.getPath("documents");
return SystemPath.getPath("documents");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "Documents");
};
@@ -38,7 +38,7 @@ const getLocalAppDataPath = () => {
return path.join(appData, "..", "Local");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "AppData", "Local");
};

View File

@@ -25,7 +25,7 @@ export const getGameAchievementData = async (
const language = await db
.get<string, string>(levelKeys.language, {
valueEncoding: "utf-8",
valueEncoding: "utf8",
})
.then((language) => language || "en");

View File

@@ -1,5 +1,4 @@
import { levelKeys, gamesSublevel, db } from "@main/level";
import { app } from "electron";
import path from "node:path";
import * as tar from "tar";
import crypto from "node:crypto";
@@ -15,6 +14,7 @@ import axios from "axios";
import { Ludusavi } from "./ludusavi";
import { formatDate, SubscriptionRequiredError } from "@shared";
import i18next, { t } from "i18next";
import { SystemPath } from "./system-path";
export class CloudSync {
public static getBackupLabel(automatic: boolean) {
@@ -102,7 +102,7 @@ export class CloudSync {
shop,
objectId,
hostname: os.hostname(),
homeDir: normalizePath(app.getPath("home")),
homeDir: normalizePath(SystemPath.getPath("home")),
downloadOptionTitle,
platform: os.platform(),
label,

View File

@@ -4,24 +4,21 @@ import fs from "node:fs";
import cp from "node:child_process";
import path from "node:path";
import { logger } from "./logger";
import { app } from "electron";
import { WindowManager } from "./window-manager";
import { SystemPath } from "./system-path";
export class CommonRedistManager {
private static readonly redistributables = [
"dotNetFx40_Full_setup.exe",
"dxwebsetup.exe",
"directx_Jun2010_redist.exe",
"oalinst.exe",
"install.bat",
"vcredist_2015-2019_x64.exe",
"vcredist_2015-2019_x86.exe",
"vcredist_x64.exe",
"vcredist_x86.exe",
"xnafx40_redist.msi",
"VisualCppRedist_AIO_x86_x64.exe",
];
private static readonly installationTimeout = 1000 * 60 * 5; // 5 minutes
private static readonly installationLog = path.join(
app.getPath("temp"),
SystemPath.getPath("temp"),
"common_redist_install.log"
);
@@ -47,6 +44,8 @@ export class CommonRedistManager {
fs.readFile(this.installationLog, "utf-8", (err, data) => {
if (err) return logger.error("Error reading log file:", err);
logger.log("Redist log file updated:", data);
const tail = data.split("\n").at(-2)?.trim();
if (tail?.includes(installationCompleteMessage)) {
@@ -92,7 +91,7 @@ export class CommonRedistManager {
for (const redist of this.redistributables) {
const filePath = path.join(commonRedistPath, redist);
if (fs.existsSync(filePath)) {
if (fs.existsSync(filePath) && redist !== "install.bat") {
continue;
}

View File

@@ -1 +1,3 @@
export * from "./download-manager";
export * from "./real-debrid";
export * from "./torbox";

View File

@@ -11,6 +11,7 @@ import { getUserData } from "./user/get-user-data";
import { db } from "@main/level";
import { levelKeys } from "@main/level/sublevels";
import type { Auth, User } from "@types";
import { WSClient } from "./ws/ws-client";
interface HydraApiOptions {
needsAuth?: boolean;
@@ -101,6 +102,8 @@ export class HydraApi {
WindowManager.mainWindow.webContents.send("on-signin");
await clearGamesRemoteIds();
uploadGamesBatch();
WSClient.close();
WSClient.connect();
}
}

View File

@@ -12,3 +12,6 @@ export * from "./7zip";
export * from "./game-files-manager";
export * from "./common-redist-manager";
export * from "./aria2";
export * from "./ws";
export * from "./system-path";
export * from "./library-sync";

View File

@@ -5,7 +5,7 @@ import { gamesSublevel, levelKeys } from "@main/level";
export const createGame = async (game: Game) => {
return HydraApi.post(`/profile/games`, {
objectId: game.objectId,
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds ?? 0),
shop: game.shop,
lastTimePlayed: game.lastTimePlayed,
}).then((response) => {

View File

@@ -1,10 +1,15 @@
import { ShopAssets } from "@types";
import { HydraApi } from "../hydra-api";
import { steamGamesWorker } from "@main/workers";
import { steamUrlBuilder } from "@shared";
import { gamesSublevel, levelKeys } from "@main/level";
import { gamesShopAssetsSublevel, gamesSublevel, levelKeys } from "@main/level";
type ProfileGame = {
id: string;
lastTimePlayed: Date | null;
playTimeInMilliseconds: number;
} & ShopAssets;
export const mergeWithRemoteGames = async () => {
return HydraApi.get("/profile/games")
return HydraApi.get<ProfileGame[]>("/profile/games")
.then(async (response) => {
for (const game of response) {
const localGame = await gamesSublevel.get(
@@ -31,25 +36,32 @@ export const mergeWithRemoteGames = async () => {
playTimeInMilliseconds: updatedPlayTime,
});
} else {
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(game.objectId, steamGame.clientIcon)
: null;
await gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
objectId: game.objectId,
title: steamGame?.name,
title: game.title,
remoteId: game.id,
shop: game.shop,
iconUrl,
iconUrl: game.iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
isDeleted: false,
});
}
await gamesShopAssetsSublevel.put(
levelKeys.game(game.shop, game.objectId),
{
shop: game.shop,
objectId: game.objectId,
title: game.title,
coverImageUrl: game.coverImageUrl,
libraryHeroImageUrl: game.libraryHeroImageUrl,
libraryImageUrl: game.libraryImageUrl,
logoImageUrl: game.logoImageUrl,
iconUrl: game.iconUrl,
logoPosition: game.logoPosition,
}
);
}
})
.catch(() => {});

View File

@@ -8,9 +8,13 @@ import YAML from "yaml";
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
import { LUDUSAVI_MANIFEST_URL } from "@main/constants";
import { SystemPath } from "./system-path";
export class Ludusavi {
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
private static ludusaviPath = path.join(
SystemPath.getPath("appData"),
"ludusavi"
);
private static ludusaviConfigPath = path.join(
this.ludusaviPath,
"config.yaml"

View File

@@ -3,7 +3,6 @@ import { DownloadManager } from "./download";
import { watchProcesses } from "./process-watcher";
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
import { UpdateManager } from "./update-manager";
import { watchFriendRequests } from "@main/events/profile/sync-friend-requests";
import { MAIN_LOOP_INTERVAL } from "@main/constants";
export const startMainLoop = async () => {
@@ -11,7 +10,6 @@ export const startMainLoop = async () => {
while (true) {
await Promise.allSettled([
watchProcesses(),
watchFriendRequests(),
DownloadManager.watchDownloads(),
AchievementWatcherManager.watchAchievements(),
DownloadManager.getSeedStatus(),

View File

@@ -1,4 +1,4 @@
import { Notification, app } from "electron";
import { Notification } from "electron";
import { t } from "i18next";
import trayIcon from "@resources/tray-icon.png?asset";
import fs from "node:fs";
@@ -10,16 +10,17 @@ import icon from "@resources/icon.png?asset";
import { NotificationOptions, toXmlString } from "./xml";
import { logger } from "../logger";
import { WindowManager } from "../window-manager";
import type { Game, UserPreferences } from "@types";
import type { Game, GameStats, UserPreferences, UserProfile } from "@types";
import { db, levelKeys } from "@main/level";
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
import { SystemPath } from "../system-path";
async function downloadImage(url: string | null) {
if (!url) return undefined;
if (!url.startsWith("http")) return undefined;
const fileName = url.split("/").pop()!;
const outputPath = path.join(app.getPath("temp"), fileName);
const outputPath = path.join(SystemPath.getPath("temp"), fileName);
const writer = fs.createWriteStream(outputPath);
const response = await axios.get(url, {
@@ -80,7 +81,9 @@ export const publishNotificationUpdateReadyToInstall = async (
.show();
};
export const publishNewFriendRequestNotification = async () => {
export const publishNewFriendRequestNotification = async (
user: UserProfile
) => {
const userPreferences = await db.get<string, UserPreferences | null>(
levelKeys.userPreferences,
{
@@ -96,8 +99,27 @@ export const publishNewFriendRequestNotification = async () => {
}),
body: t("new_friend_request_description", {
ns: "notifications",
displayName: user.displayName,
}),
icon: trayIcon,
icon: user?.profileImageUrl
? await downloadImage(user.profileImageUrl)
: trayIcon,
}).show();
};
export const publishFriendStartedPlayingGameNotification = async (
friend: UserProfile,
game: GameStats
) => {
new Notification({
title: t("friend_started_playing_game", {
ns: "notifications",
displayName: friend.displayName,
}),
body: game.assets?.title,
icon: friend?.profileImageUrl
? await downloadImage(friend.profileImageUrl)
: trayIcon,
}).show();
};

View File

@@ -7,7 +7,8 @@ import crypto from "node:crypto";
import { pythonRpcLogger } from "./logger";
import { Readable } from "node:stream";
import { app, dialog } from "electron";
import { app, dialog, safeStorage } from "electron";
import { db, levelKeys } from "@main/level";
interface GamePayload {
game_id: string;
@@ -30,17 +31,12 @@ const rustBinaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
export class PythonRPC {
public static readonly BITTORRENT_PORT = "5881";
public static readonly RPC_PORT = "8084";
private static readonly RPC_PASSWORD = crypto.randomBytes(32).toString("hex");
private static pythonProcess: cp.ChildProcess | null = null;
public static readonly rpc = axios.create({
baseURL: `http://localhost:${this.RPC_PORT}`,
headers: {
"x-hydra-rpc-password": this.RPC_PASSWORD,
},
});
private static pythonProcess: cp.ChildProcess | null = null;
private static logStderr(readable: Readable | null) {
if (!readable) return;
@@ -48,14 +44,37 @@ export class PythonRPC {
readable.on("data", pythonRpcLogger.log);
}
public static spawn(
private static async getRPCPassword() {
const existingPassword = await db.get(levelKeys.rpcPassword, {
valueEncoding: "utf8",
});
if (existingPassword)
return safeStorage.decryptString(Buffer.from(existingPassword, "hex"));
const newPassword = crypto.randomBytes(32).toString("hex");
await db.put(
levelKeys.rpcPassword,
safeStorage.encryptString(newPassword).toString("hex"),
{
valueEncoding: "utf8",
}
);
return newPassword;
}
public static async spawn(
initialDownload?: GamePayload,
initialSeeding?: GamePayload[]
) {
const rpcPassword = await this.getRPCPassword();
const commonArgs = [
this.BITTORRENT_PORT,
this.RPC_PORT,
this.RPC_PASSWORD,
rpcPassword,
initialDownload ? JSON.stringify(initialDownload) : "",
initialSeeding ? JSON.stringify(initialSeeding) : "",
app.isPackaged
@@ -116,6 +135,8 @@ export class PythonRPC {
this.pythonProcess = childProcess;
}
this.rpc.defaults.headers.common["x-hydra-rpc-password"] = rpcPassword;
}
public static kill() {

View File

@@ -0,0 +1,45 @@
import { app, dialog } from "electron";
import { logger } from "./logger";
export class SystemPath {
static readonly paths = {
userData: "userData",
downloads: "downloads",
documents: "documents",
desktop: "desktop",
home: "home",
appData: "appData",
temp: "temp",
};
static checkIfPathsAreAvailable() {
const paths = Object.keys(SystemPath.paths) as Array<
keyof typeof SystemPath.paths
>;
paths.forEach((pathName) => {
try {
app.getPath(pathName);
} catch (error) {
logger.error(`Error getting path ${pathName}`);
if (error instanceof Error) {
logger.error(error.message, error.stack);
}
dialog.showErrorBox(
`Hydra was not able to find path for '${pathName}' system folder`,
`Some functionalities may not work as expected.\nPlease check your system settings.`
);
}
});
}
static getPath(pathName: keyof typeof SystemPath.paths): string {
try {
return app.getPath(pathName);
} catch (error) {
logger.error(`Error getting path: ${error}`);
return "";
}
}
}

View File

@@ -16,7 +16,7 @@ import trayIcon from "@resources/tray-icon.png?asset";
import { HydraApi } from "./hydra-api";
import UserAgent from "user-agents";
import { db, gamesSublevel, levelKeys } from "@main/level";
import { slice, sortBy } from "lodash-es";
import { orderBy, slice } from "lodash-es";
import type { ScreenState, UserPreferences } from "@types";
import { AuthPage } from "@shared";
import { isStaging } from "@main/constants";
@@ -370,9 +370,9 @@ export class WindowManager {
!game.isDeleted && game.executablePath && game.lastTimePlayed
);
const sortedGames = sortBy(filteredGames, "lastTimePlayed", "DESC");
const sortedGames = orderBy(filteredGames, "lastTimePlayed", "desc");
return slice(sortedGames, 0, 5);
return slice(sortedGames, 0, 6);
});
const recentlyPlayedGames: Array<MenuItemConstructorOptions | MenuItem> =

View File

@@ -0,0 +1,17 @@
import type { FriendGameSession } from "@main/generated/envelope";
import { HydraApi } from "@main/services/hydra-api";
import { publishFriendStartedPlayingGameNotification } from "@main/services/notifications";
import { GameStats } from "@types";
export const friendGameSessionEvent = async (payload: FriendGameSession) => {
const [friend, gameStats] = await Promise.all([
HydraApi.get(`/users/${payload.friendId}`),
HydraApi.get<GameStats>(
`/games/stats?objectId=${payload.objectId}&shop=steam`
),
]);
if (friend && gameStats) {
publishFriendStartedPlayingGameNotification(friend, gameStats);
}
};

View File

@@ -0,0 +1,16 @@
import type { FriendRequest } from "@main/generated/envelope";
import { HydraApi } from "@main/services/hydra-api";
import { publishNewFriendRequestNotification } from "@main/services/notifications";
import { WindowManager } from "@main/services/window-manager";
export const friendRequestEvent = async (payload: FriendRequest) => {
WindowManager.mainWindow?.webContents.send("on-sync-friend-requests", {
friendRequestCount: payload.friendRequestCount,
});
const user = await HydraApi.get(`/users/${payload.senderId}`);
if (user) {
publishNewFriendRequestNotification(user);
}
};

View File

@@ -0,0 +1 @@
export * from "./ws-client";

View File

@@ -0,0 +1,119 @@
import { WebSocket } from "ws";
import { HydraApi } from "../hydra-api";
import { Envelope } from "@main/generated/envelope";
import { logger } from "../logger";
import { friendRequestEvent } from "./events/friend-request";
import { friendGameSessionEvent } from "./events/friend-game-session";
export class WSClient {
private static ws: WebSocket | null = null;
private static reconnectInterval = 1_000;
private static readonly maxReconnectInterval = 30_000;
private static shouldReconnect = true;
private static reconnecting = false;
private static heartbeatInterval: NodeJS.Timeout | null = null;
static async connect() {
this.shouldReconnect = true;
try {
const { token } = await HydraApi.post<{ token: string }>("/auth/ws");
this.ws = new WebSocket(import.meta.env.MAIN_VITE_WS_URL, {
headers: {
Authorization: `Bearer ${token}`,
},
});
this.ws.on("open", () => {
logger.info("WS connected");
this.reconnectInterval = 1000;
this.reconnecting = false;
this.startHeartbeat();
});
this.ws.on("message", (message) => {
const envelope = Envelope.fromBinary(
new Uint8Array(Buffer.from(message.toString()))
);
logger.info("Received WS envelope:", envelope);
if (envelope.payload.oneofKind === "friendRequest") {
friendRequestEvent(envelope.payload.friendRequest);
}
if (envelope.payload.oneofKind === "friendGameSession") {
friendGameSessionEvent(envelope.payload.friendGameSession);
}
});
this.ws.on("close", () => this.handleDisconnect("close"));
this.ws.on("error", (err) => {
logger.error("WS error:", err);
this.handleDisconnect("error");
});
} catch (err) {
logger.error("Failed to connect WS:", err);
this.handleDisconnect("auth-failed");
}
}
private static handleDisconnect(reason: string) {
logger.warn(`WS disconnected due to ${reason}`);
if (this.shouldReconnect) {
this.cleanupSocket();
this.tryReconnect();
}
}
private static async tryReconnect() {
if (this.reconnecting) return;
this.reconnecting = true;
logger.info(`Reconnecting in ${this.reconnectInterval / 1000}s...`);
setTimeout(async () => {
try {
await this.connect();
} catch (err) {
logger.error("Reconnect failed:", err);
this.reconnectInterval = Math.min(
this.reconnectInterval * 2,
this.maxReconnectInterval
);
this.reconnecting = false;
this.tryReconnect();
}
}, this.reconnectInterval);
}
private static cleanupSocket() {
if (this.ws) {
this.ws.removeAllListeners();
this.ws.close();
this.ws = null;
}
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
}
public static close() {
this.shouldReconnect = false;
this.reconnecting = false;
this.cleanupSocket();
}
private static startHeartbeat() {
this.heartbeatInterval = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.ping();
}
}, 15_000);
}
}

View File

@@ -6,6 +6,7 @@ interface ImportMetaEnv {
readonly MAIN_VITE_AUTH_URL: string;
readonly MAIN_VITE_CHECKOUT_URL: string;
readonly MAIN_VITE_EXTERNAL_RESOURCES_URL: string;
readonly MAIN_VITE_WS_URL: string;
}
interface ImportMeta {

View File

@@ -1,14 +0,0 @@
import path from "node:path";
import steamGamesWorkerPath from "./steam-games.worker?modulePath";
import Piscina from "piscina";
import { seedsPath } from "@main/constants";
export const steamGamesWorker = new Piscina({
filename: steamGamesWorkerPath,
workerData: {
steamGamesPath: path.join(seedsPath, "steam-games.json"),
},
maxThreads: 1,
});

View File

@@ -1,17 +0,0 @@
import type { SteamGame } from "@types";
import { slice } from "lodash-es";
import fs from "node:fs";
import { workerData } from "node:worker_threads";
const { steamGamesPath } = workerData;
const data = fs.readFileSync(steamGamesPath, "utf-8");
const steamGames = JSON.parse(data) as SteamGame[];
export const getById = (id: number) =>
steamGames.find((game) => game.id === id);
export const list = ({ limit, offset }: { limit: number; offset: number }) =>
slice(steamGames, offset, offset + limit);

View File

@@ -16,6 +16,8 @@ import type {
GameAchievement,
Theme,
FriendRequestSync,
ShortcutLocation,
ShopAssets,
} from "@types";
import type { AuthPage, CatalogueCategory } from "@shared";
import type { AxiosProgressEvent } from "axios";
@@ -63,6 +65,8 @@ contextBridge.exposeInMainWorld("electron", {
ipcRenderer.invoke("searchGames", payload, take, skip),
getCatalogue: (category: CatalogueCategory) =>
ipcRenderer.invoke("getCatalogue", category),
saveGameShopAssets: (objectId: string, shop: GameShop, assets: ShopAssets) =>
ipcRenderer.invoke("saveGameShopAssets", objectId, shop, assets),
getGameShopDetails: (objectId: string, shop: GameShop, language: string) =>
ipcRenderer.invoke("getGameShopDetails", objectId, shop, language),
getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
@@ -122,8 +126,11 @@ contextBridge.exposeInMainWorld("electron", {
),
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
createGameShortcut: (shop: GameShop, objectId: string) =>
ipcRenderer.invoke("createGameShortcut", shop, objectId),
createGameShortcut: (
shop: GameShop,
objectId: string,
location: ShortcutLocation
) => ipcRenderer.invoke("createGameShortcut", shop, objectId, location),
updateExecutablePath: (
shop: GameShop,
objectId: string,

View File

@@ -20,7 +20,6 @@ import {
setUserDetails,
setProfileBackground,
setGameRunning,
setFriendRequestCount,
} from "@renderer/features";
import { useTranslation } from "react-i18next";
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
@@ -155,16 +154,6 @@ export function App() {
});
}, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
useEffect(() => {
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
dispatch(setFriendRequestCount(result.friendRequestCount));
});
return () => {
unsubscribe();
};
}, [dispatch]);
useEffect(() => {
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
if (gamesRunning.length) {

View File

@@ -9,7 +9,6 @@ import { useTranslation } from "react-i18next";
import { Badge } from "../badge/badge";
import { useCallback, useState, useMemo } from "react";
import { useFormat, useRepacks } from "@renderer/hooks";
import { steamUrlBuilder } from "@shared";
export interface GameCardProps
extends React.DetailedHTMLProps<
@@ -63,7 +62,7 @@ export function GameCard({ game, ...props }: GameCardProps) {
>
<div className="game-card__backdrop">
<img
src={steamUrlBuilder.library(game.objectId)}
src={game.libraryImageUrl}
alt={game.title}
className="game-card__cover"
loading="lazy"

View File

@@ -33,29 +33,27 @@ export function Hero() {
}
if (featuredGameDetails?.length) {
return featuredGameDetails.map((game, index) => (
return featuredGameDetails.map((game) => (
<button
type="button"
onClick={() => navigate(game.uri)}
className="hero"
key={index}
key={game.uri}
>
<div className="hero__backdrop">
<img
src={game.background}
alt={game.description}
src={game.libraryHeroImageUrl}
alt={game.description ?? ""}
className="hero__media"
/>
<div className="hero__content">
{game.logo && (
<img
src={game.logo}
width="250px"
alt={game.description}
loading="eager"
/>
)}
<img
src={game.logoImageUrl}
width="250px"
alt={game.description ?? ""}
loading="eager"
/>
<p className="hero__description">{game.description}</p>
</div>
</div>

View File

@@ -23,6 +23,8 @@ import { sortBy } from "lodash-es";
import cn from "classnames";
import { CommentDiscussionIcon } from "@primer/octicons-react";
import { SidebarGameItem } from "./sidebar-game-item";
import { setFriendRequestCount } from "@renderer/features/user-details-slice";
import { useDispatch } from "react-redux";
const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_INITIAL_WIDTH = 250;
@@ -33,6 +35,8 @@ const initialSidebarWidth = window.localStorage.getItem("sidebarWidth");
export function Sidebar() {
const filterRef = useRef<HTMLInputElement>(null);
const dispatch = useDispatch();
const { t } = useTranslation("sidebar");
const { library, updateLibrary } = useLibrary();
const navigate = useNavigate();
@@ -60,6 +64,16 @@ export function Sidebar() {
updateLibrary();
}, [lastPacket?.gameId, updateLibrary]);
useEffect(() => {
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
dispatch(setFriendRequestCount(result.friendRequestCount));
});
return () => {
unsubscribe();
};
}, [dispatch]);
const sidebarRef = useRef<HTMLElement>(null);
const cursorPos = useRef({ x: 0 });

View File

@@ -21,7 +21,7 @@ import type {
GameShop,
GameStats,
LibraryGame,
ShopDetails,
ShopDetailsWithAssets,
UserAchievement,
} from "@types";
@@ -69,7 +69,9 @@ export function GameDetailsContextProvider({
gameTitle,
shop,
}: Readonly<GameDetailsContextProps>) {
const [shopDetails, setShopDetails] = useState<ShopDetails | null>(null);
const [shopDetails, setShopDetails] = useState<ShopDetailsWithAssets | null>(
null
);
const [achievements, setAchievements] = useState<UserAchievement[] | null>(
null
);
@@ -79,7 +81,7 @@ export function GameDetailsContextProvider({
const [stats, setStats] = useState<GameStats | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [gameColor, setGameColor] = useState("");
const [isGameRunning, setIsGameRunning] = useState(false);
const [showRepacksModal, setShowRepacksModal] = useState(false);
@@ -120,7 +122,7 @@ export function GameDetailsContextProvider({
const abortController = new AbortController();
abortControllerRef.current = abortController;
window.electron
const shopDetailsPromise = window.electron
.getGameShopDetails(objectId, shop, getSteamLanguage(i18n.language))
.then((result) => {
if (abortController.signal.aborted) return;
@@ -135,15 +137,42 @@ export function GameDetailsContextProvider({
) {
setHasNSFWContentBlocked(true);
}
})
.finally(() => {
setIsLoading(false);
if (result?.assets) {
setIsLoading(false);
}
});
window.electron.getGameStats(objectId, shop).then((result) => {
if (abortController.signal.aborted) return;
setStats(result);
});
const statsPromise = window.electron
.getGameStats(objectId, shop)
.then((result) => {
if (abortController.signal.aborted) return null;
setStats(result);
return result;
});
Promise.all([shopDetailsPromise, statsPromise])
.then(([_, stats]) => {
if (stats) {
const assets = stats.assets;
if (assets) {
window.electron.saveGameShopAssets(objectId, shop, assets);
setShopDetails((prev) => {
if (!prev) return null;
console.log("assets", assets);
return {
...prev,
assets,
};
});
}
}
})
.finally(() => {
if (abortController.signal.aborted) return;
setIsLoading(false);
});
if (userDetails) {
window.electron

View File

@@ -3,13 +3,13 @@ import type {
GameShop,
GameStats,
LibraryGame,
ShopDetails,
ShopDetailsWithAssets,
UserAchievement,
} from "@types";
export interface GameDetailsContext {
game: LibraryGame | null;
shopDetails: ShopDetails | null;
shopDetails: ShopDetailsWithAssets | null;
repacks: GameRepack[];
shop: GameShop;
gameTitle: string;

View File

@@ -3,7 +3,6 @@ import type {
AppUpdaterEvent,
GameShop,
HowLongToBeatCategory,
ShopDetails,
Steam250Game,
DownloadProgress,
SeedingStatus,
@@ -32,6 +31,10 @@ import type {
Theme,
Badge,
Auth,
ShortcutLocation,
CatalogueSearchResult,
ShopAssets,
ShopDetailsWithAssets,
} from "@types";
import type { AxiosProgressEvent } from "axios";
import type disk from "diskusage";
@@ -68,13 +71,18 @@ declare global {
payload: CatalogueSearchPayload,
take: number,
skip: number
) => Promise<{ edges: any[]; count: number }>;
getCatalogue: (category: CatalogueCategory) => Promise<any[]>;
) => Promise<{ edges: CatalogueSearchResult[]; count: number }>;
getCatalogue: (category: CatalogueCategory) => Promise<ShopAssets[]>;
saveGameShopAssets: (
objectId: string,
shop: GameShop,
assets: ShopAssets
) => Promise<void>;
getGameShopDetails: (
objectId: string,
shop: GameShop,
language: string
) => Promise<ShopDetails | null>;
) => Promise<ShopDetailsWithAssets | null>;
getRandomGame: () => Promise<Steam250Game>;
getHowLongToBeat: (
objectId: string,
@@ -101,7 +109,11 @@ declare global {
objectId: string,
title: string
) => Promise<void>;
createGameShortcut: (shop: GameShop, objectId: string) => Promise<boolean>;
createGameShortcut: (
shop: GameShop,
objectId: string,
location: ShortcutLocation
) => Promise<boolean>;
updateExecutablePath: (
shop: GameShop,
objectId: string,

View File

@@ -26,7 +26,7 @@ export const toastSlice = createSlice({
state.title = action.payload.title;
state.message = action.payload.message;
state.type = action.payload.type;
state.duration = action.payload.duration ?? 2000;
state.duration = action.payload.duration ?? 3000;
state.visible = true;
},
closeToast: (state) => {

View File

@@ -1,6 +1,5 @@
import { setHeaderTitle } from "@renderer/features";
import { useAppDispatch, useUserDetails } from "@renderer/hooks";
import { steamUrlBuilder } from "@shared";
import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
@@ -120,8 +119,15 @@ export function AchievementsContent({
const containerRef = useRef<HTMLDivElement | null>(null);
const [isHeaderStuck, setIsHeaderStuck] = useState(false);
const { gameTitle, objectId, shop, achievements, gameColor, setGameColor } =
useContext(gameDetailsContext);
const {
gameTitle,
objectId,
shop,
shopDetails,
achievements,
gameColor,
setGameColor,
} = useContext(gameDetailsContext);
const dispatch = useAppDispatch();
@@ -131,10 +137,13 @@ export function AchievementsContent({
}, [dispatch, gameTitle]);
const handleHeroLoad = async () => {
const output = await average(steamUrlBuilder.libraryHero(objectId!), {
amount: 1,
format: "hex",
});
const output = await average(
shopDetails?.assets?.libraryHeroImageUrl ?? "",
{
amount: 1,
format: "hex",
}
);
const backgroundColor = output
? (new Color(output).darken(0.7).toString() as string)
@@ -179,7 +188,7 @@ export function AchievementsContent({
return (
<div className="achievements-content__achievements-list">
<img
src={steamUrlBuilder.libraryHero(objectId)}
src={shopDetails?.assets?.libraryHeroImageUrl ?? ""}
className="achievements-content__achievements-list__image"
alt={gameTitle}
onLoad={handleHeroLoad}
@@ -205,7 +214,7 @@ export function AchievementsContent({
to={buildGameDetailsPath({ shop, objectId, title: gameTitle })}
>
<img
src={steamUrlBuilder.logo(objectId)}
src={shopDetails?.assets?.logoImageUrl ?? ""}
className="achievements-content__achievements-list__section__container__hero__content__game-logo"
alt={gameTitle}
/>

View File

@@ -1,4 +1,4 @@
import type { DownloadSource } from "@types";
import type { CatalogueSearchResult, DownloadSource } from "@types";
import {
useAppDispatch,
@@ -44,7 +44,7 @@ export default function Catalogue() {
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [results, setResults] = useState<any[]>([]);
const [results, setResults] = useState<CatalogueSearchResult[]>([]);
const [itemsCount, setItemsCount] = useState(0);

View File

@@ -25,6 +25,21 @@
border-right: 1px solid globals.$border-color;
}
&__cover-placeholder {
display: flex;
align-items: center;
justify-content: center;
color: globals.$body-color;
width: 200px;
height: 103px;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0.05) 50%,
rgba(255, 255, 255, 0.1) 100%
);
}
&__details {
display: flex;
flex-direction: column;

View File

@@ -1,15 +1,16 @@
import { Badge } from "@renderer/components";
import { buildGameDetailsPath } from "@renderer/helpers";
import { useAppSelector, useRepacks } from "@renderer/hooks";
import { steamUrlBuilder } from "@shared";
import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import "./game-item.scss";
import { useTranslation } from "react-i18next";
import { CatalogueSearchResult } from "@types";
import { QuestionIcon } from "@primer/octicons-react";
export interface GameItemProps {
game: any;
game: CatalogueSearchResult;
}
export function GameItem({ game }: GameItemProps) {
@@ -43,18 +44,32 @@ export function GameItem({ game }: GameItemProps) {
});
}, [game.genres, language, steamGenres]);
const libraryImage = useMemo(() => {
if (game.libraryImageUrl) {
return (
<img
className="game-item__cover"
src={game.libraryImageUrl}
alt={game.title}
loading="lazy"
/>
);
}
return (
<div className="game-item__cover-placeholder">
<QuestionIcon size={28} />
</div>
);
}, [game.libraryImageUrl, game.title]);
return (
<button
type="button"
className="game-item"
onClick={() => navigate(buildGameDetailsPath(game))}
>
<img
className="game-item__cover"
src={steamUrlBuilder.library(game.objectId)}
alt={game.title}
loading="lazy"
/>
{libraryImage}
<div className="game-item__details">
<span>{game.title}</span>

View File

@@ -9,7 +9,7 @@ import {
formatDownloadProgress,
} from "@renderer/helpers";
import { Downloader, formatBytes, steamUrlBuilder } from "@shared";
import { Downloader, formatBytes } from "@shared";
import { DOWNLOADER_NAME } from "@renderer/constants";
import { useAppSelector, useDownload, useLibrary } from "@renderer/hooks";
@@ -319,7 +319,7 @@ export function DownloadGroup({
<div className="download-group__cover">
<div className="download-group__cover-backdrop">
<img
src={steamUrlBuilder.library(game.objectId)}
src={game.libraryImageUrl ?? ""}
className="download-group__cover-image"
alt={game.title}
/>

View File

@@ -9,7 +9,7 @@ import { Sidebar } from "./sidebar/sidebar";
import { useTranslation } from "react-i18next";
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
import { AuthPage, steamUrlBuilder } from "@shared";
import { AuthPage } from "@shared";
import cloudIconAnimated from "@renderer/assets/icons/cloud-animated.gif";
import { useUserDetails } from "@renderer/hooks";
@@ -59,10 +59,13 @@ export function GameDetailsContent() {
const [backdropOpacity, setBackdropOpacity] = useState(1);
const handleHeroLoad = async () => {
const output = await average(steamUrlBuilder.libraryHero(objectId!), {
amount: 1,
format: "hex",
});
const output = await average(
shopDetails?.assets?.libraryHeroImageUrl ?? "",
{
amount: 1,
format: "hex",
}
);
const backgroundColor = output
? new Color(output).darken(0.7).toString()
@@ -100,7 +103,7 @@ export function GameDetailsContent() {
<section className="game-details__container">
<div ref={heroRef} className="game-details__hero">
<img
src={steamUrlBuilder.libraryHero(objectId!)}
src={shopDetails?.assets?.libraryHeroImageUrl ?? ""}
className="game-details__hero-image"
alt={game?.title}
onLoad={handleHeroLoad}
@@ -119,7 +122,7 @@ export function GameDetailsContent() {
>
<div className="game-details__hero-content">
<img
src={steamUrlBuilder.logo(objectId!)}
src={shopDetails?.assets?.logoImageUrl ?? ""}
className="game-details__game-logo"
alt={game?.title}
/>

View File

@@ -1,7 +1,7 @@
import { useContext, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button, CheckboxField, Modal, TextField } from "@renderer/components";
import type { LibraryGame } from "@types";
import type { LibraryGame, ShortcutLocation } from "@types";
import { gameDetailsContext } from "@renderer/context";
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
import { useDownload, useToast, useUserDetails } from "@renderer/hooks";
@@ -107,15 +107,18 @@ export function GameOptionsModal({
}
};
const handleCreateShortcut = async () => {
const handleCreateShortcut = async (location: ShortcutLocation) => {
window.electron
.createGameShortcut(game.shop, game.objectId)
.createGameShortcut(game.shop, game.objectId, location)
.then((success) => {
if (success) {
showSuccessToast(t("create_shortcut_success"));
} else {
showErrorToast(t("create_shortcut_error"));
}
})
.catch(() => {
showErrorToast(t("create_shortcut_error"));
});
};
@@ -176,6 +179,9 @@ export function GameOptionsModal({
const shouldShowWinePrefixConfiguration =
window.electron.platform === "linux";
const shouldShowCreateStartMenuShortcut =
window.electron.platform === "win32";
const handleResetAchievements = async () => {
setIsDeletingAchievements(true);
try {
@@ -278,9 +284,20 @@ export function GameOptionsModal({
>
{t("open_folder")}
</Button>
<Button onClick={handleCreateShortcut} theme="outline">
<Button
onClick={() => handleCreateShortcut("desktop")}
theme="outline"
>
{t("create_shortcut")}
</Button>
{shouldShowCreateStartMenuShortcut && (
<Button
onClick={() => handleCreateShortcut("start_menu")}
theme="outline"
>
{t("create_start_menu_shortcut")}
</Button>
)}
</div>
)}
</div>

View File

@@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
import { Button, GameCard, Hero } from "@renderer/components";
import type { Steam250Game } from "@types";
import type { ShopAssets, Steam250Game } from "@types";
import flameIconStatic from "@renderer/assets/icons/flame-static.png";
import flameIconAnimated from "@renderer/assets/icons/flame-animated.gif";
@@ -27,7 +27,9 @@ export default function Home() {
CatalogueCategory.Hot
);
const [catalogue, setCatalogue] = useState<Record<CatalogueCategory, any[]>>({
const [catalogue, setCatalogue] = useState<
Record<CatalogueCategory, ShopAssets[]>
>({
[CatalogueCategory.Hot]: [],
[CatalogueCategory.Weekly]: [],
[CatalogueCategory.Achievements]: [],

View File

@@ -12,7 +12,6 @@ import { userProfileContext } from "@renderer/context";
import { ClockIcon, TrophyIcon } from "@primer/octicons-react";
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
import { useTranslation } from "react-i18next";
import { steamUrlBuilder } from "@shared";
import "./user-library-game-card.scss";
interface UserLibraryGameCardProps {
@@ -150,7 +149,7 @@ export function UserLibraryGameCard({
</div>
<img
src={steamUrlBuilder.cover(game.objectId)}
src={game.coverImageUrl}
alt={game.title}
className="user-library-game__game-image"
/>

View File

@@ -1,5 +1,7 @@
export type GameShop = "steam" | "epic";
export type ShortcutLocation = "desktop" | "start_menu";
export interface UnlockedAchievement {
name: string;
unlockTime: number;

View File

@@ -36,27 +36,41 @@ export interface DownloadSource {
updatedAt: Date;
}
export interface ShopAssets {
objectId: string;
shop: GameShop;
title: string;
iconUrl: string | null;
libraryHeroImageUrl: string;
libraryImageUrl: string;
logoImageUrl: string;
logoPosition: string | null;
coverImageUrl: string;
}
export type ShopDetails = SteamAppDetails & {
objectId: string;
};
export type ShopDetailsWithAssets = ShopDetails & {
assets: ShopAssets | null;
};
export interface TorrentFile {
path: string;
length: number;
}
export interface UserGame {
export type UserGame = {
objectId: string;
shop: GameShop;
title: string;
iconUrl: string | null;
cover: string;
playTimeInSeconds: number;
lastTimePlayed: Date | null;
unlockedAchievementCount: number;
achievementCount: number;
achievementsPointsEarnedSum: number;
}
} & ShopAssets;
export interface GameRunning {
id: string;
@@ -100,13 +114,11 @@ export interface UserFriend {
profileImageUrl: string | null;
createdAt: string;
updatedAt: string;
currentGame: {
title: string;
iconUrl: string;
objectId: string;
shop: GameShop;
sessionDurationInSeconds: number;
} | null;
currentGame:
| (ShopAssets & {
sessionDurationInSeconds: number;
})
| null;
}
export interface UserFriends {
@@ -138,10 +150,10 @@ export interface UserRelation {
updatedAt: string;
}
export interface UserProfileCurrentGame extends Omit<GameRunning, "objectId"> {
objectId: string;
sessionDurationInSeconds: number;
}
export type UserProfileCurrentGame = GameRunning &
ShopAssets & {
sessionDurationInSeconds: number;
};
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
@@ -215,13 +227,12 @@ export interface DownloadSourceValidationResult {
export interface GameStats {
downloadCount: number;
playerCount: number;
assets: ShopAssets | null;
}
export interface TrendingGame {
export interface TrendingGame extends ShopAssets {
description: string | null;
uri: string;
description: string;
background: string;
logo: string | null;
}
export interface UserStatsPercentile {
@@ -302,10 +313,25 @@ export interface CatalogueSearchPayload {
developers: string[];
}
export interface LibraryGame extends Game {
export type CatalogueSearchResult = {
id: string;
download: Download | null;
}
tags: string[];
genres: string[];
objectId: string;
shop: GameShop;
createdAt: Date;
updatedAt: Date;
title: string;
installCount: number;
achievementCount: number;
shopData: string;
} & ShopAssets;
export type LibraryGame = Game &
Partial<ShopAssets> & {
id: string;
download: Download | null;
};
export * from "./game.types";
export * from "./steam.types";

View File

@@ -2004,6 +2004,42 @@
resolved "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.9.0.tgz"
integrity sha512-Uk4XrHyfylyfzZN9d8VFjF8FpfYHEyT4sabw+9+oP+GWAJHhPvNPTz6gXvUzJZmoblAvgcTrDslIPjz8zMh76w==
"@protobuf-ts/plugin-framework@^2.10.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin-framework/-/plugin-framework-2.10.0.tgz#e716a4b7fdb8710527101d145a000e254731abbb"
integrity sha512-EuW9irbt+w7Ml1CaAxK6xyl7pSuWVbNy0rsChxJEthMrAVTN5EPdJ3whNWvsRBa+HwRImEl8KHNnRoq/vGOHbg==
dependencies:
"@protobuf-ts/runtime" "^2.10.0"
typescript "^3.9"
"@protobuf-ts/plugin@^2.10.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin/-/plugin-2.10.0.tgz#dc91af30aeecf2a9debe2d0abf5b2a07a25156c5"
integrity sha512-iMX4C4TVfMNRLn2msK0cVg5jmizjtu5FYiy8EK5Lg6EgyR9TVHeK2rzmufWKYM2Pcg1jSwC0cFcXHQnCoeFxUg==
dependencies:
"@protobuf-ts/plugin-framework" "^2.10.0"
"@protobuf-ts/protoc" "^2.10.0"
"@protobuf-ts/runtime" "^2.10.0"
"@protobuf-ts/runtime-rpc" "^2.10.0"
typescript "^3.9"
"@protobuf-ts/protoc@^2.10.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@protobuf-ts/protoc/-/protoc-2.10.0.tgz#4a280ead9d5b143b6ef863339980050184bcac49"
integrity sha512-S4BtGBh22+uL5E6qLVxV0QNY6tiLVB8QL7RIkvo+KYknipZfSNwubdKy5CPkrwVXzJn4s3cx7bKx1w6BxkBIPg==
"@protobuf-ts/runtime-rpc@^2.10.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.10.0.tgz#68de8dcc369e56579569a4deafd394cf4683dc66"
integrity sha512-8CS/XPv3+pMK4v8UKhtCdvbS4h9l7aqlteKdRt0/UbIKZ8n0qHj6hX8cBhz2ngvohxCOS0N08zPr9aCLBNhW3Q==
dependencies:
"@protobuf-ts/runtime" "^2.10.0"
"@protobuf-ts/runtime@^2.10.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime/-/runtime-2.10.0.tgz#bc90f632647ff2ae72887546ddf3d193f3f43d98"
integrity sha512-ypYwGg9Pn3W/2lZ7/HW60hONGuSdzphvOY8Dq7LeNttymDe0y3LaTUUMRpuGqOT6FfrWEMnfQbyqU8AAreo8wA==
"@radix-ui/primitive@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2"
@@ -3423,6 +3459,13 @@
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
integrity sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==
"@types/ws@^8.18.1":
version "8.18.1"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9"
integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==
dependencies:
"@types/node" "*"
"@types/yauzl@^2.9.1":
version "2.10.3"
resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz"
@@ -9129,6 +9172,11 @@ typed-array-length@^1.0.7:
possible-typed-array-names "^1.0.0"
reflect.getprototypeof "^1.0.6"
typescript@^3.9:
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
typescript@^5.3.3, typescript@^5.4.3:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
@@ -9546,6 +9594,11 @@ ws@^8.18.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
ws@^8.18.1:
version "8.18.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb"
integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==
xml-name-validator@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673"