Compare commits

..

5 Commits

Author SHA1 Message Date
Jonh Alex
c63fce8edc Merge branch 'main' into error-handling-improvement 2024-10-11 01:59:38 -03:00
Jonh Alex
ec3748343d Update hydra-api.ts 2024-10-05 03:49:32 -03:00
Jonh Alex
b4526b6f67 Update register-event.ts 2024-10-05 03:48:46 -03:00
Jonh Alex
a09124e8be Refactor type definitions in hydra-api.ts to replace any with unknown
- Replaced instances of `any` with `unknown` in the `get`, `post`, `put`, `patch`, and `delete` methods for safer type handling.
- Updated parameters like `data` and `params` to use `Record<string, unknown>` for better type safety.
- Improved overall type robustness and eliminated warnings related to the use of `any`.

Contributed by: [Jonhvmp](https://github.com/Jonhvmp)
2024-10-05 03:26:36 -03:00
Jonh Alex
c7f2a861d5 Implement error handling improvements in main.py and torrent_downloader.py
- Added robust try-except blocks for all critical functions in both files.
- Improved initialization error handling for torrent session in torrent_downloader.py.
- Enhanced error logging for better debugging and clearer feedback when exceptions occur.
- Refined torrent action functions (start, pause, cancel, abort) to handle failures gracefully.
- Updated get_download_status to manage missing torrent handles and report issues accurately.

Contributed by: Jonhvmp
2024-10-05 02:54:56 -03:00
253 changed files with 8110 additions and 11030 deletions

View File

@@ -1,4 +1,5 @@
MAIN_VITE_API_URL=API_URL
MAIN_VITE_AUTH_URL=AUTH_URL
MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY
RENDERER_VITE_INTERCOM_APP_ID=YOUR_APP_ID
MAIN_VITE_SENTRY_DSN=YOUR_SENTRY_DSN
SENTRY_AUTH_TOKEN=

View File

@@ -1,5 +1,5 @@
name: Bug Report
description: Create a report to help us improve. Write in English.
description: Create a report to help us improve. Write in English, please.
title: "[BUG] Write a title for your bug"
labels: ["bug"]
body:
@@ -61,5 +61,3 @@ body:
required: true
- label: I am aware that Hydra team does not offer any support or help regarding the downloaded games.
required: true
- label: I have read the [Frequently Asked Questions (FAQ)](https://github.com/hydralauncher/hydra/wiki/FAQ).
required: true

View File

@@ -1,12 +0,0 @@
<!-- Please be sure to add one of the labels in the right hand side Labels option before creating a PR: [feature], [fix], [documentation],[translation]. This will allow Actions to automatically categorize PRs when generating Releases. -->
**When submitting this pull request, I confirm the following (please check the boxes):**
- [ ] I have read and understood the [Contributor Guidelines](https://github.com/hydralauncher/hydra?tab=readme-ov-file#ways-you-can-contribute).
- [ ] I have checked that there are no duplicate pull requests related to this request.
- [ ] I have considered, and confirm that this submission is valuable to others.
- [ ] I accept that this submission may not be used and the pull request may be closed at the discretion of the maintainers.
**Fill in the PR content:**
-

View File

@@ -17,7 +17,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.11.1
- name: Install dependencies
run: yarn
@@ -40,22 +40,20 @@ jobs:
sudo apt-get install -y libarchive-tools
yarn build:linux
env:
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_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build Windows
if: matrix.os == 'windows-latest'
run: yarn build:win
env:
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_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create artifact
@@ -63,6 +61,7 @@ jobs:
with:
name: Build-${{ matrix.os }}
path: |
dist/win-unpacked/**
dist/*-portable.exe
dist/*.zip
dist/*.dmg

View File

@@ -13,7 +13,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.11.1
- name: Install dependencies
run: yarn

View File

@@ -19,7 +19,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.11.1
- name: Install dependencies
run: yarn
@@ -44,38 +44,22 @@ jobs:
env:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build Windows
if: matrix.os == 'windows-latest'
run: yarn build:win
env:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create artifact
uses: actions/upload-artifact@v4
with:
name: Build-${{ matrix.os }}
path: |
dist/win-unpacked/**
dist/*-portable.exe
dist/*.zip
dist/*.dmg
dist/*.deb
dist/*.rpm
dist/*.tar.gz
dist/*.yml
dist/*.blockmap
dist/*.pacman
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
with:
draft: true
files: |

6
.gitignore vendored
View File

@@ -1,5 +1,5 @@
.vscode/
node_modules/
.vscode
node_modules
hydra-download-manager/
fastlist.exe
__pycache__
@@ -9,4 +9,4 @@ out
*.log*
.env
.vite
ludusavi/
sentry.properties

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Katalog](./screenshot.png)
![Hydra Katalog](./docs/screenshot.png)
</div>

View File

@@ -24,9 +24,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Katalog](./screenshot.png)
![Hydra Katalog](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Catalogue Hydra](./screenshot.png)
![Catalogue Hydra](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -13,19 +13,18 @@
[![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)
[![ee](https://img.shields.io/badge/lang-et-blue.svg)](./docs/README.et.md)
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md)
[![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)
![Hydra Catalogue](./docs/screenshot.png)

View File

@@ -24,9 +24,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>
@@ -51,9 +50,9 @@
- [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)
- [Build the bittorrent client](#-build-the-bittorrent-client)
- [Build the Electron application](#-build-the-electron-application)
- [Contributors](#-contributors)
- [Licença](#-licença)
## <a name="about"> Sobre
@@ -153,7 +152,7 @@ yarn dev
## <a name="build"></a> Build
### <a name="build-the-bittorrent-client"></a> Criar o cliente bittorrent
### <a name="build-the-bittorrent-client"></a> Build the bittorrent client
Compile o cliente BitTorrent usando este comando
@@ -161,7 +160,7 @@ Compile o cliente BitTorrent usando este comando
python torrent-client/setup.py build
```
### <a name="build-the-electron-application"></a> Criar a aplicação Electron
### <a name="build-the-electron-application"></a> Build the Electron application
Compile a aplicação Electron usando este comando:
@@ -177,7 +176,7 @@ No Linux:
yarn build:linux
```
## <a name="contributors"></a> Contribuidores
## <a name="contributors"></a> Contributors
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -25,9 +25,8 @@
[![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)
![Hydra Catalogue](./screenshot.png)
![Hydra Catalogue](./docs/screenshot.png)
</div>

View File

@@ -1,186 +0,0 @@
<div align="center">
[<img src="../resources/icon.png" width="144"/>](https://hydralauncher.site)
<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)
[![ee](https://img.shields.io/badge/lang-et-blue.svg)](./README.et.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

@@ -3,11 +3,9 @@ productName: Hydra
directories:
buildResources: build
extraResources:
- ludusavi
- hydra-download-manager
- seeds
- from: node_modules/create-desktop-shortcuts/src/windows.vbs
- from: resources/achievement.wav
files:
- "!**/.vscode/*"
- "!src/*"

View File

@@ -6,9 +6,16 @@ import {
externalizeDepsPlugin,
} from "electron-vite";
import react from "@vitejs/plugin-react";
import { sentryVitePlugin } from "@sentry/vite-plugin";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import svgr from "vite-plugin-svgr";
const sentryPlugin = sentryVitePlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "hydra-launcher",
project: "hydra-launcher",
});
export default defineConfig(({ mode }) => {
loadEnv(mode);
@@ -28,7 +35,7 @@ export default defineConfig(({ mode }) => {
"@shared": resolve("src/shared"),
},
},
plugins: [externalizeDepsPlugin(), swcPlugin()],
plugins: [externalizeDepsPlugin(), swcPlugin(), sentryPlugin],
},
preload: {
plugins: [externalizeDepsPlugin()],
@@ -44,7 +51,7 @@ export default defineConfig(({ mode }) => {
"@shared": resolve("src/shared"),
},
},
plugins: [svgr(), react(), vanillaExtractPlugin()],
plugins: [svgr(), react(), vanillaExtractPlugin(), sentryPlugin],
},
};
});

View File

@@ -1,6 +1,6 @@
{
"name": "hydralauncher",
"version": "3.0.5",
"version": "2.1.7",
"description": "Hydra",
"main": "./out/main/index.js",
"author": "Los Broxas",
@@ -23,7 +23,7 @@
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps && node ./postinstall.cjs",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
@@ -36,42 +36,42 @@
"@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.0.22",
"@hookform/resolvers": "^3.9.0",
"@intercom/messenger-js-sdk": "^0.0.14",
"@primer/octicons-react": "^19.9.0",
"@reduxjs/toolkit": "^2.2.3",
"@sentry/electron": "^5.1.0",
"@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/dynamic": "^2.1.1",
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6",
"axios": "^1.7.7",
"better-sqlite3": "^11.3.0",
"better-sqlite3": "^11.2.1",
"check-disk-space": "^3.4.0",
"classnames": "^2.5.1",
"color": "^4.2.3",
"color.js": "^1.2.0",
"create-desktop-shortcuts": "^1.11.0",
"date-fns": "^3.6.0",
"dexie": "^4.0.9",
"electron-log": "^5.2.0",
"electron-updater": "^6.3.9",
"file-type": "^19.6.0",
"dexie": "^4.0.8",
"electron-log": "^5.1.4",
"electron-updater": "^6.1.8",
"fetch-cookie": "^3.0.1",
"flexsearch": "^0.7.43",
"i18next": "^23.11.2",
"i18next-browser-languagedetector": "^7.2.1",
"icojs": "^0.19.3",
"jsdom": "^24.0.0",
"jsonwebtoken": "^9.0.2",
"knex": "^3.1.0",
"lodash-es": "^4.17.21",
"parse-torrent": "^11.0.17",
"piscina": "^4.7.0",
"lottie-react": "^2.4.0",
"parse-torrent": "^11.0.16",
"piscina": "^4.5.1",
"react-hook-form": "^7.53.0",
"react-i18next": "^14.1.0",
"react-loading-skeleton": "^3.4.0",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",
"sound-play": "^1.1.0",
"sudo-prompt": "^9.2.1",
"tar": "^7.4.3",
"typeorm": "^0.3.20",
"user-agents": "^1.1.193",
"yaml": "^2.4.1",
@@ -79,15 +79,15 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^1.0.1",
"@electron-toolkit/tsconfig": "^1.0.1",
"@sentry/vite-plugin": "^2.20.1",
"@swc/core": "^1.4.16",
"@types/auto-launch": "^5.0.5",
"@types/color": "^3.0.6",
"@types/folder-hash": "^4.0.4",
"@types/jsdom": "^21.1.6",
"@types/jsonwebtoken": "^9.0.6",
"@types/lodash-es": "^4.17.12",
@@ -95,12 +95,11 @@
"@types/parse-torrent": "^5.8.7",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/sound-play": "^1.1.3",
"@types/user-agents": "^1.0.4",
"@vanilla-extract/vite-plugin": "^4.0.7",
"@vitejs/plugin-react": "^4.2.1",
"electron": "^30.3.0",
"electron-builder": "^25.1.8",
"electron-builder": "^24.9.1",
"electron-vite": "^2.0.0",
"eslint": "^8.56.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
@@ -110,7 +109,6 @@
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass-embedded": "^1.80.6",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.0.12",

View File

@@ -1,49 +0,0 @@
const { default: axios } = require("axios");
const util = require("node:util");
const fs = require("node:fs");
const path = require("node:path");
const exec = util.promisify(require("node:child_process").exec);
const fileName = {
win32: "ludusavi-v0.25.0-win64.zip",
linux: "ludusavi-v0.25.0-linux.zip",
darwin: "ludusavi-v0.25.0-mac.zip",
};
const downloadLudusavi = async () => {
if (fs.existsSync("ludusavi")) {
console.log("Ludusavi already exists, skipping download...");
return;
}
const file = fileName[process.platform];
const downloadUrl = `https://github.com/mtkennerly/ludusavi/releases/download/v0.25.0/${file}`;
console.log(`Downloading ${file}...`);
const response = await axios.get(downloadUrl, { responseType: "stream" });
const stream = response.data.pipe(fs.createWriteStream(file));
stream.on("finish", async () => {
console.log(`Downloaded ${file}, extracting...`);
const pwd = process.cwd();
const targetPath = path.join(pwd, "ludusavi");
await exec(`npx extract-zip ${file} ${targetPath}`);
if (process.platform !== "win32") {
fs.chmodSync(path.join(targetPath, "ludusavi"), 0o755);
}
console.log("Extracted. Renaming folder...");
console.log(`Extracted ${file}, removing compressed downloaded file...`);
fs.rmSync(file);
});
};
downloadLudusavi();

View File

@@ -1,6 +1,7 @@
libtorrent
cx_Freeze == 7.2.3
cx_Freeze
cx_Logging; sys_platform == 'win32'
lief; sys_platform == 'win32'
pywin32; sys_platform == 'win32'
psutil
Pillow

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@
"language_name": "اَلْعَرَبِيَّةُ",
"home": {
"featured": "مميّز",
"trending": "شائع",
"surprise_me": "فاجئني",
"no_results": "لم يتم العثور على نتائج"
},

View File

@@ -2,6 +2,7 @@
"language_name": "беларуская мова",
"home": {
"featured": "Рэкамэндаванае",
"trending": "Актуальнае",
"surprise_me": "Здзіві мяне",
"no_results": "Няма вынікаў"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Destacats",
"trending": "Populars",
"surprise_me": "Sorprèn-me",
"no_results": "No s'ha trobat res"
},
@@ -177,6 +178,9 @@
"download_count_zero": "No hi ha baixades a la llista",
"download_count_one": "{{countFormatted}} a la llista de baixades",
"download_count_other": "{{countFormatted}} baixades a la llista",
"download_options_zero": "No hi ha cap descàrrega disponible",
"download_options_one": "{{countFormatted}} descàrrega disponible",
"download_options_other": "{{countFormatted}} baixades disponibles",
"download_source_url": "Descarrega l'URL de la font",
"add_download_source_description": "Inseriu la URL que conté el fitxer .json",
"download_source_up_to_date": "Actualitzat",

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Doporučené",
"trending": "Trendy",
"surprise_me": "Překvap mě",
"no_results": "Výsledek nenalezen"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Anbefalet",
"trending": "Trender",
"surprise_me": "Overrask mig",
"no_results": "Ingen resultater fundet",
"start_typing": "Begynd at skrive for at søge...",

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Empfohlen",
"trending": "Beliebt",
"surprise_me": "Überrasche mich",
"no_results": "Keine Ergebnisse gefunden"
},

View File

@@ -5,12 +5,12 @@
},
"home": {
"featured": "Featured",
"trending": "Trending",
"surprise_me": "Surprise me",
"no_results": "No results found",
"start_typing": "Starting typing to search...",
"hot": "Hot now",
"weekly": "📅 Top games of the week",
"achievements": "🏆 Games to beat"
"weekly": "📅 Top games of the week"
},
"sidebar": {
"catalogue": "Catalogue",
@@ -25,8 +25,7 @@
"queued": "{{title}} (Queued)",
"game_has_no_executable": "Game has no executable selected",
"sign_in": "Sign in",
"friends": "Friends",
"need_help": "Need help?"
"friends": "Friends"
},
"header": {
"search": "Search games",
@@ -41,7 +40,7 @@
"bottom_panel": {
"no_downloads_in_progress": "No downloads in progress",
"downloading_metadata": "Downloading {{title}} metadata…",
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}",
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}",
"calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…",
"checking_files": "Checking {{title}} files… ({{percentage}} complete)"
},
@@ -131,42 +130,7 @@
"download": "Download",
"executable_path_in_use": "Executable already in use by \"{{game}}\"",
"warning": "Warning:",
"hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.",
"achievements": "Achievements",
"achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Cloud save",
"cloud_save_description": "Save your progress in the cloud and continue playing on any device",
"backups": "Backups",
"install_backup": "Install",
"delete_backup": "Delete",
"create_backup": "New backup",
"last_backup_date": "Last backup on {{date}}",
"no_backup_preview": "No save games were found for this title",
"restoring_backup": "Restoring backup ({{progress}} complete)…",
"uploading_backup": "Uploading backup…",
"no_backups": "You haven't created any backups for this game yet",
"backup_uploaded": "Backup uploaded",
"backup_deleted": "Backup deleted",
"backup_restored": "Backup restored",
"see_all_achievements": "See all achievements",
"sign_in_to_see_achievements": "Sign in to see achievements",
"mapping_method_automatic": "Automatic",
"mapping_method_manual": "Manual",
"mapping_method_label": "Mapping method",
"files_automatically_mapped": "Files automatically mapped",
"no_backups_created": "No backups created for this game",
"manage_files": "Manage files",
"loading_save_preview": "Searching for save games…",
"wine_prefix": "Wine Prefix",
"wine_prefix_description": "The Wine prefix used to run this game",
"no_download_option_info": "No information available",
"backup_deletion_failed": "Failed to delete backup",
"max_number_of_artifacts_reached": "Maximum number of backups reached for this game",
"achievements_not_sync": "Your achievements are not synchronized",
"manage_files_description": "Manage which files will be backed up and restored",
"select_folder": "Select folder",
"backup_from": "Backup from {{date}}",
"custom_backup_location_set": "Custom backup location set"
"hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util its conclusion. In case Hydra closes before the conclusion, you will lose your progress."
},
"activation": {
"title": "Activate Hydra",
@@ -216,7 +180,7 @@
"language": "Language",
"real_debrid_api_token": "API Token",
"enable_real_debrid": "Enable Real-Debrid",
"real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.",
"real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to download files instantly and at the best of your Internet speed.",
"real_debrid_invalid_token": "Invalid API token",
"real_debrid_api_token_hint": "You can get your API token <0>here</0>",
"real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid",
@@ -231,7 +195,7 @@
"download_count_one": "{{countFormatted}} download option",
"download_count_other": "{{countFormatted}} download options",
"download_source_url": "Download source URL",
"add_download_source_description": "Insert the URL of the .json file",
"add_download_source_description": "Insert the URL containing the .json file",
"download_source_up_to_date": "Up-to-date",
"download_source_errored": "Errored",
"sync_download_sources": "Sync sources",
@@ -250,12 +214,10 @@
"profile_visibility": "Profile visibility",
"profile_visibility_description": "Choose who can see your profile and library",
"required_field": "This field is required",
"source_already_exists": "This source has already been added",
"source_already_exists": "This source has been already added",
"must_be_valid_url": "The source must be a valid URL",
"blocked_users": "Blocked users",
"user_unblocked": "User has been unblocked",
"enable_achievement_notifications": "When an achievement is unlocked",
"launch_minimized": "Launch Hydra minimized"
"user_unblocked": "User has been unblocked"
},
"notifications": {
"download_complete": "Download complete",
@@ -264,9 +226,7 @@
"repack_count_one": "{{count}} repack added",
"repack_count_other": "{{count}} repacks added",
"new_update_available": "Version {{version}} available",
"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"
"restart_to_install_update": "Restart Hydra to install the update"
},
"system_tray": {
"open": "Open Hydra",
@@ -290,7 +250,7 @@
"amount_hours": "{{amount}} hours",
"amount_minutes": "{{amount}} minutes",
"last_time_played": "Last played {{period}}",
"activity": "Recent Activity",
"activity": "Recent activity",
"library": "Library",
"total_play_time": "Total playtime: {{amount}}",
"no_recent_activity_title": "Hmmm… nothing here",
@@ -329,7 +289,7 @@
"user_block_modal_text": "This will block {{displayName}}",
"blocked_users": "Blocked users",
"unblock": "Unblock",
"no_friends_added": "You have no added friends",
"no_friends_added": "You still don't have added friends",
"pending": "Pending",
"no_pending_invites": "You have no pending invites",
"no_blocked_users": "You have no blocked users",
@@ -352,29 +312,6 @@
"report_reason_spam": "Spam",
"report_reason_other": "Other",
"profile_reported": "Profile reported",
"your_friend_code": "Your friend code:",
"upload_banner": "Upload banner",
"uploading_banner": "Uploading banner…",
"background_image_updated": "Background image updated"
},
"achievement": {
"achievement_unlocked": "Achievement unlocked",
"user_achievements": "{{displayName}}'s Achievements",
"your_achievements": "Your Achievements",
"unlocked_at": "Unlocked at:",
"subscription_needed": "A Hydra Cloud subscription is required to see this content",
"new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements",
"achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}"
},
"tour": {
"subscription_tour_title": "Hydra Cloud Subscription",
"subscribe_now": "Subscribe now",
"cloud_saving": "Cloud saving",
"cloud_achievements": "Save your achievements on the cloud",
"animated_profile_picture": "Animated profile pictures",
"premium_support": "Premium Support",
"show_and_compare_achievements": "Show and compare your achievements to other users",
"animated_profile_banner": "Animated profile banner"
"your_friend_code": "Your friend code:"
}
}

View File

@@ -1,16 +1,16 @@
{
"language_name": "Español",
"app": {
"successfully_signed_in": "Sesión iniciada exitosamente"
"successfully_signed_in": "Sesión iniciada correctamente"
},
"home": {
"featured": "Destacado",
"trending": "Tendencias",
"surprise_me": "¡Sorpréndeme!",
"no_results": "Sin resultados encontrados",
"start_typing": "Empieza a escribir para buscar...",
"no_results": "No se encontraron resultados",
"hot": "Popular Ahora",
"weekly": "📅 Mejores juegos de la semana",
"achievements": "🏆 Juegos para completar"
"weekly": "📅 Destacados de esta semana",
"start_typing": "Empieza a escribir para buscar..."
},
"sidebar": {
"catalogue": "Catálogo",
@@ -22,11 +22,10 @@
"downloading": "{{title}} ({{percentage}} - Descargando…)",
"filter": "Buscar en la biblioteca",
"home": "Inicio",
"queued": "{{title}} (En cola)",
"game_has_no_executable": "El juego no tiene un ejecutable seleccionado",
"queued": "{{title}} (En Cola)",
"game_has_no_executable": "El juego no tiene un ejecutable",
"sign_in": "Iniciar sesión",
"friends": "Amigos",
"need_help": "¿Necesitas ayuda?"
"friends": "Amigos"
},
"header": {
"search": "Buscar juegos",
@@ -35,8 +34,8 @@
"downloads": "Descargas",
"search_results": "Resultados de búsqueda",
"settings": "Ajustes",
"version_available_install": "Versión {{version}} disponible. Presiona acá para descargar y reinstalar.",
"version_available_download": "Versión {{version}} disponible. Presiona aquí para descargar."
"version_available_install": "Version {{version}} disponible. Haz clic aquí para reiniciar e instalar.",
"version_available_download": "Version {{version}} disponible. Haz clic aquí para descargar."
},
"bottom_panel": {
"no_downloads_in_progress": "Sin descargas en progreso",
@@ -100,7 +99,7 @@
"open_screenshot": "Abrir captura {{number}}",
"download_settings": "Ajustes de descarga",
"downloader": "Método de descarga",
"select_executable": "Seleccionar ejecutable",
"select_executable": "Seleccionar",
"no_executable_selected": "No se seleccionó un ejecutable",
"open_folder": "Abrir carpeta",
"open_download_location": "Ver archivos descargados",
@@ -120,53 +119,16 @@
"last_downloaded_option": "Última opción descargada",
"create_shortcut_success": "Atajo creado con éxito",
"create_shortcut_error": "Error al crear un atajo",
"nsfw_content_title": "Este juego contiene contenido inapropiado.",
"nsfw_content_description": "{{title}} puede ser no adecuado para todas las edades por su contenido. \n¿Deseas continuar de igual forma?",
"allow_nsfw_content": "Continuar",
"refuse_nsfw_content": "No, gracias",
"stats": "Estadísticas",
"download_count": "Downloads",
"player_count": "Jugadores activos",
"download_error": "Esta opción de descarga no está disponible.",
"download": "Descargar",
"download_count": "Descargas",
"download_error": "Esta opción de descarga no está disponible.",
"executable_path_in_use": "El ejecutable se encuentra en uso por \"{{game}}\"",
"warning": "Advertencia:",
"hydra_needs_to_remain_open": "Para esta descarga, Hydra necesita mantenerse abierta hasta que concluya. En caso de que Hydra se cierre antes de que concluya, podrías perder todo el progreso.",
"achievements": "Logros",
"achievements_count": "Logros {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Guardado en la nube",
"cloud_save_description": "Guarda tu progreso en la nube y continúa jugando en cualquier dispositivo",
"backups": "Copias de Seguridad",
"install_backup": "Instalar",
"delete_backup": "Eliminar",
"create_backup": "Nueva Copia de Seguridad",
"last_backup_date": "Última copia de seguridad el {{date}}",
"no_backup_preview": "No se encontraron datos de guardados para este juego",
"restoring_backup": "Restaurando copia de seguridad ({{progress}} completado)…",
"uploading_backup": "Subiendo copia de seguridad…",
"no_backups": "No has creado ninguna copia de seguridad para este juego aún",
"backup_uploaded": "Copia de seguridad subida",
"backup_deleted": "Copia de seguridad eliminada",
"backup_restored": "Copia de seguridad restaurada",
"see_all_achievements": "Ver todos los logros",
"sign_in_to_see_achievements": "Inicia sesión para ver los logros",
"mapping_method_automatic": "Automático",
"mapping_method_manual": "Manual",
"mapping_method_label": "Método de mapeo",
"files_automatically_mapped": "Archivos mapeados automáticamente",
"no_backups_created": "Sin copias de seguridad creadas para este juego",
"manage_files": "Gestionar archivos",
"loading_save_preview": "Buscando datos de guardados de juegos…",
"wine_prefix": "Prefijo de Wine",
"wine_prefix_description": "El prefijo de Wine usado para ejecutar este juego",
"no_download_option_info": "Sin información disponible",
"backup_deletion_failed": "La eliminación de la copia de seguridad falló",
"max_number_of_artifacts_reached": "Número máximo de copias de seguridad de este juego alcanzadas",
"achievements_not_sync": "Tus logros no están sincronizados",
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
"select_folder": "Seleccionar carpeta",
"backup_from": "Copia de seguridad de {{date}}",
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad"
"nsfw_content_description": "{{title}} puede ser no adecuado para todas las edades por su contenido. \n¿Deseas continuar de igual forma?",
"nsfw_content_title": "Este juego contiene contenido inapropiado.",
"player_count": "Jugadores activos",
"refuse_nsfw_content": "No, gracias",
"stats": "Estadísticas"
},
"activation": {
"title": "Activar Hydra",
@@ -243,18 +205,20 @@
"found_download_option_one": "Se encontró {{countFormatted}} opción de descarga",
"found_download_option_other": "Se encontraron {{countFormatted}} opciones de descarga",
"import": "Importar",
"public": "Público",
"private": "Privado",
"blocked_users": "Usuarios bloqueados",
"download_options_one": "",
"download_options_other": "",
"download_options_zero": "",
"friends_only": "Solo amigos",
"must_be_valid_url": "La fuente debe ser una URL válida.",
"privacy": "Privacidad",
"private": "Privado",
"profile_visibility": "Visibilidad del perfil",
"profile_visibility_description": "Elige quién puede ver tu perfil y biblioteca",
"public": "Público",
"required_field": "Este campo es obligatorio",
"source_already_exists": "Esta fuente ya ha sido agregada.",
"must_be_valid_url": "La fuente debe ser una URL válida.",
"blocked_users": "Usuarios bloqueados",
"user_unblocked": "El usuario ha sido desbloqueado",
"enable_achievement_notifications": "Cuando un logro se desbloquea"
"user_unblocked": "El usuario ha sido desbloqueado"
},
"notifications": {
"download_complete": "Descarga completada",
@@ -263,9 +227,7 @@
"repack_count_one": "{{count}} repack ha sido añadido",
"repack_count_other": "{{count}} repacks añadidos",
"new_update_available": "Version {{version}} disponible",
"restart_to_install_update": "Reinicia Hydra para instalar la actualización",
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados"
"restart_to_install_update": "Reinicia Hydra para instalar la actualización"
},
"system_tray": {
"open": "Abrir Hydra",
@@ -276,7 +238,7 @@
},
"binary_not_found_modal": {
"title": "Programas no instalados",
"description": "Los ejecutables de Wine o Lutris no se encontraron en tu sistema",
"description": "Los ejecutables de Wine o Lutris no se encontraron en su sistema",
"instructions": "Comprueba como instalar de forma correcta uno de los dos en tu distro de Linux para ejecutar el juego con normalidad"
},
"modal": {
@@ -334,44 +296,23 @@
"no_blocked_users": "No has bloqueado a ningún usuario",
"friend_code_copied": "Código de amigo copiado",
"undo_friendship_modal_text": "Esto deshará tu amistad con {{displayName}}",
"privacy_hint": "Para ajustar quién puede ver esto, ve a <0>Configuración</0>.",
"locked_profile": "Este perfil es privado",
"image_process_failure": "Error al procesar la imagen",
"required_field": "Este campo es obligatorio",
"displayname_min_length": "El nombre a mostrar debe tener al menos 3 caracteres",
"displayname_max_length": "El nombre a mostrar debe tener como máximo 50 caracteres",
"report_profile": "Reportar este perfil",
"report_reason": "¿Cual es el motivo del reporte?",
"displayname_min_length": "El nombre a mostrar debe tener al menos 3 caracteres",
"locked_profile": "Este perfil es privado.",
"privacy_hint": "Para ajustar quién puede ver esto, ve a <0>Configuración</0>.",
"profile_locked": "Este perfil es privado",
"profile_reported": "Perfil reportado",
"report": "Reportar",
"report_description": "Información adicional",
"report_description_placeholder": "Información adicional",
"report": "Reportar",
"report_profile": "Reportar este perfil",
"report_reason": "¿Cual es el motivo del reporte?",
"report_reason_hate": "Discursos de odio",
"report_reason_sexual_content": "Contenido sexual",
"report_reason_violence": "Violencia",
"report_reason_spam": "Spam / Contenido no deseado",
"report_reason_other": "Otro",
"profile_reported": "Perfil reportado",
"your_friend_code": "Tu código de amigo:",
"upload_banner": "Subir un banner",
"uploading_banner": "Subiendo banner…",
"background_image_updated": "Imagen de fondo actualizada"
},
"achievement": {
"achievement_unlocked": "Logro desbloqueado",
"user_achievements": "Logros de {{displayName}}",
"your_achievements": "Tus Logros",
"unlocked_at": "Desbloqueado el:",
"subscription_needed": "Se necesita una suscripción a Hydra Cloud se necesita para ver este contenido",
"new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos"
},
"tour": {
"subscription_tour_title": "Suscripción Hydra Cloud",
"subscribe_now": "Suscribirse ahora",
"cloud_saving": "Guardado en la nube",
"cloud_achievements": "Guarda tus logros en la nube",
"animated_profile_picture": "Fotos de perfil animadas",
"premium_support": "Soporte Premium",
"show_and_compare_achievements": "Muestra y compara tus logros con otros usuarios",
"animated_profile_banner": "Fondo de perfil animado"
"report_reason_sexual_content": "Contenido sexual",
"report_reason_spam": "Spam/Contenido no deseado",
"report_reason_violence": "Violencia",
"required_field": "Este campo es obligatorio",
"image_process_failure": "Error al procesar la imagen"
}
}

View File

@@ -1,376 +0,0 @@
{
"language_name": "Eesti",
"app": {
"successfully_signed_in": "Edukalt sisse logitud"
},
"home": {
"featured": "Esile toodud",
"surprise_me": "Üllata mind",
"no_results": "Tulemusi ei leitud",
"start_typing": "Alusta otsimiseks kirjutamist...",
"hot": "Praegu kuum",
"weekly": "📅 Nädala top mängud",
"achievements": "🏆 Mängud, mida läbida"
},
"sidebar": {
"catalogue": "Kataloog",
"downloads": "Allalaadimised",
"settings": "Seaded",
"my_library": "Minu kogu",
"downloading_metadata": "{{title}} (Metaandmete allalaadimine…)",
"paused": "{{title}} (Peatatud)",
"downloading": "{{title}} ({{percentage}} - Allalaadimine…)",
"filter": "Filtreeri kogu",
"home": "Avaleht",
"queued": "{{title}} (Järjekorras)",
"game_has_no_executable": "Mängul pole käivitusfaili valitud",
"sign_in": "Logi sisse",
"friends": "Sõbrad"
},
"header": {
"search": "Otsi mänge",
"home": "Avaleht",
"catalogue": "Kataloog",
"downloads": "Allalaadimised",
"search_results": "Otsingutulemused",
"settings": "Seaded",
"version_available_install": "Versioon {{version}} on saadaval. Klõpsa siia taaskäivitamiseks ja installimiseks.",
"version_available_download": "Versioon {{version}} on saadaval. Klõpsa siia allalaadimiseks."
},
"bottom_panel": {
"no_downloads_in_progress": "Allalaadimisi pole pooleli",
"downloading_metadata": "{{title}} metaandmete allalaadimine…",
"downloading": "{{title}} allalaadimine… ({{percentage}} valmis) - Lõpp {{eta}} - {{speed}}",
"calculating_eta": "{{title}} allalaadimine… ({{percentage}} valmis) - Järelejäänud aja arvutamine…",
"checking_files": "{{title}} failide kontrollimine… ({{percentage}} valmis)"
},
"catalogue": {
"next_page": "Järgmine leht",
"previous_page": "Eelmine leht"
},
"game_details": {
"open_download_options": "Ava allalaadimise valikud",
"download_options_zero": "Allalaadimise valikuid pole",
"download_options_one": "{{count}} allalaadimise valik",
"download_options_other": "{{count}} allalaadimise valikut",
"updated_at": "Uuendatud {{updated_at}}",
"install": "Installi",
"resume": "Jätka",
"pause": "Peata",
"cancel": "Tühista",
"remove": "Eemalda",
"space_left_on_disk": "{{space}} kettaruumi järel",
"eta": "Lõpp {{eta}}",
"calculating_eta": "Järelejäänud aja arvutamine…",
"downloading_metadata": "Metaandmete allalaadimine…",
"filter": "Filtreeri repacke",
"requirements": "Süsteeminõuded",
"minimum": "Miinimum",
"recommended": "Soovitatav",
"paused": "Peatatud",
"release_date": "Välja antud {{date}}",
"publisher": "Avaldaja {{publisher}}",
"hours": "tundi",
"minutes": "minutit",
"amount_hours": "{{amount}} tundi",
"amount_minutes": "{{amount}} minutit",
"accuracy": "{{accuracy}}% täpsus",
"add_to_library": "Lisa kogusse",
"remove_from_library": "Eemalda kogust",
"no_downloads": "Allalaadimisi pole saadaval",
"play_time": "Mängitud {{amount}}",
"last_time_played": "Viimati mängitud {{period}}",
"not_played_yet": "Sa pole veel {{title}} mänginud",
"next_suggestion": "Järgmine soovitus",
"play": "Mängi",
"deleting": "Installeri kustutamine…",
"close": "Sulge",
"playing_now": "Mängib praegu",
"change": "Muuda",
"repacks_modal_description": "Vali repack, mida soovid alla laadida",
"select_folder_hint": "Vaikimisi kausta muutmiseks mine <0>Seadetesse</0>",
"download_now": "Laadi alla kohe",
"no_shop_details": "Poe andmeid ei õnnestunud laadida.",
"download_options": "Allalaadimise valikud",
"download_path": "Allalaadimise tee",
"previous_screenshot": "Eelmine kuvatõmmis",
"next_screenshot": "Järgmine kuvatõmmis",
"screenshot": "Kuvatõmmis {{number}}",
"open_screenshot": "Ava kuvatõmmis {{number}}",
"download_settings": "Allalaadimise seaded",
"downloader": "Allalaadija",
"select_executable": "Vali",
"no_executable_selected": "Käivitusfaili pole valitud",
"open_folder": "Ava kaust",
"open_download_location": "Vaata allalaaditud faile",
"create_shortcut": "Loo töölaua otsetee",
"remove_files": "Eemalda failid",
"remove_from_library_title": "Oled sa kindel?",
"remove_from_library_description": "See eemaldab {{game}} sinu kogust",
"options": "Valikud",
"executable_section_title": "Käivitusfail",
"executable_section_description": "Faili tee, mida käivitatakse \"Mängi\" nupule vajutades",
"downloads_secion_title": "Allalaadimised",
"downloads_section_description": "Vaata uuendusi või selle mängu teisi versioone",
"danger_zone_section_title": "Ohutsoon",
"danger_zone_section_description": "Eemalda see mäng oma kogust või Hydra poolt allalaaditud failid",
"download_in_progress": "Allalaadimine käimas",
"download_paused": "Allalaadimine peatatud",
"last_downloaded_option": "Viimane allalaaditud variant",
"create_shortcut_success": "Otsetee edukalt loodud",
"create_shortcut_error": "Viga otsetee loomisel",
"nsfw_content_title": "See mäng sisaldab sobimatut sisu",
"nsfw_content_description": "{{title}} sisaldab sisu, mis ei pruugi sobida kõigile vanusegruppidele. Kas soovid kindlasti jätkata?",
"allow_nsfw_content": "Jätka",
"refuse_nsfw_content": "Mine tagasi",
"stats": "Statistika",
"download_count": "Allalaadimised",
"player_count": "Aktiivsed mängijad",
"download_error": "See allalaadimise valik pole saadaval",
"download": "Laadi alla",
"executable_path_in_use": "Käivitusfail on juba kasutusel mängus \"{{game}}\"",
"warning": "Hoiatus:",
"hydra_needs_to_remain_open": "selle allalaadimise jaoks peab Hydra jääma avatuks kuni lõpuni. Kui Hydra sulgub enne lõppu, kaotad oma progressi.",
"achievements": "Saavutused",
"achievements_count": "Saavutused {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Pilvesalvestus",
"cloud_save_description": "Salvesta oma progress pilve ja jätka mängimist mistahes seadmes",
"backups": "Varundused",
"install_backup": "Installi",
"delete_backup": "Kustuta",
"create_backup": "Uus varundus",
"last_backup_date": "Viimane varundus {{date}}",
"no_backup_preview": "Selle mängu jaoks ei leitud salvestusi",
"restoring_backup": "Varunduse taastamine ({{progress}} valmis)…",
"uploading_backup": "Varunduse üleslaadimine…",
"no_backups": "Sa pole veel selle mängu jaoks varundusi loonud",
"backup_uploaded": "Varundus üles laaditud",
"backup_deleted": "Varundus kustutatud",
"backup_restored": "Varundus taastatud",
"see_all_achievements": "Vaata kõiki saavutusi",
"sign_in_to_see_achievements": "Logi sisse, et näha saavutusi",
"mapping_method_automatic": "Automaatne",
"mapping_method_manual": "Käsitsi",
"mapping_method_label": "Kaardistamise meetod",
"files_automatically_mapped": "Failid automaatselt kaardistatud",
"no_backups_created": "Selle mängu jaoks pole varundusi loodud",
"manage_files": "Halda faile",
"loading_save_preview": "Salvestuste otsimine…",
"wine_prefix": "Wine Prefix",
"wine_prefix_description": "Wine prefix, mida kasutatakse selle mängu käivitamiseks",
"no_download_option_info": "Info pole saadaval",
"backup_deletion_failed": "Varunduse kustutamine ebaõnnestus",
"max_number_of_artifacts_reached": "Selle mängu varunduste maksimaalne arv on saavutatud",
"achievements_not_sync": "Sinu saavutused pole sünkroniseeritud",
"manage_files_description": "Hallake, millised failid varundatakse ja taastatakse",
"select_folder": "Vali kaust",
"backup_from": "Varundamine kuupäevast {{date}}",
"custom_backup_location_set": "Kohandatud varundamise asukoht määratud"
},
"activation": {
"title": "Aktiveeri Hydra",
"installation_id": "Installatsiooni ID:",
"enter_activation_code": "Sisesta oma aktiveerimiskood",
"message": "Kui sa ei tea, kust seda küsida, siis sa ei peaks seda omama.",
"activate": "Aktiveeri",
"loading": "Laadimine…"
},
"downloads": {
"resume": "Jätka",
"pause": "Peata",
"eta": "Lõpp {{eta}}",
"paused": "Peatatud",
"verifying": "Kontrollimine…",
"completed": "Lõpetatud",
"removed": "Pole alla laaditud",
"cancel": "Tühista",
"filter": "Filtreeri allalaaditud mänge",
"remove": "Eemalda",
"downloading_metadata": "Metaandmete allalaadimine…",
"deleting": "Installeri kustutamine…",
"delete": "Eemalda installer",
"delete_modal_title": "Oled sa kindel?",
"delete_modal_description": "See eemaldab kõik installifailid sinu arvutist",
"install": "Installi",
"download_in_progress": "Töös",
"queued_downloads": "Järjekorras allalaadimised",
"downloads_completed": "Lõpetatud",
"queued": "Järjekorras",
"no_downloads_title": "Nii tühi",
"no_downloads_description": "Sa pole veel Hydraga midagi alla laadinud, aga pole kunagi hilja alustada.",
"checking_files": "Failide kontrollimine…"
},
"settings": {
"downloads_path": "Allalaadimiste tee",
"change": "Uuenda",
"notifications": "Teavitused",
"enable_download_notifications": "Kui allalaadimine on lõpetatud",
"enable_repack_list_notifications": "Kui uus repack on lisatud",
"real_debrid_api_token_label": "Real-Debrid API võti",
"quit_app_instead_hiding": "Ära peida Hydrat sulgemisel",
"launch_with_system": "Käivita Hydra süsteemi käivitamisel",
"general": "Üldine",
"behavior": "Käitumine",
"download_sources": "Allalaadimise allikad",
"language": "Keel",
"real_debrid_api_token": "API Võti",
"enable_real_debrid": "Luba Real-Debrid",
"real_debrid_description": "Real-Debrid on piiranguteta allalaadija, mis võimaldab sul faile alla laadida koheselt ja sinu internetiühenduse parima kiirusega.",
"real_debrid_invalid_token": "Vigane API võti",
"real_debrid_api_token_hint": "Sa saad oma API võtme <0>siit</0>",
"real_debrid_free_account_error": "Konto \"{{username}}\" on tasuta konto. Palun telli Real-Debrid",
"real_debrid_linked_message": "Konto \"{{username}}\" ühendatud",
"save_changes": "Salvesta muudatused",
"changes_saved": "Muudatused edukalt salvestatud",
"download_sources_description": "Hydra laeb allalaadimise lingid nendest allikatest. Allika URL peab olema otsene link .json failile, mis sisaldab allalaadimise linke.",
"validate_download_source": "Valideeri",
"remove_download_source": "Eemalda",
"add_download_source": "Lisa allikas",
"download_count_zero": "Allalaadimise valikuid pole",
"download_count_one": "{{countFormatted}} allalaadimise valik",
"download_count_other": "{{countFormatted}} allalaadimise valikut",
"download_source_url": "Allalaadimise allika URL",
"add_download_source_description": "Sisesta URL, mis sisaldab .json faili",
"download_source_up_to_date": "Ajakohane",
"download_source_errored": "Vigane",
"sync_download_sources": "Sünkroniseeri allikad",
"removed_download_source": "Allalaadimise allikas eemaldatud",
"added_download_source": "Allalaadimise allikas lisatud",
"download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud",
"insert_valid_json_url": "Sisesta kehtiv JSON url",
"found_download_option_zero": "Allalaadimise valikuid ei leitud",
"found_download_option_one": "Leitud {{countFormatted}} allalaadimise valik",
"found_download_option_other": "Leitud {{countFormatted}} allalaadimise valikut",
"import": "Impordi",
"public": "Avalik",
"private": "Privaatne",
"friends_only": "Ainult sõpradele",
"privacy": "Privaatsus",
"profile_visibility": "Profiili nähtavus",
"profile_visibility_description": "Vali, kes saavad näha sinu profiili ja kogu",
"required_field": "See väli on kohustuslik",
"source_already_exists": "See allikas on juba lisatud",
"must_be_valid_url": "Allikas peab olema kehtiv URL",
"blocked_users": "Blokeeritud kasutajad",
"user_unblocked": "Kasutaja blokeering on eemaldatud",
"enable_achievement_notifications": "Kui saavutus avatakse"
},
"notifications": {
"download_complete": "Allalaadimine lõpetatud",
"game_ready_to_install": "{{title}} on valmis installimiseks",
"repack_list_updated": "Repackide nimekiri uuendatud",
"repack_count_one": "{{count}} repack lisatud",
"repack_count_other": "{{count}} repacki lisatud",
"new_update_available": "Versioon {{version}} saadaval",
"restart_to_install_update": "Taaskäivita Hydra uuenduse installimiseks",
"notification_achievement_unlocked_title": "Saavutus avatud mängus {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} ja veel {{count}} avati"
},
"system_tray": {
"open": "Ava Hydra",
"quit": "Välju"
},
"game_card": {
"no_downloads": "Allalaadimisi pole saadaval"
},
"binary_not_found_modal": {
"title": "Programmid pole installitud",
"description": "Wine või Lutrise käivitusfaile ei leitud sinu süsteemist",
"instructions": "Kontrolli õiget viisi nende installimiseks oma Linuxi distrol, et mäng saaks normaalselt töötada"
},
"modal": {
"close": "Sulgemise nupp"
},
"forms": {
"toggle_password_visibility": "Lülita parooli nähtavust"
},
"user_profile": {
"amount_hours": "{{amount}} tundi",
"amount_minutes": "{{amount}} minutit",
"last_time_played": "Viimati mängitud {{period}}",
"activity": "Hiljutine aktiivsus",
"library": "Kogu",
"total_play_time": "Kogu mängitud aeg: {{amount}}",
"no_recent_activity_title": "Hmmm… siin pole midagi",
"no_recent_activity_description": "Sa pole hiljuti ühtegi mängu mänginud. On aeg seda muuta!",
"display_name": "Kuvatav nimi",
"saving": "Salvestamine",
"save": "Salvesta",
"edit_profile": "Muuda profiili",
"saved_successfully": "Edukalt salvestatud",
"try_again": "Palun proovi uuesti",
"sign_out_modal_title": "Oled sa kindel?",
"cancel": "Tühista",
"successfully_signed_out": "Edukalt välja logitud",
"sign_out": "Logi välja",
"playing_for": "Mängib {{amount}}",
"sign_out_modal_text": "Sinu kogu on seotud sinu praeguse kontoga. Välja logides pole sinu kogu enam nähtav ja edasist progressi ei salvestata. Jätkata väljalogimisega?",
"add_friends": "Lisa sõpru",
"add": "Lisa",
"friend_code": "Sõbrakood",
"see_profile": "Vaata profiili",
"sending": "Saatmine",
"friend_request_sent": "Sõbrakutse saadetud",
"friends": "Sõbrad",
"friends_list": "Sõprade nimekiri",
"user_not_found": "Kasutajat ei leitud",
"block_user": "Blokeeri kasutaja",
"add_friend": "Lisa sõbraks",
"request_sent": "Kutse saadetud",
"request_received": "Kutse saadud",
"accept_request": "Võta kutse vastu",
"ignore_request": "Ignoreeri kutset",
"cancel_request": "Tühista kutse",
"undo_friendship": "Tühista sõprus",
"request_accepted": "Kutse vastu võetud",
"user_blocked_successfully": "Kasutaja edukalt blokeeritud",
"user_block_modal_text": "See blokeerib kasutaja {{displayName}}",
"blocked_users": "Blokeeritud kasutajad",
"unblock": "Eemalda blokeering",
"no_friends_added": "Sul pole veel lisatud sõpru",
"pending": "Ootel",
"no_pending_invites": "Sul pole ootel kutseid",
"no_blocked_users": "Sul pole blokeeritud kasutajaid",
"friend_code_copied": "Sõbrakood kopeeritud",
"undo_friendship_modal_text": "See tühistab sinu sõpruse kasutajaga {{displayName}}",
"privacy_hint": "Et muuta, kes seda näevad, mine <0>Seadetesse</0>",
"locked_profile": "See profiil on privaatne",
"image_process_failure": "Viga pildi töötlemisel",
"required_field": "See väli on kohustuslik",
"displayname_min_length": "Kuvatav nimi peab olema vähemalt 3 tähemärki pikk",
"displayname_max_length": "Kuvatav nimi võib olla maksimaalselt 50 tähemärki pikk",
"report_profile": "Teata sellest profiilist",
"report_reason": "Miks sa sellest profiilist teatad?",
"report_description": "Lisainfo",
"report_description_placeholder": "Lisainfo",
"report": "Teata",
"report_reason_hate": "Vaenukõne",
"report_reason_sexual_content": "Seksuaalne sisu",
"report_reason_violence": "Vägivald",
"report_reason_spam": "Rämpspost",
"report_reason_other": "Muu",
"profile_reported": "Profiilist teatatud",
"your_friend_code": "Sinu sõbrakood:",
"upload_banner": "Lae üles bänner",
"uploading_banner": "Bänneri üleslaadimine…",
"background_image_updated": "Bänner uuendatud"
},
"achievement": {
"achievement_unlocked": "Saavutus avatud",
"user_achievements": "{{displayName}} saavutused",
"your_achievements": "Sinu saavutused",
"unlocked_at": "Avatud:",
"subscription_needed": "Selle sisu nägemiseks on vaja Hydra Cloud tellimust",
"new_achievements_unlocked": "Avatud {{achievementCount}} uut saavutust {{gameCount}} mängust"
},
"tour": {
"subscription_tour_title": "Hydra Cloud Tellimus",
"subscribe_now": "Telli kohe",
"cloud_saving": "Pilvesalvestus",
"cloud_achievements": "Salvesta oma saavutused pilve",
"animated_profile_picture": "Animeeritud profiilipildid",
"premium_support": "Premium tugi",
"show_and_compare_achievements": "Näita ja võrdle oma saavutusi teiste kasutajatega",
"animated_profile_banner": "Animeeritud profiilibänner"
}
}

View File

@@ -2,6 +2,7 @@
"language_name": "فارسی",
"home": {
"featured": "پیشنهادی",
"trending": "پرطرفدار",
"surprise_me": "سوپرایزم کن",
"no_results": "اتمام‌ای پیدا نشد"
},

View File

@@ -2,6 +2,7 @@
"language_name": "Français",
"home": {
"featured": "En vedette",
"trending": "Tendance",
"surprise_me": "Surprenez-moi",
"no_results": "Aucun résultat trouvé"
},
@@ -57,14 +58,14 @@
"remove_from_library": "Supprimer de la bibliothèque",
"no_downloads": "Aucun téléchargement disponible",
"next_suggestion": "Suggestion suivante",
"play_time": "Joué pour {{amount}}",
"play_time": "Joué pour {{montant}}",
"install": "Installer",
"play": "Jouer",
"not_played_yet": "Vous n'avez pas encore joué à {{title}}",
"close": "Fermer",
"deleting": "Suppression du programme d'installation…",
"playing_now": "Jeu en cours",
"last_time_played": "Dernièrement joué {{period}}"
"last_time_played": "Dernièrement joué {{période}}"
},
"activation": {
"title": "Activer Hydra",

View File

@@ -2,6 +2,7 @@
"language_name": "Magyar",
"home": {
"featured": "Featured",
"trending": "Népszerű",
"surprise_me": "Lepj meg",
"no_results": "Nem található"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Unggulan",
"trending": "Sedang Tren",
"surprise_me": "Kejutkan saya",
"no_results": "Tidak ada hasil ditemukan"
},
@@ -177,6 +178,9 @@
"download_count_zero": "Tidak ada unduhan dalam daftar",
"download_count_one": "{{countFormatted}} unduhan dalam daftar",
"download_count_other": "{{countFormatted}} unduhan dalam daftar",
"download_options_zero": "Tidak ada unduhan tersedia",
"download_options_one": "{{countFormatted}} unduhan tersedia",
"download_options_other": "{{countFormatted}} unduhan tersedia",
"download_source_url": "URL sumber unduhan",
"add_download_source_description": "Masukkan URL yang berisi file .json",
"download_source_up_to_date": "Terkini",

View File

@@ -23,7 +23,6 @@ import ca from "./ca/translation.json";
import kk from "./kk/translation.json";
import cs from "./cs/translation.json";
import nb from "./nb/translation.json";
import et from "./et/translation.json";
export default {
"pt-BR": ptBR,
@@ -51,5 +50,4 @@ export default {
kk,
cs,
nb,
et,
};

View File

@@ -2,6 +2,7 @@
"language_name": "Italiano",
"home": {
"featured": "In primo piano",
"trending": "Di tendenza",
"surprise_me": "Sorprendimi",
"no_results": "Nessun risultato trovato"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Ұсынылған",
"trending": "Трендте",
"surprise_me": "Таңқалдыр",
"no_results": "Ештеңе табылмады"
},
@@ -175,6 +176,9 @@
"download_count_zero": "Жүктеулер тізімінде жоқ",
"download_count_one": "{{countFormatted}} жүктеу тізімде",
"download_count_other": "{{countFormatted}} жүктеу тізімде",
"download_options_zero": "Қолжетімді жүктеулер жоқ",
"download_options_one": "{{countFormatted}} жүктеу нұсқасы қол жетімді",
"download_options_other": "{{countFormatted}} жүктеу нұсқалары қол жетімді",
"download_source_url": "Көздің сілтемесі",
"add_download_source_description": ".json файлға сілтемені қойыңыз",
"download_source_up_to_date": "Жаңартылған",

View File

@@ -2,6 +2,7 @@
"language_name": "한국어",
"home": {
"featured": "추천",
"trending": "인기",
"surprise_me": "무작위 추천",
"no_results": "결과 없음"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Anbefalinger",
"trending": "Trender",
"surprise_me": "Overrask meg",
"no_results": "Ingen resultater fundet",
"start_typing": "Begynn å skrive for å søke...",

View File

@@ -2,6 +2,7 @@
"language_name": "Nederlands",
"home": {
"featured": "Uitgelicht",
"trending": "Trending",
"surprise_me": "Verrasing",
"no_results": "Geen resultaten gevonden"
},

View File

@@ -2,6 +2,7 @@
"language_name": "Polski",
"home": {
"featured": "Wyróżnione",
"trending": "Trendujące",
"surprise_me": "Zaskocz mnie",
"no_results": "Nie znaleziono wyników"
},

View File

@@ -5,9 +5,9 @@
},
"home": {
"featured": "Destaques",
"hot": "Populares",
"trending": "Populares",
"hot": "Populares agora",
"weekly": "📅 Mais baixados da semana",
"achievements": "🏆 Pra platinar",
"surprise_me": "Surpreenda-me",
"no_results": "Nenhum resultado encontrado",
"start_typing": "Comece a digitar para pesquisar…"
@@ -25,8 +25,7 @@
"queued": "{{title}} (Na fila)",
"game_has_no_executable": "Jogo não possui executável selecionado",
"sign_in": "Login",
"friends": "Amigos",
"need_help": "Precisa de ajuda?"
"friends": "Amigos"
},
"header": {
"search": "Buscar jogos",
@@ -127,42 +126,7 @@
"download": "Baixar",
"executable_path_in_use": "Executável em uso por \"{{game}}\"",
"warning": "Aviso:",
"hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.",
"achievements": "Conquistas",
"achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})",
"cloud_save": "Salvamento em nuvem",
"cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo",
"backups": "Backups",
"install_backup": "Restaurar",
"delete_backup": "Apagar",
"create_backup": "Novo backup",
"last_backup_date": "Último backup em {{date}}",
"no_backup_preview": "Não foi possível encontrar nenhum salvamento para este jogo",
"restoring_backup": "Restaurando backup ({{progress}} concluído)…",
"uploading_backup": "Criando backup…",
"no_backups": "Você ainda não fez nenhum backup deste jogo",
"backup_uploaded": "Backup criado",
"backup_deleted": "Backup apagado",
"backup_restored": "Backup restaurado",
"see_all_achievements": "Ver todas as conquistas",
"sign_in_to_see_achievements": "Faça login para ver as conquistas",
"mapping_method_automatic": "Automático",
"mapping_method_manual": "Manual",
"mapping_method_label": "Método de mapeamento",
"files_automatically_mapped": "Arquivos automaticamente mapeados",
"no_backups_created": "Nenhum backup criado para este jogo",
"manage_files": "Gerenciar arquivos",
"loading_save_preview": "Buscando por arquivos de salvamento…",
"wine_prefix": "Prefixo Wine",
"wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo",
"no_download_option_info": "Sem informações disponíveis",
"backup_deletion_failed": "Falha ao apagar backup",
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
"achievements_not_sync": "Suas conquistas não estão sincronizadas",
"backup_from": "Backup de {{date}}",
"custom_backup_location_set": "Localização customizada selecionada",
"select_folder": "Selecione a pasta",
"manage_files_description": "Gerencie quais arquivos serão feitos backup"
"hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso."
},
"activation": {
"title": "Ativação",
@@ -191,7 +155,7 @@
"install": "Instalar",
"download_in_progress": "Baixando agora",
"queued_downloads": "Na fila",
"downloads_completed": "Concluído",
"downloads_completed": "Completo",
"queued": "Na fila",
"no_downloads_title": "Nada por aqui…",
"no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
@@ -204,7 +168,7 @@
"enable_download_notifications": "Quando um download for concluído",
"enable_repack_list_notifications": "Quando a lista de repacks for atualizada",
"real_debrid_api_token_label": "Token de API do Real-Debrid",
"quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar",
"quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar.",
"launch_with_system": "Iniciar o Hydra junto com o sistema",
"general": "Geral",
"behavior": "Comportamento",
@@ -226,6 +190,9 @@
"download_count_zero": "Sem downloads na lista",
"download_count_one": "{{countFormatted}} download na lista",
"download_count_other": "{{countFormatted}} downloads na lista",
"download_options_zero": "Sem downloads disponíveis",
"download_options_one": "{{countFormatted}} download disponível",
"download_options_other": "{{countFormatted}} downloads disponíveis",
"download_source_url": "URL da fonte",
"add_download_source_description": "Insira a URL contendo o arquivo .json",
"download_source_up_to_date": "Sincronizada",
@@ -249,9 +216,7 @@
"source_already_exists": "Essa fonte já foi adicionada",
"must_be_valid_url": "A fonte deve ser uma URL válida",
"blocked_users": "Usuários bloqueados",
"user_unblocked": "Usuário desbloqueado",
"enable_achievement_notifications": "Quando uma conquista é desbloqueada",
"launch_minimized": "Iniciar o Hydra minimizado"
"user_unblocked": "Usuário desbloqueado"
},
"notifications": {
"download_complete": "Download concluído",
@@ -334,6 +299,7 @@
"friend_code_copied": "Código de amigo copiado",
"undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}",
"privacy_hint": "Pra controlar quem pode ver seu perfil, acesse a <0>Tela de Configurações</0>",
"profile_locked": "Este perfil é privado",
"image_process_failure": "Falha ao processar a imagem",
"required_field": "Este campo é obrigatório",
"displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres",
@@ -350,29 +316,6 @@
"report_reason_spam": "Spam",
"report_reason_other": "Outro",
"profile_reported": "Perfil reportado",
"your_friend_code": "Seu código de amigo:",
"upload_banner": "Carregar banner",
"uploading_banner": "Carregando banner…",
"background_image_updated": "Imagem de fundo salva"
},
"achievement": {
"achievement_unlocked": "Conquista desbloqueada",
"your_achievements": "Suas Conquistas",
"user_achievements": "Conquistas de {{displayName}}",
"unlocked_at": "Desbloqueado em:",
"subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo",
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}"
},
"tour": {
"subscription_tour_title": "Assinatura Hydra Cloud",
"subscribe_now": "Inscreva-se agora",
"cloud_achievements": "Salvamento de conquistas em nuvem",
"animated_profile_picture": "Fotos de perfil animadas",
"premium_support": "Suporte Premium",
"show_and_compare_achievements": "Exiba e compare suas conquistas com outros usuários",
"animated_profile_banner": "Banner animado no perfil",
"cloud_saving": "Saves de jogos em nuvem"
"your_friend_code": "Seu código de amigo:"
}
}

View File

@@ -5,12 +5,10 @@
},
"home": {
"featured": "Destaques",
"hot": "Populares",
"weekly": "📅 Mais descarregados esta semana",
"achievements": "🏆 Para completar",
"trending": "Populares",
"surprise_me": "Surpreende-me",
"no_results": "Nenhum resultado encontrado",
"start_typing": "Começa a escrever para pesquisar…"
"start_typing": "Comece a digitar para pesquisar…"
},
"sidebar": {
"catalogue": "Catálogo",
@@ -18,14 +16,13 @@
"settings": "Definições",
"my_library": "Biblioteca",
"downloading_metadata": "{{title}} (A transferir metadados…)",
"paused": "{{title}} (Em pausa)",
"paused": "{{title}} (Pausado)",
"downloading": "{{title}} ({{percentage}} - A transferir…)",
"filter": "Procurar",
"home": "Início",
"queued": "{{title}} (Na fila)",
"game_has_no_executable": "O jogo não tem um executável selecionado",
"sign_in": "Iniciar sessão",
"friends": "Amigos"
"game_has_no_executable": "Jogo não tem executável selecionado",
"sign_in": "Iniciar sessão"
},
"header": {
"search": "Procurar jogos",
@@ -34,8 +31,8 @@
"search_results": "Resultados da pesquisa",
"settings": "Definições",
"home": "Início",
"version_available_install": "Versão {{version}} disponível. Clica aqui para reiniciar e instalar.",
"version_available_download": "Versão {{version}} disponível. Clica aqui para fazer o download."
"version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.",
"version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download."
},
"bottom_panel": {
"no_downloads_in_progress": "Sem transferências em andamento",
@@ -50,19 +47,19 @@
"download_options_one": "{{count}} opção de transferência",
"download_options_other": "{{count}} opções de transferência",
"updated_at": "Atualizado a {{updated_at}}",
"resume": "Continuar",
"pause": "Colocar em pausa",
"resume": "Retomar",
"pause": "Pausar",
"cancel": "Cancelar",
"remove": "Remover",
"space_left_on_disk": "{{space}} livres no disco",
"eta": "Conclusão {{eta}}",
"calculating_eta": "A calcular tempo restante…",
"downloading_metadata": "A transferir metadados…",
"filter": "Filtrar opções de transferência",
"filter": "Filtrar repacks",
"requirements": "Requisitos do sistema",
"minimum": "Mínimos",
"recommended": "Recomendados",
"paused": "Em pausa",
"paused": "Pausado",
"release_date": "Lançado em {{date}}",
"publisher": "Publicado por {{publisher}}",
"hours": "horas",
@@ -73,24 +70,24 @@
"add_to_library": "Adicionar à biblioteca",
"remove_from_library": "Remover da biblioteca",
"no_downloads": "Nenhuma transferência disponível",
"play_time": "Jogaste por {{amount}}",
"play_time": "Jogou por {{amount}}",
"next_suggestion": "Próxima sugestão",
"install": "Instalar",
"last_time_played": "Última sessão {{period}}",
"play": "Jogar",
"not_played_yet": "Ainda não jogaste {{title}}",
"not_played_yet": "Ainda não jogou {{title}}",
"close": "Fechar",
"deleting": "A eliminar instalador…",
"playing_now": "A jogar agora",
"change": "Explorar",
"repacks_modal_description": "Escolhe a versão do jogo que desejas transferir",
"select_folder_hint": "Para alterar o local predefinido, acede às <0>Definições</0>",
"repacks_modal_description": "Escolha o repack do jogo que deseja transferir",
"select_folder_hint": "Para trocar o diretório padrão, aceda à <0>Tela de Definições</0>",
"download_now": "Iniciar transferência",
"no_shop_details": "Não foi possível obter os detalhes da loja.",
"download_options": "Opções de transferência",
"download_path": "Local de transferência",
"download_path": "Diretório de transferência",
"previous_screenshot": "Captura de ecrã anterior",
"next_screenshot": "Captura de ecrã seguinte",
"next_screenshot": "Próxima captura de ecrã",
"screenshot": "Captura de ecrã {{number}}",
"open_screenshot": "Ver captura de ecrã {{number}}",
"download_settings": "Definições de transferência",
@@ -102,104 +99,65 @@
"create_shortcut": "Criar atalho no ambiente de trabalho",
"remove_files": "Remover ficheiros",
"options": "Gerir",
"remove_from_library_description": "Isto vai remover {{game}} da tua biblioteca",
"remove_from_library_title": "Tens a certeza?",
"remove_from_library_description": "Isto irá remover {{game}} da sua biblioteca",
"remove_from_library_title": "Tem a certeza?",
"executable_section_title": "Executável",
"executable_section_description": "O caminho do ficheiro que vai ser executado ao clicar em \"Jogar\"",
"executable_section_description": "O caminho do ficheiro que será executado ao clicar em \"Jogar\"",
"downloads_secion_title": "Transferências",
"downloads_section_description": "Encontra atualizações ou versões diferentes para este mesmo título",
"downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título",
"danger_zone_section_title": "Zona de perigo",
"danger_zone_section_description": "Remove o jogo da tua biblioteca ou os ficheiros que foram transferidos pelo Hydra",
"danger_zone_section_description": "Remova o jogo da sua biblioteca ou os ficheiros que foram transferidos pelo Hydra",
"download_in_progress": "Transferência em andamento",
"download_paused": "Transferência em pausa",
"download_paused": "Transferência pausada",
"last_downloaded_option": "Última opção transferida",
"create_shortcut_success": "Atalho criado com sucesso",
"create_shortcut_error": "Erro ao criar atalho",
"nsfw_content_title": "Este jogo contém conteúdo inapropriado",
"nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Desejas continuar?",
"allow_nsfw_content": "Continuar",
"refuse_nsfw_content": "Voltar",
"stats": "Estatísticas",
"download_count": "Transferências",
"player_count": "Jogadores ativos",
"download_error": "Esta opção de transferência falhou",
"download": "Transferir",
"executable_path_in_use": "Executável em uso por \"{{game}}\"",
"warning": "Aviso:",
"hydra_needs_to_remain_open": "para esta transferência, o Hydra precisa de ficar aberto até a conclusão. Caso o Hydra encerre antes da transferência terminar, vais perder o teu progresso.",
"achievements": "Conquistas",
"achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})",
"cloud_save": "Gravação na nuvem",
"cloud_save_description": "Mantém o teu progresso guardado na nuvem e continua de onde paraste em qualquer dispositivo",
"backups": "Backups",
"install_backup": "Restaurar",
"delete_backup": "Apagar",
"create_backup": "Novo backup",
"last_backup_date": "Último backup realizado em {{date}}",
"no_backup_preview": "Não foi possível encontrar nenhum ficheiro de gravação para este jogo",
"restoring_backup": "A restaurar backup ({{progress}} concluído)…",
"uploading_backup": "A criar backup…",
"no_backups": "Ainda não fizeste nenhum backup deste jogo",
"backup_uploaded": "Backup criado",
"backup_deleted": "Backup apagado",
"backup_restored": "Backup restaurado",
"see_all_achievements": "Ver todas as conquistas",
"sign_in_to_see_achievements": "Faz login para vez as conquistas",
"mapping_method_automatic": "Automático",
"mapping_method_manual": "Manual",
"mapping_method_label": "Modo de mapeamento",
"files_automatically_mapped": "Ficheiros automaticamente mapeados",
"no_backups_created": "Nenhum backup criado para este jogo",
"manage_files": "Gerir ficheiros",
"loading_save_preview": "A procurar ficheiros de gravação…",
"wine_prefix": "Prefixo Wine",
"wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo",
"no_download_option_info": "Sem informações disponíveis",
"backup_deletion_failed": "Falha ao apagar o backup",
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
"achievements_not_sync": "As tuas conquistas não estão sincronizadas"
"hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso."
},
"activation": {
"title": "Ativação",
"installation_id": "ID da instalação:",
"enter_activation_code": "Insere o teu código de ativação",
"message": "Se não souberes onde conseguir o código, talvez não devias estar aqui.",
"enter_activation_code": "Insira o seu código de ativação",
"message": "Se não sabe onde conseguir o código, talvez não devesse estar aqui.",
"activate": "Ativar",
"loading": "A carregar…"
},
"downloads": {
"resume": "Continuar",
"pause": "Colocar em pausa",
"resume": "Retomar",
"pause": "Pausar",
"eta": "Conclusão {{eta}}",
"paused": "Em pausa",
"paused": "Pausado",
"verifying": "A verificar…",
"completed": "Concluído",
"removed": "Cancelado",
"cancel": "Cancelar",
"filter": "Filtrar jogos descarregados",
"filter": "Filtrar jogos transferidos",
"remove": "Remover",
"downloading_metadata": "A transferir metadados…",
"delete": "Remover instalador",
"delete_modal_description": "Isto vai remover todos os ficheiros de instalação do teu computador",
"delete_modal_title": "Tens a certeza?",
"deleting": "A remover o instalador…",
"delete_modal_description": "Isto removerá todos os ficheiros de instalação do seu computador",
"delete_modal_title": "Tem a certeza?",
"deleting": "A eliminar instalador…",
"install": "Instalar",
"download_in_progress": "A descarregar agora",
"download_in_progress": "A transferir agora",
"queued_downloads": "Na fila",
"downloads_completed": "Concluído",
"queued": "Na fila",
"no_downloads_title": "Nada por aqui…",
"no_downloads_description": "Ainda não descarregaste nada pelo Hydra, mas nunca é tarde para começar.",
"no_downloads_description": "Ainda não transferiu nada pelo Hydra, mas nunca é tarde para começar.",
"checking_files": "A verificar ficheiros…"
},
"settings": {
"downloads_path": "Local das transferências",
"change": "Procurar...",
"downloads_path": "Diretório das transferências",
"change": "Explorar...",
"notifications": "Notificações",
"enable_download_notifications": "Quando uma transferência for concluída",
"enable_repack_list_notifications": "Quando a lista de repacks for atualizada",
"real_debrid_api_token_label": "Token de API do Real-Debrid",
"quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar",
"quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar.",
"launch_with_system": "Iniciar o Hydra com o sistema",
"general": "Geral",
"behavior": "Comportamento",
@@ -207,72 +165,60 @@
"language": "Idioma",
"real_debrid_api_token": "Token de API",
"enable_real_debrid": "Ativar Real-Debrid",
"real_debrid_api_token_hint": "Podes obter o teu token de API <0>aqui</0>",
"real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite descarregar ficheiros instantaneamente e com a melhor velocidade da tua Internet.",
"real_debrid_api_token_hint": "Pode obter o seu token de API <0>aqui</0>",
"real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite transferir ficheiros instantaneamente e com a melhor velocidade da sua Internet.",
"real_debrid_invalid_token": "Token de API inválido",
"real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, subscreve o Real-Debrid",
"real_debrid_linked_message": "Conta \"{{username}}\" associada",
"real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, subscreva o Real-Debrid",
"real_debrid_linked_message": "Conta \"{{username}}\" vinculada",
"save_changes": "Guardar alterações",
"changes_saved": "Alterações guardadas com sucesso",
"download_sources_description": "O Hydra vai procurar links de download em todas as fontes ativadas. O URL da fonte deve ser um link direto para um ficheiro .json que contenha uma lista de links.",
"changes_saved": "Definições guardadas com sucesso",
"download_sources_description": "O Hydra vai procurar links de transferência em todas as fontes ativadas. A URL da página de detalhes da loja não é guardada no seu dispositivo. Utilizamos um sistema de metadados criado pela comunidade para fornecer suporte a mais fontes de transferência de jogos.",
"validate_download_source": "Validar",
"remove_download_source": "Remover",
"add_download_source": "Adicionar fonte",
"download_count_zero": "Sem downloads na lista",
"download_count_one": "{{countFormatted}} download na lista",
"download_count_other": "{{countFormatted}} downloads na lista",
"download_options_zero": "Sem downloads disponíveis",
"download_options_one": "{{countFormatted}} download disponível",
"download_options_other": "{{countFormatted}} downloads disponíveis",
"download_count_zero": "Sem transferências na lista",
"download_count_one": "{{countFormatted}} transferência na lista",
"download_count_other": "{{countFormatted}} transferências na lista",
"download_options_zero": "Sem transferências disponíveis",
"download_options_one": "{{countFormatted}} transferência disponível",
"download_options_other": "{{countFormatted}} transferências disponíveis",
"download_source_url": "URL da fonte",
"add_download_source_description": "Insere o URL que contém o ficheiro .json",
"add_download_source_description": "Insira o URL contendo o arquivo .json",
"download_source_up_to_date": "Sincronizada",
"download_source_errored": "Falhou",
"sync_download_sources": "Sincronizar",
"removed_download_source": "Fonte removida",
"added_download_source": "Fonte adicionada",
"download_sources_synced": "As fontes foram sincronizadas",
"insert_valid_json_url": "Insere o URL de um JSON válido",
"insert_valid_json_url": "Insira o URL de um JSON válido",
"found_download_option_zero": "Nenhuma opção de transferência encontrada",
"found_download_option_one": "{{countFormatted}} opção de transferência encontrada",
"found_download_option_other": "{{countFormatted}} opções de transferência encontradas",
"import": "Importar",
"privacy": "Privacidade",
"private": "Privado",
"friends_only": "Apenas amigos",
"public": "Público",
"profile_visibility": "Visibilidade do perfil",
"profile_visibility_description": "Escolhe quem pode ver o teu perfil e biblioteca",
"required_field": "Este campo é obrigatório",
"source_already_exists": "Essa fonte já foi adicionada",
"must_be_valid_url": "A fonte deve ser um URL válido",
"blocked_users": "Utilizadores bloqueados",
"user_unblocked": "Utilizador desbloqueado",
"enable_achievement_notifications": "Quando uma conquista é desbloqueada"
"found_download_option_other": "{{countFormatted}} opções de transferências encontradas",
"import": "Importar"
},
"notifications": {
"download_complete": "Transferência concluída",
"game_ready_to_install": "{{title}} está pronto para ser instalado",
"game_ready_to_install": "{{title}} está pronto para ser descarregado",
"repack_list_updated": "Lista de repacks atualizada",
"repack_count_one": "{{count}} novo repack",
"repack_count_other": "{{count}} novos repacks",
"new_update_available": "Versão {{version}} disponível",
"restart_to_install_update": "Reinicia o Hydra para instalar a nova versão"
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão"
},
"system_tray": {
"open": "Abrir o Hydra",
"quit": "Sair"
"open": "Abrir Hydra",
"quit": "Fechar"
},
"game_card": {
"no_downloads": "Sem downloads disponíveis"
"no_downloads": "Sem transferências disponíveis"
},
"binary_not_found_modal": {
"title": "Programas não instalados",
"description": "Os executáveis do Wine ou Lutris não foram encontrados no teu sistema.",
"instructions": "Verifica a forma correta de instalar algum deles na tua distribuição Linux, para garantir a execução normal do jogo"
"description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.",
"instructions": "Verifique a forma correta de instalar algum deles na sua distro Linux, garantindo assim a execução normal do jogo."
},
"catalogue": {
"next_page": "Página seguinte",
"next_page": "Próxima página",
"previous_page": "Página anterior"
},
"modal": {
@@ -285,24 +231,24 @@
"amount_hours": "{{amount}} horas",
"amount_minutes": "{{amount}} minutos",
"last_time_played": "Última sessão {{period}}",
"activity": "Atividade recente",
"activity": "Atividades recentes",
"library": "Biblioteca",
"total_play_time": "Tempo total de jogo: {{amount}}",
"no_recent_activity_title": "Hmmm… não há nada por aqui",
"no_recent_activity_title": "Hmmm… nada por aqui",
"no_recent_activity_description": "Parece que não jogaste nada recentemente. Que tal começar agora?",
"display_name": "Nome de apresentação",
"saving": "A guardar…",
"display_name": "Nome de exibição",
"saving": "a guardar…",
"save": "Guardar",
"edit_profile": "Editar perfil",
"saved_successfully": "Guardado com sucesso",
"try_again": "Por favor, tenta novamente",
"cancel": "Cancelar",
"successfully_signed_out": "Sessão terminada com sucesso",
"successfully_signed_out": "Terminado com sucesso",
"sign_out": "Terminar sessão",
"sign_out_modal_title": "Desejas mesmo terminar sessão?",
"playing_for": "A jogar por {{amount}}",
"sign_out_modal_text": "A tua biblioteca de jogos está associada à conta atual. Ao terminar sessão, a tua biblioteca não irá aparecer mais no Hydra e qualquer progresso não será guardado. Desejas continuar?",
"add_friends": "Adicionar amigos",
"sign_out_modal_title": "Tens a certeza?",
"playing_for": "A jogar {{amount}}",
"sign_out_modal_text": "A tua biblioteca de jogos está associada com a tua conta atual. Ao sair, a tua biblioteca não aparecerá mais no Hydra e qualquer progresso não será guardado. Desejas continuar?",
"add_friends": "Adicionar Amigos",
"friend_code": "Código de amigo",
"see_profile": "Ver perfil",
"friend_request_sent": "Pedido de amizade enviado",
@@ -324,49 +270,12 @@
"user_block_modal_text": "Bloquear {{displayName}}",
"blocked_users": "Utilizadores bloqueados",
"unblock": "Desbloquear",
"no_friends_added": "Ainda não adicionaste nenhum amigo",
"no_friends_added": "Ainda não adicionaste amigos",
"pending": "Pendentes",
"no_pending_invites": "Não tens pedidos de amizade pendentes",
"no_pending_invites": "Não tens convites de amizade pendentes",
"no_blocked_users": "Não tens nenhum utilizador bloqueado",
"friend_code_copied": "Código de amigo copiado",
"undo_friendship_modal_text": "Isto vai remover a tua amizade com {{displayName}}",
"privacy_hint": "Para controlar quem pode ver o teu perfil, acede às <0>Definições</0>",
"profile_locked": "Este perfil é privado",
"image_process_failure": "Falha ao processar a imagem",
"required_field": "Este campo é obrigatório",
"displayname_min_length": "O nome de apresentação deve ter pelo menos 3 caracteres",
"displayname_max_length": "O nome de apresentação deve ter no máximo 50 caracteres",
"locked_profile": "Este perfil é privado",
"report_profile": "Denunciar este perfil",
"report_reason": "Por que é que desejas denunciar este perfil?",
"report_description": "Informações adicionais",
"report_description_placeholder": "Insere aqui",
"report": "Denunciar",
"report_reason_hate": "Discurso de ódio",
"report_reason_sexual_content": "Conteúdo sexual",
"report_reason_violence": "Violência",
"report_reason_spam": "Spam",
"report_reason_other": "Outro",
"profile_reported": "Perfil denunciado",
"your_friend_code": "O teu código de amigo:",
"upload_banner": "Fazer upload do banner",
"uploading_banner": "A fazer upload do banner…"
},
"achievement": {
"achievement_unlocked": "Conquista desbloqueada",
"your_achievements": "As tuas Conquistas",
"user_achievements": "Conquistas de {{displayName}}",
"unlocked_at": "Desbloqueada em:",
"subscription_needed": "Precisas de uma subscrição Hydra Cloud para visualizar este conteúdo",
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos"
},
"tour": {
"subscription_tour_title": "Subscrição Hydra Cloud",
"subscribe_now": "Subscreve agora",
"cloud_achievements": "Gravação de conquistas na nuvem",
"animated_profile_picture": "Fotos de perfil animadas",
"premium_support": "Apoio Premium",
"show_and_compare_achievements": "Mostra e compara as tuas conquistas com as de outros utilizadores",
"animated_profile_banner": "Banner animado no perfil"
"your_friend_code": "Seu código de amigo:"
}
}

View File

@@ -2,6 +2,7 @@
"language_name": "Română",
"home": {
"featured": "Recomandate",
"trending": "Populare",
"surprise_me": "Surprinde-mă",
"no_results": "Niciun rezultat găsit"
},

View File

@@ -5,9 +5,10 @@
},
"home": {
"featured": "Рекомендованное",
"trending": "В тренде",
"surprise_me": "Удиви меня",
"no_results": "Ничего не найдено",
"hot": "Сейчас в топе",
"hot": "Сейчас жарко",
"start_typing": "Начинаю вводить текст для поиска...",
"weekly": "📅 Лучшие игры недели"
},
@@ -24,8 +25,7 @@
"queued": "{{title}} (В очереди)",
"game_has_no_executable": "Файл запуска игры не выбран",
"sign_in": "Войти",
"friends": "Друзья",
"need_help": "Нужна помощь?"
"friends": "Друзья"
},
"header": {
"search": "Поиск",
@@ -206,6 +206,9 @@
"found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки",
"import": "Импортировать",
"blocked_users": "Заблокированные пользователи",
"download_options_one": "",
"download_options_other": "",
"download_options_zero": "",
"friends_only": "Только друзья",
"must_be_valid_url": "Источник должен быть действительным URL-адресом.",
"privacy": "Конфиденциальность",
@@ -297,6 +300,7 @@
"image_process_failure": "Сбой при обработке изображения",
"locked_profile": "Этот профиль является частным",
"privacy_hint": "Чтобы указать, кто может это видеть, перейдите в <0>Настройки</0>.",
"profile_locked": "",
"profile_reported": "Профиль сообщил",
"report": "Отчет",
"report_description": "Дополнительная информация",

View File

@@ -2,6 +2,7 @@
"language_name": "Türkçe",
"home": {
"featured": "Öne çıkan",
"trending": "Popüler",
"surprise_me": "Şaşırt beni",
"no_results": "Sonuç bulunamadı"
},

View File

@@ -5,6 +5,7 @@
},
"home": {
"featured": "Рекомендоване",
"trending": "У тренді",
"surprise_me": "Здивуй мене",
"no_results": "Результатів не знайдено"
},
@@ -160,6 +161,9 @@
"download_count_one": "{{countFormatted}} завантаження в списку",
"download_count_other": "{{countFormatted}} завантажень в списку",
"download_count_zero": "В списку немає завантажень",
"download_options_one": "{{countFormatted}} доступний варіант завантаження",
"download_options_other": "{{countFormatted}} доступних варіантів завантаження",
"download_options_zero": "Немає доступних завантажень",
"download_source_errored": "Помилка",
"download_source_up_to_date": "Оновлено",
"download_source_url": "Посилання на джерело",

View File

@@ -5,12 +5,9 @@
},
"home": {
"featured": "特色推荐",
"trending": "最近热门",
"surprise_me": "向我推荐",
"no_results": "没有找到结果",
"start_typing": "键入以开始搜素...",
"hot": "当下热门",
"weekly": "📅本周热门游戏",
"achievements": "🏆尝试击败"
"no_results": "没有找到结果"
},
"sidebar": {
"catalogue": "游戏目录",
@@ -24,8 +21,7 @@
"home": "主页",
"queued": "{{title}} (已加入下载队列)",
"game_has_no_executable": "未选择游戏的可执行文件",
"sign_in": "登入",
"friends": "好友"
"sign_in": "登入"
},
"header": {
"search": "搜索游戏",
@@ -41,18 +37,17 @@
"no_downloads_in_progress": "没有正在进行的下载",
"downloading_metadata": "正在下载{{title}}的元数据…",
"downloading": "正在下载{{title}}… ({{percentage}}完成) - 剩余时间{{eta}} - 速度{{speed}}",
"calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间...",
"checking_files": "正在校验 {{title}} 的文件... ({{percentage}} 已完成)"
"calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间..."
},
"catalogue": {
"next_page": "下一页",
"previous_page": "上一页"
},
"game_details": {
"open_download_options": "打开下载菜单",
"download_options_zero": "无下载项",
"download_options_one": "{{count}}个下载项",
"download_options_other": "{{count}}个下载项",
"open_download_options": "打开下载选项",
"download_options_zero": "无下载项",
"download_options_one": "{{count}}个下载项",
"download_options_other": "{{count}}个下载项",
"updated_at": "更新于{{updated_at}}",
"install": "安装",
"resume": "恢复",
@@ -61,13 +56,11 @@
"remove": "移除",
"space_left_on_disk": "磁盘剩余空间{{space}}",
"eta": "预计完成时间{{eta}}",
"calculating_eta": "正在计算剩余时间…",
"downloading_metadata": "正在下载元数据…",
"filter": "筛选重打包",
"requirements": "配置要求",
"minimum": "最低要求",
"recommended": "推荐要求",
"paused": "已暂停",
"release_date": "发布于{{date}}",
"publisher": "发行商{{publisher}}",
"hours": "小时",
@@ -88,18 +81,15 @@
"playing_now": "正在游戏中",
"change": "更改",
"repacks_modal_description": "选择您想要下载的重打包",
"select_folder_hint": "要更改默认文件夹,请访问<0>设置</0>",
"select_folder_hint": "要更改默认文件夹,请访问",
"download_now": "立即下载",
"no_shop_details": "无法检索商店详细信息.",
"download_options": "下载选项",
"download_path": "下载路径",
"previous_screenshot": "上一张截图",
"next_screenshot": "下一张截图",
"screenshot": "截图 {{number}}",
"open_screenshot": "打开截图 {{number}}",
"download_settings": "下载设置",
"downloader": "下载器",
"select_executable": "选择可执行文件",
"select_executable": "选择",
"no_executable_selected": "没有可执行文件被指定",
"open_folder": "打开目录",
"open_download_location": "查看已下载的文件",
@@ -118,54 +108,7 @@
"download_paused": "下载暂停",
"last_downloaded_option": "上次下载的选项",
"create_shortcut_success": "成功创建快捷方式",
"create_shortcut_error": "创建快捷方式出错",
"nsfw_content_title": "本游戏包含不适合展示的内容",
"nsfw_content_description": "{{title}}包含可能不适合所有年龄段的内容。您确定要继续吗?",
"allow_nsfw_content": "继续",
"refuse_nsfw_content": "返回",
"stats": "统计数据",
"download_count": "下载量",
"player_count": "活跃玩家",
"download_error": "此下载选项不可用",
"download": "下载",
"executable_path_in_use": "可执行文件已经被以下游戏 \"{{game}}\" 使用",
"warning": "警告:",
"hydra_needs_to_remain_open": "对于此次下载,Hydra必须保持开启直至其完成。若海德拉在完成前关闭,您的进度将丢失。",
"achievements": "成就",
"achievements_count": "成就 {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "云存档",
"cloud_save_description": "将您的进度保存在云端,便可在任何设备上继续游戏。",
"backups": "备份",
"install_backup": "安装",
"delete_backup": "删除",
"create_backup": "新备份",
"last_backup_date": "最后一次备份于{{date}}",
"no_backup_preview": "未找到此游戏标题的存档",
"restoring_backup": "正在恢复备份({{progress}}已完成)…",
"uploading_backup": "上传备份中…",
"no_backups": "您尚未为这款游戏创建任何备份",
"backup_uploaded": "备份已上传",
"backup_deleted": "备份已删除",
"backup_restored": "备份已恢复",
"see_all_achievements": "查看所有成就",
"sign_in_to_see_achievements": "登入以查看所有成就",
"mapping_method_automatic": "自动",
"mapping_method_manual": "常规",
"mapping_method_label": "索引类型",
"files_automatically_mapped": "文件已自动索引",
"no_backups_created": "没有为此游戏创建过备份",
"manage_files": "管理文件",
"loading_save_preview": "正在查找要保存的游戏…",
"wine_prefix": "Wine 前置",
"wine_prefix_description": "运行该游戏所用的 Wine 前置",
"no_download_option_info": "无可用信息",
"backup_deletion_failed": "删除备份失败",
"max_number_of_artifacts_reached": "已达到该游戏备份上限",
"achievements_not_sync": "你的成就未同步",
"manage_files_description": "管理哪些文件要备份和恢复",
"select_folder": "选择文件夹",
"backup_from": "{{date}} 时备份",
"custom_backup_location_set": "自定义备份文件位置"
"create_shortcut_error": "创建快捷方式出错"
},
"activation": {
"title": "激活 Hydra",
@@ -182,7 +125,6 @@
"paused": "已暂停",
"verifying": "正在验证…",
"completed": "已完成",
"removed": "未下载",
"cancel": "取消",
"filter": "筛选已下载游戏",
"remove": "移除",
@@ -197,8 +139,7 @@
"downloads_completed": "已完成",
"queued": "下载列表",
"no_downloads_title": "空空如也",
"no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。",
"checking_files": "正在校验文件…"
"no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。"
},
"settings": {
"downloads_path": "下载路径",
@@ -229,6 +170,9 @@
"download_count_zero": "列表中无下载",
"download_count_one": "列表中有 {{countFormatted}} 个下载",
"download_count_other": "列表中有 {{countFormatted}} 个下载",
"download_options_zero": "无可用下载",
"download_options_one": "有 {{countFormatted}} 个下载可用",
"download_options_other": "有 {{countFormatted}} 个下载可用",
"download_source_url": "下载源 URL",
"add_download_source_description": "插入包含 .json 文件的 URL",
"download_source_up_to_date": "已更新",
@@ -241,49 +185,14 @@
"found_download_option_zero": "未找到下载选项",
"found_download_option_one": "找到 {{countFormatted}} 个下载选项",
"found_download_option_other": "找到 {{countFormatted}} 个下载选项",
"import": "导入",
"public": "公开",
"private": "私密",
"friends_only": "仅限朋友",
"privacy": "隐私",
"profile_visibility": "资料可见性",
"profile_visibility_description": "选择谁可以查看您的个人资料和资料库",
"required_field": "该字段为必填字段",
"source_already_exists": "已添加此来源",
"must_be_valid_url": "来源必须是有效的 URL",
"blocked_users": "已屏蔽用户",
"user_unblocked": "用户已经被屏蔽",
"enable_achievement_notifications": "当成就解锁时"
},
"notifications": {
"download_complete": "下载完成",
"game_ready_to_install": "{{title}} 已准备就绪",
"repack_list_updated": "重打包列表已更新",
"repack_count_one": "{{count}} 重打包已添加",
"repack_count_other": "{{count}} 重打包已添加",
"new_update_available": "版本 {{version}} 可用",
"restart_to_install_update": "重启 Hydra 以安装更新",
"notification_achievement_unlocked_title": "{{game}} 的成绩已解锁",
"notification_achievement_unlocked_body": "{{achievement}} 和其他 {{count}} 已解锁"
},
"system_tray": {
"open": "打开 Hydra",
"quit": "退出"
},
"game_card": {
"no_downloads": "无可用下载选项"
},
"binary_not_found_modal": {
"title": "程序未安装",
"description": "您的系统中找不到 Wine 或 Lutris 的可执行文件",
"instructions": "请检查在 Linux 发行版上安装这些软件的正确方法,以便游戏能够正常运行"
},
"forms": {
"toggle_password_visibility": "切换密码可见性"
"import": "导入"
},
"modal": {
"close": "关闭按钮"
},
"forms": {
"toggle_password_visibility": "切换密码可见性"
},
"user_profile": {
"amount_hours": "{{amount}} 小时",
"amount_minutes": "{{amount}} 分钟",
@@ -303,74 +212,7 @@
"cancel": "取消",
"successfully_signed_out": "登出成功",
"sign_out": "登出",
"playing_for": "已经玩了{{amount}}",
"sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?",
"add_friends": "添加好友",
"add": "添加",
"friend_code": "好友代码",
"see_profile": "查看资料",
"sending": "发送中",
"friend_request_sent": "好友请求已发送",
"friends": "好友",
"friends_list": "好友列表",
"user_not_found": "未找到此用户",
"block_user": "屏蔽此用户",
"add_friend": "添加好友",
"request_sent": "请求已发送",
"request_received": "已收到请求",
"accept_request": "同意申请",
"ignore_request": "忽略申请",
"cancel_request": "取消申请",
"undo_friendship": "解除好友关系",
"request_accepted": "请求已通过",
"user_blocked_successfully": "成功屏蔽此用户",
"user_block_modal_text": "这将会屏蔽 {{displayName}}",
"blocked_users": "黑名单用户",
"unblock": "解除屏蔽",
"no_friends_added": "你还没有添加过好友",
"pending": "待处理",
"no_pending_invites": "您没有待处理的邀请",
"no_blocked_users": "你没有已经拉人黑名单的用户",
"friend_code_copied": "好友代码已复制",
"undo_friendship_modal_text": "这将使你与 {{displayName}} 解除好友关系",
"privacy_hint": "要调整谁可以看到你的个人资料,可以去<0>设置</0>中修改",
"locked_profile": "此个人资料是私密的",
"image_process_failure": "处理图片时发生错误",
"required_field": "此字段为必填项",
"displayname_min_length": "显示名称最少必须为3个字符。",
"displayname_max_length": "显示名称最多必须为50个字符",
"report_profile": "举报此资料",
"report_reason": "为什么你要举报此资料?",
"report_description": "额外信息",
"report_description_placeholder": "额外信息",
"report": "举报",
"report_reason_hate": "Hate speech",
"report_reason_sexual_content": "色情内容",
"report_reason_violence": "暴力",
"report_reason_spam": "骚扰",
"report_reason_other": "其他",
"profile_reported": "个人资料已举报",
"your_friend_code": "你的好友代码:",
"upload_banner": "上传横幅",
"uploading_banner": "上传横幅中…",
"background_image_updated": "背景图片已更新"
},
"achievement": {
"achievement_unlocked": "成就已解锁",
"user_achievements": "{{displayName}}的成就",
"your_achievements": "你的成就",
"unlocked_at": "解锁于:",
"subscription_needed": "需要订阅 Hydra Cloud 才能看到此内容",
"new_achievements_unlocked": "从 {{gameCount}} 游戏中解锁 {{achievementCount}} 新成就"
},
"tour": {
"subscription_tour_title": "Hydra 云订阅",
"subscribe_now": "现在订购",
"cloud_saving": "云存档",
"cloud_achievements": "将你的成就保存至云端",
"animated_profile_picture": "动画头像",
"premium_support": "高级技术支持",
"show_and_compare_achievements": "展示并与其他用户比较您的成就",
"animated_profile_banner": "动态个人简介横幅"
"playing_for": "Playing for {{amount}}",
"sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?"
}
}

View File

@@ -1,17 +1,10 @@
import { app } from "electron";
import path from "node:path";
export const LUDUSAVI_MANIFEST_URL = "https://cdn.losbroxas.org/manifest.yaml";
export const defaultDownloadsPath = app.getPath("downloads");
export const databaseDirectory = path.join(app.getPath("appData"), "hydra");
export const databasePath = path.join(
databaseDirectory,
import.meta.env.MAIN_VITE_API_URL.includes("staging")
? "hydra_test.db"
: "hydra.db"
);
export const databasePath = path.join(databaseDirectory, "hydra.db");
export const logsPath = path.join(app.getPath("appData"), "hydra", "logs");
@@ -19,10 +12,4 @@ export const seedsPath = app.isPackaged
? path.join(process.resourcesPath, "seeds")
: path.join(__dirname, "..", "..", "seeds");
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 appVersion = app.getVersion();

View File

@@ -7,8 +7,6 @@ import {
Repack,
UserPreferences,
UserAuth,
GameAchievement,
UserSubscription,
} from "@main/entity";
import { databasePath } from "./constants";
@@ -18,13 +16,11 @@ export const dataSource = new DataSource({
entities: [
Game,
Repack,
UserAuth,
UserPreferences,
UserSubscription,
GameShopCache,
DownloadSource,
DownloadQueue,
GameAchievement,
UserAuth,
],
synchronize: false,
database: databasePath,

View File

@@ -1,19 +0,0 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity("game_achievement")
export class GameAchievement {
@PrimaryGeneratedColumn()
id: number;
@Column("text")
objectId: string;
@Column("text")
shop: string;
@Column("text", { nullable: true })
unlockedAchievements: string | null;
@Column("text", { nullable: true })
achievements: string | null;
}

View File

@@ -18,9 +18,6 @@ export class GameShopCache {
@Column("text", { nullable: true })
serializedData: string;
/**
* @deprecated Use IndexedDB's `howLongToBeatEntries` instead
*/
@Column("text", { nullable: true })
howLongToBeatSerializedData: string;

View File

@@ -39,9 +39,6 @@ export class Game {
@Column("text", { nullable: true })
executablePath: string | null;
@Column("text", { nullable: true })
winePrefixPath: string | null;
@Column("int", { default: 0 })
playTimeInMilliseconds: number;

View File

@@ -1,10 +1,7 @@
export * from "./game.entity";
export * from "./repack.entity";
export * from "./user-auth.entity";
export * from "./user-preferences.entity";
export * from "./user-subscription.entity";
export * from "./game-shop-cache.entity";
export * from "./game.entity";
export * from "./game-achievements.entity";
export * from "./download-source.entity";
export * from "./download-queue.entity";
export * from "./user-auth";

View File

@@ -4,9 +4,7 @@ import {
Column,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
} from "typeorm";
import { UserSubscription } from "./user-subscription.entity";
@Entity("user_auth")
export class UserAuth {
@@ -22,9 +20,6 @@ export class UserAuth {
@Column("text", { nullable: true })
profileImageUrl: string | null;
@Column("text", { nullable: true })
backgroundImageUrl: string | null;
@Column("text", { default: "" })
accessToken: string;
@@ -34,9 +29,6 @@ export class UserAuth {
@Column("int", { default: 0 })
tokenExpirationTimestamp: number;
@OneToOne("UserSubscription", "user")
subscription: UserSubscription | null;
@CreateDateColumn()
createdAt: Date;

View File

@@ -26,18 +26,12 @@ export class UserPreferences {
@Column("boolean", { default: false })
repackUpdatesNotificationsEnabled: boolean;
@Column("boolean", { default: true })
achievementNotificationsEnabled: boolean;
@Column("boolean", { default: false })
preferQuitInsteadOfHiding: boolean;
@Column("boolean", { default: false })
runAtStartup: boolean;
@Column("boolean", { default: false })
startMinimized: boolean;
@CreateDateColumn()
createdAt: Date;

View File

@@ -1,42 +0,0 @@
import type { SubscriptionStatus } from "@types";
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
JoinColumn,
} from "typeorm";
import { UserAuth } from "./user-auth.entity";
@Entity("user_subscription")
export class UserSubscription {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { default: "" })
subscriptionId: string;
@OneToOne("UserAuth", "subscription")
@JoinColumn()
user: UserAuth;
@Column("text", { default: "" })
status: SubscriptionStatus;
@Column("text", { default: "" })
planId: string;
@Column("text", { default: "" })
planName: string;
@Column("datetime", { nullable: true })
expiresAt: Date | null;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -1,4 +1,5 @@
import jwt from "jsonwebtoken";
import * as Sentry from "@sentry/electron/main";
import { userAuthRepository } from "@main/repository";
import { registerEvent } from "../register-event";
@@ -9,6 +10,8 @@ const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
if (!auth) return null;
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;
Sentry.setContext("sessionId", payload.sessionId);
return payload.sessionId;
};

View File

@@ -1,4 +1,5 @@
import { registerEvent } from "../register-event";
import * as Sentry from "@sentry/electron/main";
import {
DownloadManager,
HydraApi,
@@ -6,7 +7,7 @@ import {
gamesPlaytime,
} from "@main/services";
import { dataSource } from "@main/data-source";
import { DownloadQueue, Game, UserAuth, UserSubscription } from "@main/entity";
import { DownloadQueue, Game, UserAuth } from "@main/entity";
const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
const databaseOperations = dataSource
@@ -18,16 +19,15 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
await transactionalEntityManager
.getRepository(UserAuth)
.delete({ id: 1 });
await transactionalEntityManager
.getRepository(UserSubscription)
.delete({ id: 1 });
})
.then(() => {
/* Removes all games being played */
gamesPlaytime.clear();
});
/* Removes user from Sentry */
Sentry.setUser(null);
/* Cancels any ongoing downloads */
DownloadManager.cancelDownload();

View File

@@ -1,4 +1,4 @@
import type { AppUpdaterEvent } from "@types";
import { AppUpdaterEvent } from "@types";
import { registerEvent } from "../register-event";
import updater, { UpdateInfo } from "electron-updater";
import { WindowManager } from "@main/services";

View File

@@ -7,7 +7,7 @@ const { autoUpdater } = updater;
const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
autoUpdater.removeAllListeners();
if (app.isPackaged) {
autoUpdater.quitAndInstall(false);
autoUpdater.quitAndInstall(true, true);
}
};

View File

@@ -15,7 +15,7 @@ const getCatalogue = async (
});
const response = await HydraApi.get<{ objectId: string; shop: GameShop }[]>(
`/catalogue/${category}?${params.toString()}`,
`/games/${category}?${params.toString()}`,
{},
{ needsAuth: false }
);
@@ -30,7 +30,7 @@ const getCatalogue = async (
title: steamGame.name,
shop: game.shop,
cover: steamUrlBuilder.library(game.objectId),
objectId: game.objectId,
objectID: game.objectId,
};
})
);

View File

@@ -7,16 +7,16 @@ import { registerEvent } from "../register-event";
import { steamGamesWorker } from "@main/workers";
const getLocalizedSteamAppDetails = async (
objectId: string,
objectID: string,
language: string
): Promise<ShopDetails | null> => {
if (language === "english") {
return getSteamAppDetails(objectId, language);
return getSteamAppDetails(objectID, language);
}
return getSteamAppDetails(objectId, language).then(
return getSteamAppDetails(objectID, language).then(
async (localizedAppDetails) => {
const steamGame = await steamGamesWorker.run(Number(objectId), {
const steamGame = await steamGamesWorker.run(Number(objectID), {
name: "getById",
});
@@ -34,21 +34,21 @@ const getLocalizedSteamAppDetails = async (
const getGameShopDetails = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
objectID: string,
shop: GameShop,
language: string
): Promise<ShopDetails | null> => {
if (shop === "steam") {
const cachedData = await gameShopCacheRepository.findOne({
where: { objectID: objectId, language },
where: { objectID, language },
});
const appDetails = getLocalizedSteamAppDetails(objectId, language).then(
const appDetails = getLocalizedSteamAppDetails(objectID, language).then(
(result) => {
if (result) {
gameShopCacheRepository.upsert(
{
objectID: objectId,
objectID,
shop: "steam",
language,
serializedData: JSON.stringify(result),
@@ -68,7 +68,7 @@ const getGameShopDetails = async (
if (cachedGame) {
return {
...cachedGame,
objectId,
objectID,
} as ShopDetails;
}

View File

@@ -1,17 +1,23 @@
import type { GameShop, GameStats } from "@types";
import type { GameShop } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import type { GameStats } from "@types";
const getGameStats = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop
) => {
return HydraApi.get<GameStats>(
`/games/stats`,
{ objectId, shop },
{ needsAuth: false }
const params = new URLSearchParams({
objectId,
shop,
});
const response = await HydraApi.get<GameStats>(
`/games/stats?${params.toString()}`
);
return response;
};
registerEvent("getGameStats", getGameStats);

View File

@@ -1,29 +1,28 @@
import type { CatalogueEntry } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { steamGamesWorker } from "@main/workers";
import { steamUrlBuilder } from "@shared";
const getGames = async (
_event: Electron.IpcMainInvokeEvent,
take = 12,
skip = 0
): Promise<CatalogueEntry[]> => {
const searchParams = new URLSearchParams({
take: take.toString(),
skip: skip.toString(),
});
const games = await HydraApi.get<CatalogueEntry[]>(
`/games/catalogue?${searchParams.toString()}`,
undefined,
{ needsAuth: false }
cursor = 0
): Promise<{ results: CatalogueEntry[]; cursor: number }> => {
const steamGames = await steamGamesWorker.run(
{ limit: take, offset: cursor },
{ name: "list" }
);
return games.map((game) => ({
...game,
cover: steamUrlBuilder.library(game.objectId),
}));
return {
results: steamGames.map((steamGame) => ({
title: steamGame.name,
shop: "steam",
cover: steamUrlBuilder.library(steamGame.id),
objectID: steamGame.id,
})),
cursor: cursor + steamGames.length,
};
};
registerEvent("getGames", getGames);

View File

@@ -1,23 +1,45 @@
import type { HowLongToBeatCategory } from "@types";
import type { GameShop, HowLongToBeatCategory } from "@types";
import { getHowLongToBeatGame, searchHowLongToBeat } from "@main/services";
import { registerEvent } from "../register-event";
import { formatName } from "@shared";
import { gameShopCacheRepository } from "@main/repository";
const getHowLongToBeat = async (
_event: Electron.IpcMainInvokeEvent,
objectID: string,
shop: GameShop,
title: string
): Promise<HowLongToBeatCategory[] | null> => {
const response = await searchHowLongToBeat(title);
const searchHowLongToBeatPromise = searchHowLongToBeat(title);
const game = response.data.find((game) => {
return formatName(game.game_name) === formatName(title);
const gameShopCache = await gameShopCacheRepository.findOne({
where: { objectID, shop },
});
if (!game) return null;
const howLongToBeat = await getHowLongToBeatGame(String(game.game_id));
const howLongToBeatCachedData = gameShopCache?.howLongToBeatSerializedData
? JSON.parse(gameShopCache?.howLongToBeatSerializedData)
: null;
if (howLongToBeatCachedData) return howLongToBeatCachedData;
return howLongToBeat;
return searchHowLongToBeatPromise.then(async (response) => {
const game = response.data.find(
(game) => game.profile_steam === Number(objectID)
);
if (!game) return null;
const howLongToBeat = await getHowLongToBeatGame(String(game.game_id));
gameShopCacheRepository.upsert(
{
objectID,
shop,
howLongToBeatSerializedData: JSON.stringify(howLongToBeat),
},
["objectID"]
);
return howLongToBeat;
});
};
registerEvent("getHowLongToBeat", getHowLongToBeat);

View File

@@ -1,7 +1,7 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { userPreferencesRepository } from "@main/repository";
import type { TrendingGame } from "@types";
import { TrendingGame } from "@types";
const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => {
const userPreferences = await userPreferencesRepository.findOne({

View File

@@ -1,6 +1,6 @@
import { registerEvent } from "../register-event";
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
import type { CatalogueEntry } from "@types";
import { CatalogueEntry } from "@types";
import { HydraApi } from "@main/services";
const searchGamesEvent = async (

View File

@@ -1,12 +0,0 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const deleteGameArtifact = async (
_event: Electron.IpcMainInvokeEvent,
gameArtifactId: string
) =>
HydraApi.delete<{ ok: boolean }>(
`/profile/games/artifacts/${gameArtifactId}`
);
registerEvent("deleteGameArtifact", deleteGameArtifact);

View File

@@ -1,147 +0,0 @@
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
import fs from "node:fs";
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";
export interface LudusaviBackup {
files: {
[key: string]: {
hash: string;
size: number;
};
};
}
const replaceLudusaviBackupWithCurrentUser = (
backupPath: string,
title: string,
homeDir: string
) => {
const gameBackupPath = path.join(backupPath, title);
const mappingYamlPath = path.join(gameBackupPath, "mapping.yaml");
const data = fs.readFileSync(mappingYamlPath, "utf8");
const manifest = YAML.parse(data) as {
backups: LudusaviBackup[];
drives: Record<string, string>;
};
const currentHomeDir = normalizePath(app.getPath("home"));
/* Renaming logic */
if (os.platform() === "win32") {
const mappedHomeDir = path.join(
gameBackupPath,
path.join("drive-C", homeDir.replace("C:", ""))
);
if (fs.existsSync(mappedHomeDir)) {
fs.renameSync(
mappedHomeDir,
path.join(gameBackupPath, "drive-C", currentHomeDir.replace("C:", ""))
);
}
}
const backups = manifest.backups.map((backup: LudusaviBackup) => {
const files = Object.entries(backup.files).reduce((prev, [key, value]) => {
const updatedKey = key.replace(homeDir, currentHomeDir);
return {
...prev,
[updatedKey]: value,
};
}, {});
return {
...backup,
files,
};
});
fs.writeFileSync(mappingYamlPath, YAML.stringify({ ...manifest, backups }));
};
const downloadGameArtifact = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
gameArtifactId: string
) => {
try {
const { downloadUrl, objectKey, homeDir } = await HydraApi.post<{
downloadUrl: string;
objectKey: string;
homeDir: string;
}>(`/profile/games/artifacts/${gameArtifactId}/download`);
const zipLocation = path.join(app.getPath("userData"), objectKey);
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
if (fs.existsSync(backupPath)) {
fs.rmSync(backupPath, {
recursive: true,
force: true,
});
}
const response = await axios.get(downloadUrl, {
responseType: "stream",
onDownloadProgress: (progressEvent) => {
WindowManager.mainWindow?.webContents.send(
`on-backup-download-progress-${objectId}-${shop}`,
progressEvent
);
},
});
const writer = fs.createWriteStream(zipLocation);
response.data.pipe(writer);
writer.on("error", (err) => {
logger.error("Failed to write zip", err);
throw err;
});
fs.mkdirSync(backupPath, { recursive: true });
writer.on("close", () => {
tar
.x({
file: zipLocation,
cwd: backupPath,
})
.then(async () => {
replaceLudusaviBackupWithCurrentUser(
backupPath,
objectId,
normalizePath(homeDir)
);
Ludusavi.restoreBackup(backupPath).then(() => {
WindowManager.mainWindow?.webContents.send(
`on-backup-download-complete-${objectId}-${shop}`,
true
);
});
});
});
} catch (err) {
WindowManager.mainWindow?.webContents.send(
`on-backup-download-complete-${objectId}-${shop}`,
false
);
}
};
registerEvent("downloadGameArtifact", downloadGameArtifact);

View File

@@ -1,20 +0,0 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
import type { GameArtifact, GameShop } from "@types";
const getGameArtifacts = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop
) => {
const params = new URLSearchParams({
objectId,
shop,
});
return HydraApi.get<GameArtifact[]>(
`/profile/games/artifacts?${params.toString()}`
);
};
registerEvent("getGameArtifacts", getGameArtifacts);

View File

@@ -1,21 +0,0 @@
import { registerEvent } from "../register-event";
import type { GameShop } from "@types";
import { Ludusavi } from "@main/services";
import { gameRepository } from "@main/repository";
const getGameBackupPreview = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop
) => {
const game = await gameRepository.findOne({
where: {
objectID: objectId,
shop,
},
});
return Ludusavi.getBackupPreview(shop, objectId, game?.winePrefixPath);
};
registerEvent("getGameBackupPreview", getGameBackupPreview);

View File

@@ -1,14 +0,0 @@
import { registerEvent } from "../register-event";
import type { GameShop } from "@types";
import { Ludusavi } from "@main/services";
const selectGameBackupPath = async (
_event: Electron.IpcMainInvokeEvent,
_shop: GameShop,
objectId: string,
backupPath: string | null
) => {
return Ludusavi.addCustomGame(objectId, backupPath);
};
registerEvent("selectGameBackupPath", selectGameBackupPath);

View File

@@ -1,111 +0,0 @@
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
import { registerEvent } from "../register-event";
import fs from "node:fs";
import path from "node:path";
import * as tar from "tar";
import crypto from "node:crypto";
import type { GameShop } from "@types";
import axios from "axios";
import os from "node:os";
import { backupsPath } from "@main/constants";
import { app } from "electron";
import { normalizePath } from "@main/helpers";
import { gameRepository } from "@main/repository";
const bundleBackup = async (
shop: GameShop,
objectId: string,
winePrefix: string | null
) => {
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
// Remove existing backup
if (fs.existsSync(backupPath)) {
fs.rmSync(backupPath, { recursive: true });
}
await Ludusavi.backupGame(shop, objectId, backupPath, winePrefix);
const tarLocation = path.join(backupsPath, `${crypto.randomUUID()}.tar`);
await tar.create(
{
gzip: false,
file: tarLocation,
cwd: backupPath,
},
["."]
);
return tarLocation;
};
const uploadSaveGame = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
downloadOptionTitle: string | null
) => {
const game = await gameRepository.findOne({
where: {
objectID: objectId,
shop,
},
});
const bundleLocation = await bundleBackup(
shop,
objectId,
game?.winePrefixPath ?? null
);
fs.stat(bundleLocation, async (err, stat) => {
if (err) {
logger.error("Failed to get zip file stats", err);
throw err;
}
const { uploadUrl } = await HydraApi.post<{
id: string;
uploadUrl: string;
}>("/profile/games/artifacts", {
artifactLengthInBytes: stat.size,
shop,
objectId,
hostname: os.hostname(),
homeDir: normalizePath(app.getPath("home")),
downloadOptionTitle,
platform: os.platform(),
});
fs.readFile(bundleLocation, async (err, fileBuffer) => {
if (err) {
logger.error("Failed to read zip file", err);
throw err;
}
await axios.put(uploadUrl, fileBuffer, {
headers: {
"Content-Type": "application/tar",
},
onUploadProgress: (progressEvent) => {
console.log(progressEvent);
},
});
WindowManager.mainWindow?.webContents.send(
`on-upload-complete-${objectId}-${shop}`,
true
);
fs.rm(bundleLocation, (err) => {
if (err) {
logger.error("Failed to remove tar file", err);
throw err;
}
});
});
});
};
registerEvent("uploadSaveGame", uploadSaveGame);

View File

@@ -12,7 +12,7 @@ export interface SearchGamesArgs {
export const convertSteamGameToCatalogueEntry = (
game: SteamGame
): CatalogueEntry => ({
objectId: String(game.id),
objectID: String(game.id),
title: game.name,
shop: "steam" as GameShop,
cover: steamUrlBuilder.library(String(game.id)),

View File

@@ -24,8 +24,6 @@ import "./library/update-executable-path";
import "./library/verify-executable-path";
import "./library/remove-game";
import "./library/remove-game-from-library";
import "./library/select-game-wine-prefix";
import "./misc/open-checkout";
import "./misc/open-external";
import "./misc/show-open-dialog";
import "./torrenting/cancel-game-download";
@@ -50,8 +48,6 @@ import "./user/unblock-user";
import "./user/get-user-friends";
import "./user/get-user-stats";
import "./user/report-user";
import "./user/get-unlocked-achievements";
import "./user/get-compared-unlocked-achievements";
import "./profile/get-friend-requests";
import "./profile/get-me";
import "./profile/undo-friendship";
@@ -60,15 +56,8 @@ import "./profile/update-profile";
import "./profile/process-profile-image";
import "./profile/send-friend-request";
import "./profile/sync-friend-requests";
import "./cloud-save/download-game-artifact";
import "./cloud-save/get-game-artifacts";
import "./cloud-save/get-game-backup-preview";
import "./cloud-save/upload-save-game";
import "./cloud-save/delete-game-artifact";
import "./cloud-save/select-game-backup-path";
import "./notifications/publish-new-repacks-notification";
import { isPortableVersion } from "@main/helpers";
import "./misc/show-item-in-folder";
ipcMain.handle("ping", () => "pong");
ipcMain.handle("getVersion", () => appVersion);

View File

@@ -7,18 +7,17 @@ import type { GameShop } from "@types";
import { steamGamesWorker } from "@main/workers";
import { createGame } from "@main/services/library-sync";
import { steamUrlBuilder } from "@shared";
import { updateLocalUnlockedAchivements } from "@main/services/achievements/update-local-unlocked-achivements";
const addGameToLibrary = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
objectID: string,
title: string,
shop: GameShop
) => {
return gameRepository
.update(
{
objectID: objectId,
objectID,
},
{
shop,
@@ -28,27 +27,23 @@ const addGameToLibrary = async (
)
.then(async ({ affected }) => {
if (!affected) {
const steamGame = await steamGamesWorker.run(Number(objectId), {
const steamGame = await steamGamesWorker.run(Number(objectID), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
? steamUrlBuilder.icon(objectID, steamGame.clientIcon)
: null;
await gameRepository.insert({
title,
iconUrl,
objectID: objectId,
objectID,
shop,
});
}
const game = await gameRepository.findOne({
where: { objectID: objectId },
});
updateLocalUnlockedAchivements(game!);
const game = await gameRepository.findOne({ where: { objectID } });
createGame(game!).catch(() => {});
});

View File

@@ -24,7 +24,6 @@ const createGameShortcut = async (
const options = {
filePath,
name: removeSymbolsFromName(game.title),
outputPath: app.getPath("desktop"),
};
return createDesktopShortcut({

View File

@@ -2,15 +2,15 @@ import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event";
const getGameByObjectId = async (
const getGameByObjectID = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string
objectID: string
) =>
gameRepository.findOne({
where: {
objectID: objectId,
objectID,
isDeleted: false,
},
});
registerEvent("getGameByObjectId", getGameByObjectId);
registerEvent("getGameByObjectID", getGameByObjectID);

View File

@@ -50,8 +50,7 @@ const openGameInstaller = async (
}
if (fs.lstatSync(gamePath).isFile()) {
shell.showItemInFolder(gamePath);
return true;
return executeGameInstaller(gamePath);
}
const setupPath = path.join(gamePath, "setup.exe");

View File

@@ -1,13 +0,0 @@
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event";
const selectGameWinePrefix = async (
_event: Electron.IpcMainInvokeEvent,
id: number,
winePrefixPath: string
) => {
return gameRepository.update({ id }, { winePrefixPath });
};
registerEvent("selectGameWinePrefix", selectGameWinePrefix);

View File

@@ -1,26 +0,0 @@
import { shell } from "electron";
import { registerEvent } from "../register-event";
import { userAuthRepository } from "@main/repository";
import { HydraApi } from "@main/services";
const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
const userAuth = await userAuthRepository.findOne({ where: { id: 1 } });
if (!userAuth) {
return;
}
const paymentToken = await HydraApi.post("/auth/payment", {
refreshToken: userAuth.refreshToken,
}).then((response) => response.accessToken);
const params = new URLSearchParams({
token: paymentToken,
});
shell.openExternal(
`${import.meta.env.MAIN_VITE_CHECKOUT_URL}?${params.toString()}`
);
};
registerEvent("openCheckout", openCheckout);

View File

@@ -1,11 +0,0 @@
import { shell } from "electron";
import { registerEvent } from "../register-event";
const showItemInFolder = async (
_event: Electron.IpcMainInvokeEvent,
filePath: string
) => {
return shell.showItemInFolder(filePath);
};
registerEvent("showItemInFolder", showItemInFolder);

View File

@@ -1,6 +1,6 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import type { FriendRequest } from "@types";
import { FriendRequest } from "@types";
const getFriendRequests = async (
_event: Electron.IpcMainInvokeEvent

View File

@@ -1,11 +1,48 @@
import { registerEvent } from "../register-event";
import type { UserDetails } from "@types";
import { getUserData } from "@main/services/user/get-user-data";
import * as Sentry from "@sentry/electron/main";
import { HydraApi } from "@main/services";
import { ProfileVisibility, UserDetails } from "@types";
import { userAuthRepository } from "@main/repository";
import { UserNotLoggedInError } from "@shared";
const getMe = async (
_event: Electron.IpcMainInvokeEvent
): Promise<UserDetails | null> => {
return getUserData();
return HydraApi.get<UserDetails>(`/profile/me`)
.then(async (me) => {
userAuthRepository.upsert(
{
id: 1,
displayName: me.displayName,
profileImageUrl: me.profileImageUrl,
userId: me.id,
},
["id"]
);
Sentry.setUser({ id: me.id, username: me.username });
return me;
})
.catch(async (err) => {
if (err instanceof UserNotLoggedInError) {
return null;
}
const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } });
if (loggedUser) {
return {
...loggedUser,
id: loggedUser.userId,
username: "",
bio: "",
profileVisibility: "PUBLIC" as ProfileVisibility,
};
}
return null;
});
};
registerEvent("getMe", getMe);

View File

@@ -1,17 +1,9 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { UserNotLoggedInError } from "@shared";
import type { FriendRequestSync } from "@types";
import { FriendRequestSync } from "@types";
const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => {
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`).catch(
(err) => {
if (err instanceof UserNotLoggedInError) {
return { friendRequests: [] };
}
throw err;
}
);
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`);
};
registerEvent("syncFriendRequests", syncFriendRequests);

View File

@@ -1,6 +1,6 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import type { FriendRequestAction } from "@types";
import { FriendRequestAction } from "@types";
const updateFriendRequest = async (
_event: Electron.IpcMainInvokeEvent,

View File

@@ -1,75 +1,56 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { HydraApi, PythonInstance } from "@main/services";
import fs from "node:fs";
import path from "node:path";
import type { UpdateProfileRequest, UserProfile } from "@types";
import { omit } from "lodash-es";
import axios from "axios";
import { fileTypeFromFile } from "file-type";
interface PresignedResponse {
presignedUrl: string;
profileImageUrl: string;
}
const patchUserProfile = async (updateProfile: UpdateProfileRequest) => {
return HydraApi.patch<UserProfile>("/profile", updateProfile);
};
const uploadImage = async (
type: "profile-image" | "background-image",
imagePath: string
) => {
const stat = fs.statSync(imagePath);
const fileBuffer = fs.readFileSync(imagePath);
const fileSizeInBytes = stat.size;
const getNewProfileImageUrl = async (localImageUrl: string) => {
const { imagePath, mimeType } =
await PythonInstance.processProfileImage(localImageUrl);
const response = await HydraApi.post<{ presignedUrl: string }>(
`/presigned-urls/${type}`,
{
const stats = fs.statSync(imagePath);
const fileBuffer = fs.readFileSync(imagePath);
const fileSizeInBytes = stats.size;
const { presignedUrl, profileImageUrl } =
await HydraApi.post<PresignedResponse>(`/presigned-urls/profile-image`, {
imageExt: path.extname(imagePath).slice(1),
imageLength: fileSizeInBytes,
}
);
});
const mimeType = await fileTypeFromFile(imagePath);
await axios.put(response.presignedUrl, fileBuffer, {
await axios.put(presignedUrl, fileBuffer, {
headers: {
"Content-Type": mimeType?.mime,
"Content-Type": mimeType,
},
});
if (type === "background-image") {
return response["backgroundImageUrl"];
}
return response["profileImageUrl"];
return profileImageUrl;
};
const updateProfile = async (
_event: Electron.IpcMainInvokeEvent,
updateProfile: UpdateProfileRequest
) => {
const payload = omit(updateProfile, [
"profileImageUrl",
"backgroundImageUrl",
]);
if (updateProfile.profileImageUrl) {
const profileImageUrl = await uploadImage(
"profile-image",
updateProfile.profileImageUrl
).catch(() => undefined);
payload["profileImageUrl"] = profileImageUrl;
if (!updateProfile.profileImageUrl) {
return patchUserProfile(omit(updateProfile, "profileImageUrl"));
}
if (updateProfile.backgroundImageUrl) {
const backgroundImageUrl = await uploadImage(
"background-image",
updateProfile.backgroundImageUrl
).catch(() => undefined);
const profileImageUrl = await getNewProfileImageUrl(
updateProfile.profileImageUrl
).catch(() => undefined);
payload["backgroundImageUrl"] = backgroundImageUrl;
}
return patchUserProfile(payload);
return patchUserProfile({ ...updateProfile, profileImageUrl });
};
registerEvent("updateProfile", updateProfile);

View File

@@ -1,5 +1,5 @@
import { registerEvent } from "../register-event";
import parseTorrent from "parse-torrent";
import type { StartGameDownloadPayload } from "@types";
import { DownloadManager, HydraApi, logger } from "@main/services";
@@ -9,13 +9,12 @@ import { createGame } from "@main/services/library-sync";
import { steamUrlBuilder } from "@shared";
import { dataSource } from "@main/data-source";
import { DownloadQueue, Game } from "@main/entity";
import { HydraAnalytics } from "@main/services/hydra-analytics";
const startGameDownload = async (
_event: Electron.IpcMainInvokeEvent,
payload: StartGameDownloadPayload
) => {
const { objectId, title, shop, downloadPath, downloader, uri } = payload;
const { objectID, title, shop, downloadPath, downloader, uri } = payload;
return dataSource.transaction(async (transactionalEntityManager) => {
const gameRepository = transactionalEntityManager.getRepository(Game);
@@ -24,7 +23,7 @@ const startGameDownload = async (
const game = await gameRepository.findOne({
where: {
objectID: objectId,
objectID,
shop,
},
});
@@ -52,18 +51,18 @@ const startGameDownload = async (
}
);
} else {
const steamGame = await steamGamesWorker.run(Number(objectId), {
const steamGame = await steamGamesWorker.run(Number(objectID), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
? steamUrlBuilder.icon(objectID, steamGame.clientIcon)
: null;
await gameRepository.insert({
title,
iconUrl,
objectID: objectId,
objectID,
downloader,
shop,
status: "active",
@@ -74,7 +73,7 @@ const startGameDownload = async (
const updatedGame = await gameRepository.findOne({
where: {
objectID: objectId,
objectID,
},
});
@@ -91,17 +90,6 @@ const startGameDownload = async (
logger.error("Failed to create game download", err);
});
if (uri.startsWith("magnet:")) {
try {
const { infoHash } = await parseTorrent(payload.uri);
if (infoHash) {
HydraAnalytics.postDownload(infoHash).catch(() => {});
}
} catch (err) {
logger.error("Failed to parse torrent", err);
}
}
await DownloadManager.cancelDownload(updatedGame!.id);
await DownloadManager.startDownload(updatedGame!);

View File

@@ -16,16 +16,15 @@ const windowsStartupPath = path.join(
const autoLaunch = async (
_event: Electron.IpcMainInvokeEvent,
autoLaunchProps: { enabled: boolean; minimized: boolean }
enabled: boolean
) => {
if (!app.isPackaged) return;
const appLauncher = new AutoLaunch({
name: app.getName(),
isHidden: autoLaunchProps.minimized,
});
if (autoLaunchProps.enabled) {
if (enabled) {
appLauncher.enable().catch((err) => {
logger.error(err);
});

View File

@@ -1,19 +1,13 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { UserNotLoggedInError } from "@shared";
import type { UserBlocks } from "@types";
import { UserBlocks } from "@types";
export const getBlockedUsers = async (
_event: Electron.IpcMainInvokeEvent,
take: number,
skip: number
): Promise<UserBlocks> => {
return HydraApi.get(`/profile/blocks`, { take, skip }).catch((err) => {
if (err instanceof UserNotLoggedInError) {
return { blocks: [] };
}
throw err;
});
return HydraApi.get(`/profile/blocks`, { take, skip });
};
registerEvent("getBlockedUsers", getBlockedUsers);

View File

@@ -1,44 +0,0 @@
import type { ComparedAchievements, GameShop } from "@types";
import { registerEvent } from "../register-event";
import { userPreferencesRepository } from "@main/repository";
import { HydraApi } from "@main/services";
const getComparedUnlockedAchievements = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
userId: string
) => {
const userPreferences = await userPreferencesRepository.findOne({
where: { id: 1 },
});
return HydraApi.get<ComparedAchievements>(
`/users/${userId}/games/achievements/compare`,
{
shop,
objectId,
language: userPreferences?.language || "en",
}
).then((achievements) => {
const sortedAchievements = achievements.achievements.sort((a, b) => {
if (a.targetStat.unlocked && !b.targetStat.unlocked) return -1;
if (!a.targetStat.unlocked && b.targetStat.unlocked) return 1;
if (a.targetStat.unlocked && b.targetStat.unlocked) {
return b.targetStat.unlockTime! - a.targetStat.unlockTime!;
}
return Number(a.hidden) - Number(b.hidden);
});
return {
...achievements,
achievements: sortedAchievements,
} as ComparedAchievements;
});
};
registerEvent(
"getComparedUnlockedAchievements",
getComparedUnlockedAchievements
);

View File

@@ -1,73 +0,0 @@
import type { GameShop, UnlockedAchievement, UserAchievement } from "@types";
import { registerEvent } from "../register-event";
import { gameAchievementRepository } from "@main/repository";
import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data";
export const getUnlockedAchievements = async (
objectId: string,
shop: GameShop,
useCachedData: boolean
): Promise<UserAchievement[]> => {
const cachedAchievements = await gameAchievementRepository.findOne({
where: { objectId, shop },
});
const achievementsData = await getGameAchievementData(
objectId,
shop,
useCachedData
);
const unlockedAchievements = JSON.parse(
cachedAchievements?.unlockedAchievements || "[]"
) as UnlockedAchievement[];
return achievementsData
.map((achievementData) => {
const unlockedAchiementData = unlockedAchievements.find(
(localAchievement) => {
return (
localAchievement.name.toUpperCase() ==
achievementData.name.toUpperCase()
);
}
);
const icongray = achievementData.icongray.endsWith("/")
? achievementData.icon
: achievementData.icongray;
if (unlockedAchiementData) {
return {
...achievementData,
unlocked: true,
unlockTime: unlockedAchiementData.unlockTime,
};
}
return {
...achievementData,
unlocked: false,
unlockTime: null,
icongray: icongray,
} as UserAchievement;
})
.sort((a, b) => {
if (a.unlocked && !b.unlocked) return -1;
if (!a.unlocked && b.unlocked) return 1;
if (a.unlocked && b.unlocked) {
return b.unlockTime! - a.unlockTime!;
}
return Number(a.hidden) - Number(b.hidden);
});
};
const getUnlockedAchievementsEvent = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop
): Promise<UserAchievement[]> => {
return getUnlockedAchievements(objectId, shop, false);
};
registerEvent("getUnlockedAchievements", getUnlockedAchievementsEvent);

Some files were not shown because too many files have changed in this diff Show More