Compare commits

..

2 Commits

Author SHA1 Message Date
Chubby Granny Chaser
decacfa1d9 Merge branch 'main' into fix/using-python-http-server 2025-02-01 19:13:34 +00:00
Chubby Granny Chaser
954037b826 fix: using python http server 2025-01-18 01:58:42 +00:00
456 changed files with 9491 additions and 17151 deletions

View File

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

View File

@@ -1,9 +1,5 @@
name: Build name: Build
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: pull_request on: pull_request
jobs: jobs:
@@ -11,7 +7,6 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [windows-latest, ubuntu-latest] os: [windows-latest, ubuntu-latest]
fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -32,16 +27,6 @@ jobs:
with: with:
python-version: 3.9 python-version: 3.9
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
- name: Build Rust
run: cargo build --release
working-directory: ./rust_rpc
- name: Install dependencies - name: Install dependencies
run: pip install -r requirements.txt run: pip install -r requirements.txt
@@ -58,13 +43,12 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }} MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }} MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }} MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
- name: Build Windows - name: Build Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
@@ -73,15 +57,14 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }} MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }} MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }} MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
- name: Upload build - name: Test Upload build
env: env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }} S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
@@ -91,6 +74,7 @@ jobs:
BUILDS_URL: ${{ secrets.BUILDS_URL }} BUILDS_URL: ${{ secrets.BUILDS_URL }}
BUILD_WEBHOOK_URL: ${{ secrets.BUILD_WEBHOOK_URL }} BUILD_WEBHOOK_URL: ${{ secrets.BUILD_WEBHOOK_URL }}
GITHUB_ACTOR: ${{ github.actor }} GITHUB_ACTOR: ${{ github.actor }}
run: node scripts/upload-build.cjs run: node scripts/upload-build.cjs
- name: Create artifact - name: Create artifact

View File

@@ -1,9 +1,5 @@
name: Lint name: Lint
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: pull_request on: pull_request
jobs: jobs:

View File

@@ -1,9 +1,5 @@
name: Release name: Release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: on:
push: push:
branches: main branches: main
@@ -33,16 +29,6 @@ jobs:
with: with:
python-version: 3.9 python-version: 3.9
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
- name: Build Rust
run: cargo build --release
working-directory: ./rust_rpc
- name: Install dependencies - name: Install dependencies
run: pip install -r requirements.txt run: pip install -r requirements.txt
@@ -59,13 +45,13 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }} MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }} MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_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 }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
- name: Build Windows - name: Build Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
@@ -74,13 +60,13 @@ jobs:
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }} MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }} MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_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 }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }} MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
- name: Create artifact - name: Create artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4

View File

@@ -1,13 +0,0 @@
name: Trigger Landing Page Build
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Trigger Landing Page build
run: curl --location --request POST '${{ secrets.LP_TRIGGER_DEPLOY_URL }}'

6
.gitignore vendored
View File

@@ -9,12 +9,8 @@ out
.vite .vite
ludusavi/ ludusavi/
hydra-python-rpc/ hydra-python-rpc/
aria2/
.python-version .python-version
# Sentry Config File # Sentry Config File
.env.sentry-build-plugin .env.sentry-build-plugin
*storybook.log
target/

View File

@@ -25,8 +25,7 @@
[![cs](https://img.shields.io/badge/lang-cs-purple)](./docs/README.cs.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) [![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) [![nb](https://img.shields.io/badge/lang-nb-blue)](./docs/README.nb.md)
[![et](https://img.shields.io/badge/lang-et-blue.svg)](./docs/README.et.md) [![ee](https://img.shields.io/badge/lang-et-blue.svg)](./docs/README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](./docs/README.tr.md)
![Hydra Catalogue](./docs/screenshot.png) ![Hydra Catalogue](./docs/screenshot.png)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png) ![Hydra Catalogue](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Katalog](screenshot.png) ![Hydra Katalog](screenshot.png)

View File

@@ -25,7 +25,6 @@
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png) ![Hydra Catalogue](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Katalog](screenshot.png) ![Hydra Katalog](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png) ![Hydra Catalogue](screenshot.png)

View File

@@ -23,8 +23,7 @@
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.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) [![da](https://img.shields.io/badge/lang-da-red)](README.da.md)
[![nb](https://img.shields.io/badge/lang-nb-blue)](README.nb.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) [![ee](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Kataloog](screenshot.png) ![Hydra Kataloog](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Catalogue Hydra](screenshot.png) ![Catalogue Hydra](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png) ![Hydra Catalogue](./screenshot.png)

View File

@@ -25,7 +25,6 @@
[![cs](https://img.shields.io/badge/lang-cs-purple)](README.cs.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png) ![Hydra Catalogue](screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png) ![Hydra Catalogue](./screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png) ![Hydra Catalogue](./screenshot.png)

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](./screenshot.png) ![Hydra Catalogue](./screenshot.png)

View File

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

View File

@@ -26,7 +26,6 @@
[![da](https://img.shields.io/badge/lang-da-red)](README.da.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) [![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) [![et](https://img.shields.io/badge/lang-et-blue.svg)](README.et.md)
[![tr](https://img.shields.io/badge/lang-tr-red.svg)](README.tr.md)
![Hydra Catalogue](screenshot.png) ![Hydra Catalogue](screenshot.png)

View File

@@ -3,6 +3,7 @@ productName: Hydra
directories: directories:
buildResources: build buildResources: build
extraResources: extraResources:
- aria2
- ludusavi - ludusavi
- hydra-python-rpc - hydra-python-rpc
- seeds - seeds
@@ -19,10 +20,6 @@ asarUnpack:
- resources/** - resources/**
win: win:
executableName: Hydra executableName: Hydra
extraResources:
- from: binaries/7z.exe
- from: binaries/7z.dll
- from: rust_rpc/target/release/hydra-httpdl.exe
target: target:
- nsis - nsis
- portable - portable
@@ -38,9 +35,6 @@ portable:
artifactName: ${name}-${version}-portable.${ext} artifactName: ${name}-${version}-portable.${ext}
mac: mac:
entitlementsInherit: build/entitlements.mac.plist entitlementsInherit: build/entitlements.mac.plist
extraResources:
- from: binaries/7zz
- from: rust_rpc/target/release/hydra-httpdl
extendInfo: extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera. - NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
@@ -50,9 +44,6 @@ mac:
dmg: dmg:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-${version}.${ext}
linux: linux:
extraResources:
- from: binaries/7zzs
- from: rust_rpc/target/release/hydra-httpdl
target: target:
- AppImage - AppImage
- snap - snap

View File

@@ -6,6 +6,7 @@ import {
externalizeDepsPlugin, externalizeDepsPlugin,
} from "electron-vite"; } from "electron-vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import svgr from "vite-plugin-svgr"; import svgr from "vite-plugin-svgr";
import { sentryVitePlugin } from "@sentry/vite-plugin"; import { sentryVitePlugin } from "@sentry/vite-plugin";
@@ -16,6 +17,9 @@ export default defineConfig(({ mode }) => {
main: { main: {
build: { build: {
sourcemap: true, sourcemap: true,
rollupOptions: {
external: ["better-sqlite3"],
},
}, },
resolve: { resolve: {
alias: { alias: {
@@ -34,13 +38,6 @@ export default defineConfig(({ mode }) => {
build: { build: {
sourcemap: true, sourcemap: true,
}, },
css: {
preprocessorOptions: {
scss: {
api: "modern",
},
},
},
resolve: { resolve: {
alias: { alias: {
"@renderer": resolve("src/renderer/src"), "@renderer": resolve("src/renderer/src"),
@@ -51,6 +48,7 @@ export default defineConfig(({ mode }) => {
plugins: [ plugins: [
svgr(), svgr(),
react(), react(),
vanillaExtractPlugin(),
sentryVitePlugin({ sentryVitePlugin({
authToken: process.env.SENTRY_AUTH_TOKEN, authToken: process.env.SENTRY_AUTH_TOKEN,
org: "hydra-launcher", org: "hydra-launcher",

View File

@@ -1,6 +1,6 @@
{ {
"name": "hydralauncher", "name": "hydralauncher",
"version": "3.4.2", "version": "3.1.5",
"description": "Hydra", "description": "Hydra",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "Los Broxas", "author": "Los Broxas",
@@ -21,30 +21,32 @@
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false", "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:node && npm run typecheck:web", "typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview", "start": "electron-vite preview",
"dev": "cargo build --manifest-path=rust_rpc/Cargo.toml && electron-vite dev", "dev": "electron-vite dev",
"build": "npm run typecheck && electron-vite build", "build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps && node ./scripts/postinstall.cjs", "postinstall": "electron-builder install-app-deps && node ./scripts/postinstall.cjs",
"build:unpack": "npm run build && electron-builder --dir", "build:unpack": "npm run build && electron-builder --dir",
"build:win": "electron-vite build && electron-builder --win", "build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac", "build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux", "build:linux": "electron-vite build && electron-builder --linux",
"prepare": "husky" "prepare": "husky",
"knex:migrate:make": "knex --knexfile src/main/knexfile.ts migrate:make --esm"
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.1.0", "@fontsource/noto-sans": "^5.1.0",
"@hookform/resolvers": "^3.9.1", "@hookform/resolvers": "^3.9.1",
"@monaco-editor/react": "^4.6.0",
"@primer/octicons-react": "^19.9.0", "@primer/octicons-react": "^19.9.0",
"@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2",
"@reduxjs/toolkit": "^2.2.3", "@reduxjs/toolkit": "^2.2.3",
"@sentry/react": "^8.47.0", "@sentry/react": "^8.47.0",
"@sentry/vite-plugin": "^2.22.7", "@sentry/vite-plugin": "^2.22.7",
"@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/dynamic": "^2.1.2",
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6", "auto-launch": "^5.0.6",
"axios": "^1.7.9", "axios": "^1.7.9",
"axios-cookiejar-support": "^5.0.5", "better-sqlite3": "^11.7.0",
"classic-level": "^2.0.0",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"color": "^4.2.3", "color": "^4.2.3",
"color.js": "^1.2.0", "color.js": "^1.2.0",
@@ -53,13 +55,13 @@
"dexie": "^4.0.10", "dexie": "^4.0.10",
"diskusage": "^1.2.0", "diskusage": "^1.2.0",
"electron-log": "^5.2.4", "electron-log": "^5.2.4",
"electron-updater": "^6.6.2", "electron-updater": "^6.3.9",
"file-type": "^19.6.0", "file-type": "^19.6.0",
"i18next": "^23.11.2", "i18next": "^23.11.2",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"kill-port": "^2.0.1", "knex": "^3.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"parse-torrent": "^11.0.17", "parse-torrent": "^11.0.17",
"piscina": "^4.7.0", "piscina": "^4.7.0",
@@ -69,11 +71,10 @@
"react-loading-skeleton": "^3.4.0", "react-loading-skeleton": "^3.4.0",
"react-redux": "^9.1.1", "react-redux": "^9.1.1",
"react-router-dom": "^6.22.3", "react-router-dom": "^6.22.3",
"react-tooltip": "^5.28.0",
"sound-play": "^1.1.0", "sound-play": "^1.1.0",
"sudo-prompt": "^9.2.1", "sudo-prompt": "^9.2.1",
"tar": "^7.4.3", "tar": "^7.4.3",
"tough-cookie": "^5.1.1", "typeorm": "^0.3.20",
"user-agents": "^1.1.387", "user-agents": "^1.1.387",
"yaml": "^2.6.1", "yaml": "^2.6.1",
"yup": "^1.5.0", "yup": "^1.5.0",
@@ -89,8 +90,9 @@
"@swc/core": "^1.4.16", "@swc/core": "^1.4.16",
"@types/auto-launch": "^5.0.5", "@types/auto-launch": "^5.0.5",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/folder-hash": "^4.0.4",
"@types/jsdom": "^21.1.7", "@types/jsdom": "^21.1.7",
"@types/jsonwebtoken": "^9.0.8", "@types/jsonwebtoken": "^9.0.7",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7", "@types/node": "^20.12.7",
"@types/parse-torrent": "^5.8.7", "@types/parse-torrent": "^5.8.7",
@@ -98,13 +100,14 @@
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@types/sound-play": "^1.1.3", "@types/sound-play": "^1.1.3",
"@types/user-agents": "^1.0.4", "@types/user-agents": "^1.0.4",
"@vanilla-extract/vite-plugin": "^4.0.7",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"electron": "^31.7.7", "electron": "^31.7.6",
"electron-builder": "^26.0.12", "electron-builder": "^25.1.8",
"electron-vite": "^2.3.0", "electron-vite": "^2.0.0",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"prettier": "^3.4.2", "prettier": "^3.4.2",

View File

@@ -1,94 +1,47 @@
import os import aria2p
import subprocess
import json
class HttpDownloader: class HttpDownloader:
def __init__(self, hydra_httpdl_bin: str): def __init__(self):
self.hydra_exe = hydra_httpdl_bin self.download = None
self.process = None self.aria2 = aria2p.API(
self.last_status = None aria2p.Client(
host="http://localhost",
def start_download(self, url: str, save_path: str, header: str = None, allow_multiple_connections: bool = False, connections_limit: int = 1): port=6800,
cmd = [self.hydra_exe] secret=""
cmd.append(url)
cmd.extend([
"--chunk-size", "10",
"--buffer-size", "16",
"--force-download",
"--log",
"--silent"
])
if header:
cmd.extend(["--header", header])
if allow_multiple_connections:
cmd.extend(["--connections", str(connections_limit)])
else:
cmd.extend(["--connections", "1"])
print(f"running hydra-httpdl: {' '.join(cmd)}")
try:
self.process = subprocess.Popen(
cmd,
cwd=save_path,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
) )
except Exception as e: )
print(f"error running hydra-httpdl: {e}")
return None
def start_download(self, url: str, save_path: str, header: str):
if self.download:
self.aria2.resume([self.download])
else:
downloads = self.aria2.add(url, options={"header": header, "dir": save_path})
self.download = downloads[0]
def pause_download(self):
if self.download:
self.aria2.pause([self.download])
def cancel_download(self):
if self.download:
self.aria2.remove([self.download])
self.download = None
def get_download_status(self): def get_download_status(self):
if self.download == None:
if not self.process:
return None return None
try: download = self.aria2.get_download(self.download.gid)
line = self.process.stdout.readline()
if line: response = {
status = json.loads(line.strip()) 'folderName': download.name,
self.last_status = status 'fileSize': download.total_length,
elif self.last_status: 'progress': download.completed_length / download.total_length if download.total_length else 0,
status = self.last_status 'downloadSpeed': download.download_speed,
else: 'numPeers': 0,
return None 'numSeeds': 0,
'status': download.status,
response = { 'bytesDownloaded': download.completed_length,
"status": "active", }
"progress": status["progress"],
"downloadSpeed": status["speed_bps"], return response
"numPeers": 0,
"numSeeds": 0,
"bytesDownloaded": status["downloaded_bytes"],
"fileSize": status["total_bytes"],
"folderName": status["filename"]
}
if status["progress"] == 1:
response["status"] = "complete"
return response
except Exception as e:
print(f"error getting download status: {e}")
return None
def stop_download(self):
if self.process:
self.process.terminate()
self.process = None
self.last_status = None
def pause_download(self):
self.stop_download()
def cancel_download(self):
self.stop_download()

View File

@@ -1,42 +1,41 @@
from flask import Flask, request, jsonify from http.server import BaseHTTPRequestHandler, HTTPServer
import sys, json, urllib.parse, psutil import json
import urllib.parse
import sys
import psutil
from torrent_downloader import TorrentDownloader from torrent_downloader import TorrentDownloader
from http_downloader import HttpDownloader from http_downloader import HttpDownloader
from profile_image_processor import ProfileImageProcessor from profile_image_processor import ProfileImageProcessor
import libtorrent as lt import libtorrent as lt
app = Flask(__name__)
# Retrieve command line arguments # Retrieve command line arguments
torrent_port = sys.argv[1] torrent_port = sys.argv[1]
http_port = sys.argv[2] http_port = int(sys.argv[2])
rpc_password = sys.argv[3] rpc_password = sys.argv[3]
start_download_payload = sys.argv[4] start_download_payload = sys.argv[4]
start_seeding_payload = sys.argv[5] start_seeding_payload = sys.argv[5]
hydra_httpdl_bin = sys.argv[6]
downloads = {} downloads = {}
# This can be streamed down from Node
downloading_game_id = -1 downloading_game_id = -1
torrent_session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=torrent_port)}) torrent_session = lt.session({'listen_interfaces': f'0.0.0.0:{torrent_port}'})
if start_download_payload: if start_download_payload:
initial_download = json.loads(urllib.parse.unquote(start_download_payload)) initial_download = json.loads(urllib.parse.unquote(start_download_payload))
downloading_game_id = initial_download['game_id'] downloading_game_id = initial_download['game_id']
if initial_download['url'].startswith('magnet'): if initial_download['url'].startswith('magnet'):
torrent_downloader = TorrentDownloader(torrent_session) torrent_downloader = TorrentDownloader(torrent_session)
downloads[initial_download['game_id']] = torrent_downloader downloads[initial_download['game_id']] = torrent_downloader
try: try:
torrent_downloader.start_download(initial_download['url'], initial_download['save_path']) torrent_downloader.start_download(initial_download['url'], initial_download['save_path'], "")
except Exception as e: except Exception as e:
print("Error starting torrent download", e) print("Error starting torrent download", e)
else: else:
http_downloader = HttpDownloader(hydra_httpdl_bin) http_downloader = HttpDownloader()
downloads[initial_download['game_id']] = http_downloader downloads[initial_download['game_id']] = http_downloader
try: try:
http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get('allow_multiple_connections', False), initial_download.get('connections_limit', 24)) http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'))
except Exception as e: except Exception as e:
print("Error starting http download", e) print("Error starting http download", e)
@@ -46,141 +45,146 @@ if start_seeding_payload:
torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode) torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode)
downloads[seed['game_id']] = torrent_downloader downloads[seed['game_id']] = torrent_downloader
try: try:
torrent_downloader.start_download(seed['url'], seed['save_path']) torrent_downloader.start_download(seed['url'], seed['save_path'], "")
except Exception as e: except Exception as e:
print("Error starting seeding", e) print("Error starting seeding", e)
def validate_rpc_password(): class RequestHandler(BaseHTTPRequestHandler):
"""Middleware to validate RPC password.""" def validate_rpc_password(self):
header_password = request.headers.get('x-hydra-rpc-password') header_password = self.headers.get('x-hydra-rpc-password')
if header_password != rpc_password: if header_password != rpc_password:
return jsonify({"error": "Unauthorized"}), 401 self.send_response(401)
self.end_headers()
self.wfile.write(json.dumps({"error": "Unauthorized"}).encode('utf-8'))
return False
return True
@app.route("/status", methods=["GET"]) def do_GET(self):
def status(): if self.path == "/status":
auth_error = validate_rpc_password() if not self.validate_rpc_password():
if auth_error: return
return auth_error
downloader = downloads.get(downloading_game_id) downloader = downloads.get(downloading_game_id)
if downloader: if downloader:
status = downloads.get(downloading_game_id).get_download_status() status = downloader.get_download_status()
return jsonify(status), 200 self.send_response(200)
else: self.end_headers()
return jsonify(None) self.wfile.write(json.dumps(status).encode('utf-8'))
@app.route("/seed-status", methods=["GET"])
def seed_status():
auth_error = validate_rpc_password()
if auth_error:
return auth_error
seed_status = []
for game_id, downloader in downloads.items():
if not downloader:
continue
response = downloader.get_download_status()
if response is None:
continue
if response.get('status') == 5:
seed_status.append({
'gameId': game_id,
**response,
})
return jsonify(seed_status), 200
@app.route("/healthcheck", methods=["GET"])
def healthcheck():
return "ok", 200
@app.route("/process-list", methods=["GET"])
def process_list():
auth_error = validate_rpc_password()
if auth_error:
return auth_error
process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'name'])]
return jsonify(process_list), 200
@app.route("/profile-image", methods=["POST"])
def profile_image():
auth_error = validate_rpc_password()
if auth_error:
return auth_error
data = request.get_json()
image_path = data.get('image_path')
try:
processed_image_path, mime_type = ProfileImageProcessor.process_image(image_path)
return jsonify({'imagePath': processed_image_path, 'mimeType': mime_type}), 200
except Exception as e:
return jsonify({"error": str(e)}), 400
@app.route("/action", methods=["POST"])
def action():
global torrent_session
global downloading_game_id
auth_error = validate_rpc_password()
if auth_error:
return auth_error
data = request.get_json()
action = data.get('action')
game_id = data.get('game_id')
if action == 'start':
url = data.get('url')
existing_downloader = downloads.get(game_id)
if url.startswith('magnet'):
if existing_downloader and isinstance(existing_downloader, TorrentDownloader):
existing_downloader.start_download(url, data['save_path'])
else: else:
torrent_downloader = TorrentDownloader(torrent_session) self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(None).encode('utf-8'))
elif self.path == "/seed-status":
if not self.validate_rpc_password():
return
seed_status = []
for game_id, downloader in downloads.items():
if not downloader:
continue
response = downloader.get_download_status()
if response is None:
continue
if response.get('status') == 5:
seed_status.append({
'gameId': game_id,
**response,
})
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(seed_status).encode('utf-8'))
elif self.path == "/healthcheck":
self.send_response(200)
self.end_headers()
elif self.path == "/process-list":
if not self.validate_rpc_password():
return
process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'name'])]
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(process_list).encode('utf-8'))
def do_POST(self):
if not self.validate_rpc_password():
return
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
data = json.loads(post_data.decode('utf-8'))
if self.path == "/profile-image":
image_path = data.get('image_path')
try:
processed_image_path, mime_type = ProfileImageProcessor.process_image(image_path)
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({'imagePath': processed_image_path, 'mimeType': mime_type}).encode('utf-8'))
except Exception as e:
self.send_response(400)
self.end_headers()
self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
elif self.path == "/action":
global downloading_game_id
action = data.get('action')
game_id = data.get('game_id')
if action == 'start':
url = data.get('url')
existing_downloader = downloads.get(game_id)
if url.startswith('magnet'):
if existing_downloader and isinstance(existing_downloader, TorrentDownloader):
existing_downloader.start_download(url, data['save_path'], "")
else:
torrent_downloader = TorrentDownloader(torrent_session)
downloads[game_id] = torrent_downloader
torrent_downloader.start_download(url, data['save_path'], "")
else:
if existing_downloader and isinstance(existing_downloader, HttpDownloader):
existing_downloader.start_download(url, data['save_path'], data.get('header'))
else:
http_downloader = HttpDownloader()
downloads[game_id] = http_downloader
http_downloader.start_download(url, data['save_path'], data.get('header'))
downloading_game_id = game_id
elif action == 'pause':
downloader = downloads.get(game_id)
if downloader:
downloader.pause_download()
downloading_game_id = -1
elif action == 'cancel':
downloader = downloads.get(game_id)
if downloader:
downloader.cancel_download()
elif action == 'resume_seeding':
torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode)
downloads[game_id] = torrent_downloader downloads[game_id] = torrent_downloader
torrent_downloader.start_download(url, data['save_path']) torrent_downloader.start_download(data['url'], data['save_path'], "")
else: elif action == 'pause_seeding':
if existing_downloader and isinstance(existing_downloader, HttpDownloader): downloader = downloads.get(game_id)
existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('allow_multiple_connections', False), data.get('connections_limit', 24)) if downloader:
downloader.cancel_download()
else: else:
http_downloader = HttpDownloader(hydra_httpdl_bin) self.send_response(400)
downloads[game_id] = http_downloader self.end_headers()
http_downloader.start_download(url, data['save_path'], data.get('header'), data.get('allow_multiple_connections', False), data.get('connections_limit', 24)) self.wfile.write(json.dumps({"error": "Invalid action"}).encode('utf-8'))
return
downloading_game_id = game_id
elif action == 'pause': self.send_response(200)
downloader = downloads.get(game_id) self.end_headers()
if downloader:
downloader.pause_download()
if downloading_game_id == game_id:
downloading_game_id = -1
elif action == 'cancel':
downloader = downloads.get(game_id)
if downloader:
downloader.cancel_download()
elif action == 'resume_seeding':
torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode)
downloads[game_id] = torrent_downloader
torrent_downloader.start_download(data['url'], data['save_path'])
elif action == 'pause_seeding':
downloader = downloads.get(game_id)
if downloader:
downloader.cancel_download()
else:
return jsonify({"error": "Invalid action"}), 400
return "", 200
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(http_port)) server = HTTPServer(('0.0.0.0', http_port), RequestHandler)
print(f"Server running on port {http_port}")
server.serve_forever()

View File

@@ -102,7 +102,7 @@ class TorrentDownloader:
"http://bvarf.tracker.sh:2086/announce", "http://bvarf.tracker.sh:2086/announce",
] ]
def start_download(self, magnet: str, save_path: str): def start_download(self, magnet: str, save_path: str, header: str):
params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers, 'flags': self.flags} params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers, 'flags': self.flags}
self.torrent_handle = self.session.add_torrent(params) self.torrent_handle = self.session.add_torrent(params)
self.torrent_handle.resume() self.torrent_handle.resume()

View File

@@ -4,4 +4,4 @@ cx_Logging; sys_platform == 'win32'
pywin32; sys_platform == 'win32' pywin32; sys_platform == 'win32'
psutil psutil
Pillow Pillow
flask aria2p

2040
rust_rpc/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
[package]
name = "hydra-httpdl"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full", "macros", "rt-multi-thread"] }
reqwest = { version = "0.12.5", features = ["stream"] }
futures = "0.3"
bytes = "1.4"
indicatif = "0.17"
anyhow = "1.0"
async-trait = "0.1"
tokio-util = { version = "0.7", features = ["io"] }
clap = { version = "4.4", features = ["derive"] }
urlencoding = "2.1"
serde_json = "1.0"
bitvec = "1.0"
sha2 = "0.10"
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true

View File

@@ -1,966 +0,0 @@
use anyhow::Result;
use bitvec::prelude::*;
use clap::Parser;
use futures::stream::{FuturesUnordered, StreamExt};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::{Client, StatusCode, Url};
use serde_json::json;
use sha2::{Digest, Sha256};
use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::sync::Arc;
use tokio::sync::Mutex;
const DEFAULT_MAX_RETRIES: usize = 3;
const RETRY_BACKOFF_MS: u64 = 500;
const DEFAULT_OUTPUT_FILENAME: &str = "output.bin";
const DEFAULT_CONNECTIONS: usize = 16;
const DEFAULT_CHUNK_SIZE_MB: usize = 5;
const DEFAULT_BUFFER_SIZE_MB: usize = 8;
const DEFAULT_VERBOSE: bool = false;
const DEFAULT_SILENT: bool = false;
const DEFAULT_LOG: bool = false;
const DEFAULT_FORCE_NEW: bool = false;
const DEFAULT_RESUME_ONLY: bool = false;
const DEFAULT_FORCE_DOWNLOAD: bool = false;
const HEADER_SIZE: usize = 4096;
const MAGIC_NUMBER: &[u8; 5] = b"HYDRA";
const FORMAT_VERSION: u8 = 1;
const FINALIZE_BUFFER_SIZE: usize = 1024 * 1024;
#[derive(Parser)]
#[command(name = "hydra-httpdl")]
#[command(author = "los-broxas")]
#[command(version = "0.2.0")]
#[command(about = "high speed and low resource usage http downloader with resume capability", long_about = None)]
struct CliArgs {
/// file url to download
#[arg(required = true)]
url: String,
/// output file path (or directory to save with original filename)
#[arg(default_value = DEFAULT_OUTPUT_FILENAME)]
output: String,
/// number of concurrent connections for parallel download
#[arg(short = 'c', long, default_value_t = DEFAULT_CONNECTIONS)]
connections: usize,
/// chunk size in MB for each connection
#[arg(short = 'k', long, default_value_t = DEFAULT_CHUNK_SIZE_MB)]
chunk_size: usize,
/// buffer size in MB for file writing
#[arg(short, long, default_value_t = DEFAULT_BUFFER_SIZE_MB)]
buffer_size: usize,
/// show detailed progress information
#[arg(short = 'v', long, default_value_t = DEFAULT_VERBOSE)]
verbose: bool,
/// suppress progress bar
#[arg(short = 's', long, default_value_t = DEFAULT_SILENT)]
silent: bool,
/// log download statistics in JSON format every second
#[arg(short = 'l', long, default_value_t = DEFAULT_LOG)]
log: bool,
/// force new download, ignore existing partial files
#[arg(short = 'f', long, default_value_t = DEFAULT_FORCE_NEW)]
force_new: bool,
/// only resume existing download, exit if no partial file exists
#[arg(short = 'r', long, default_value_t = DEFAULT_RESUME_ONLY)]
resume_only: bool,
/// force download, ignore some verification checks
#[arg(short = 'F', long, default_value_t = DEFAULT_FORCE_DOWNLOAD)]
force_download: bool,
/// HTTP headers to send with request (format: "Key: Value")
#[arg(short = 'H', long)]
header: Vec<String>,
}
struct DownloadConfig {
url: String,
output_path: String,
num_connections: usize,
chunk_size: usize,
buffer_size: usize,
verbose: bool,
silent: bool,
log: bool,
force_new: bool,
resume_only: bool,
headers: Vec<String>,
force_download: bool,
}
impl DownloadConfig {
fn should_log(&self) -> bool {
self.verbose && !self.silent
}
fn should_log_stats(&self) -> bool {
self.log
}
}
struct DownloadStats {
progress_percent: f64,
bytes_downloaded: u64,
total_size: u64,
speed_bytes_per_sec: f64,
eta_seconds: u64,
elapsed_seconds: u64,
}
struct HydraHeader {
magic: [u8; 5], // "HYDRA" identifier
version: u8, // header version
file_size: u64, // file size
etag: [u8; 32], // etag hash
url_hash: [u8; 32], // url hash
chunk_size: u32, // chunk size
chunk_count: u32, // chunk count
chunks_bitmap: BitVec<u8>, // chunks bitmap
}
impl HydraHeader {
fn new(file_size: u64, etag: &str, url: &str, chunk_size: u32) -> Self {
let chunk_count = ((file_size as f64) / (chunk_size as f64)).ceil() as u32;
let chunks_bitmap = bitvec![u8, Lsb0; 0; chunk_count as usize];
let mut etag_hash = [0u8; 32];
let etag_digest = Sha256::digest(etag.as_bytes());
etag_hash.copy_from_slice(&etag_digest[..]);
let mut url_hash = [0u8; 32];
let url_digest = Sha256::digest(url.as_bytes());
url_hash.copy_from_slice(&url_digest[..]);
Self {
magic: *MAGIC_NUMBER,
version: FORMAT_VERSION,
file_size,
etag: etag_hash,
url_hash,
chunk_size,
chunk_count,
chunks_bitmap,
}
}
fn write_to_file<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&self.magic)?;
writer.write_all(&[self.version])?;
writer.write_all(&self.file_size.to_le_bytes())?;
writer.write_all(&self.etag)?;
writer.write_all(&self.url_hash)?;
writer.write_all(&self.chunk_size.to_le_bytes())?;
writer.write_all(&self.chunk_count.to_le_bytes())?;
let bitmap_bytes = self.chunks_bitmap.as_raw_slice();
writer.write_all(bitmap_bytes)?;
let header_size = 5 + 1 + 8 + 32 + 32 + 4 + 4 + bitmap_bytes.len();
let padding_size = HEADER_SIZE - header_size;
let padding = vec![0u8; padding_size];
writer.write_all(&padding)?;
Ok(())
}
fn read_from_file<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let mut magic = [0u8; 5];
reader.read_exact(&mut magic)?;
if magic != *MAGIC_NUMBER {
anyhow::bail!("Not a valid Hydra download file");
}
let mut version = [0u8; 1];
reader.read_exact(&mut version)?;
if version[0] != FORMAT_VERSION {
anyhow::bail!("Incompatible format version");
}
let mut file_size_bytes = [0u8; 8];
reader.read_exact(&mut file_size_bytes)?;
let file_size = u64::from_le_bytes(file_size_bytes);
let mut etag = [0u8; 32];
reader.read_exact(&mut etag)?;
let mut url_hash = [0u8; 32];
reader.read_exact(&mut url_hash)?;
let mut chunk_size_bytes = [0u8; 4];
reader.read_exact(&mut chunk_size_bytes)?;
let chunk_size = u32::from_le_bytes(chunk_size_bytes);
let mut chunk_count_bytes = [0u8; 4];
reader.read_exact(&mut chunk_count_bytes)?;
let chunk_count = u32::from_le_bytes(chunk_count_bytes);
let bitmap_bytes_len = (chunk_count as usize + 7) / 8;
let mut bitmap_bytes = vec![0u8; bitmap_bytes_len];
reader.read_exact(&mut bitmap_bytes)?;
let chunks_bitmap = BitVec::<u8, Lsb0>::from_vec(bitmap_bytes);
reader.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
Ok(Self {
magic,
version: version[0],
file_size,
etag,
url_hash,
chunk_size,
chunk_count,
chunks_bitmap,
})
}
fn set_chunk_complete(&mut self, chunk_index: usize) -> Result<()> {
if chunk_index >= self.chunk_count as usize {
anyhow::bail!("Chunk index out of bounds");
}
self.chunks_bitmap.set(chunk_index, true);
Ok(())
}
fn is_chunk_complete(&self, chunk_index: usize) -> bool {
if chunk_index >= self.chunk_count as usize {
return false;
}
self.chunks_bitmap[chunk_index]
}
fn get_incomplete_chunks(&self) -> Vec<(u64, u64)> {
let incomplete_count = self.chunk_count as usize - self.chunks_bitmap.count_ones();
let mut chunks = Vec::with_capacity(incomplete_count);
let chunk_size = self.chunk_size as u64;
for i in 0..self.chunk_count as usize {
if !self.is_chunk_complete(i) {
let start = i as u64 * chunk_size;
let end = std::cmp::min((i as u64 + 1) * chunk_size - 1, self.file_size - 1);
chunks.push((start, end));
}
}
chunks
}
fn is_download_complete(&self) -> bool {
self.chunks_bitmap.count_ones() == self.chunk_count as usize
}
}
struct ProgressTracker {
bar: Option<ProgressBar>,
}
impl ProgressTracker {
fn new(file_size: u64, silent: bool, enable_stats: bool) -> Result<Self> {
let bar = if !silent || enable_stats {
let pb = ProgressBar::new(file_size);
pb.set_style(
ProgressStyle::default_bar()
.template("[{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")?
);
if silent {
pb.set_draw_target(indicatif::ProgressDrawTarget::hidden());
}
Some(pb)
} else {
None
};
Ok(Self { bar })
}
fn increment(&self, amount: u64) {
if let Some(pb) = &self.bar {
pb.inc(amount);
}
}
fn finish(&self) {
if let Some(pb) = &self.bar {
pb.finish_with_message("Download complete");
}
}
fn get_stats(&self) -> Option<DownloadStats> {
if let Some(pb) = &self.bar {
let position = pb.position();
let total = pb.length().unwrap_or(1);
Some(DownloadStats {
progress_percent: position as f64 / total as f64,
bytes_downloaded: position,
total_size: total,
speed_bytes_per_sec: pb.per_sec(),
eta_seconds: pb.eta().as_secs(),
elapsed_seconds: pb.elapsed().as_secs(),
})
} else {
None
}
}
}
struct Downloader {
client: Client,
config: DownloadConfig,
}
impl Downloader {
async fn download(&self) -> Result<()> {
let (file_size, filename, etag) = self.get_file_info().await?;
let output_path = self.determine_output_path(filename);
if self.config.should_log() {
println!("Detected filename: {}", output_path);
}
let resume_manager = ResumeManager::try_from_file(
&output_path,
file_size,
&etag,
&self.config.url,
self.config.chunk_size as u32,
self.config.force_new,
self.config.resume_only,
)?;
let file = self.prepare_output_file(&output_path, file_size)?;
let progress = ProgressTracker::new(file_size, self.config.silent, self.config.log)?;
let chunks = if resume_manager.is_download_complete() {
if self.config.should_log() {
println!("File is already fully downloaded, finalizing...");
}
resume_manager.finalize_download()?;
return Ok(());
} else {
let completed_chunks = resume_manager.header.chunks_bitmap.count_ones() as u32;
let total_chunks = resume_manager.header.chunk_count;
if completed_chunks > 0 {
if self.config.should_log() {
let percent_done = (completed_chunks as f64 / total_chunks as f64) * 100.0;
println!("Resuming download: {:.1}% already downloaded", percent_done);
}
if let Some(pb) = &progress.bar {
let downloaded = file_size * completed_chunks as u64 / total_chunks as u64;
pb.inc(downloaded);
}
}
resume_manager.get_incomplete_chunks()
};
if self.config.should_log() {
println!(
"Downloading {} chunks of total {}",
chunks.len(),
resume_manager.header.chunk_count
);
}
self.process_chunks_with_resume(
chunks,
file,
file_size,
progress,
output_path.clone(),
resume_manager,
)
.await?;
Ok(())
}
fn determine_output_path(&self, filename: Option<String>) -> String {
if Path::new(&self.config.output_path)
.file_name()
.unwrap_or_default()
== DEFAULT_OUTPUT_FILENAME
&& filename.is_some()
{
filename.unwrap()
} else {
self.config.output_path.clone()
}
}
fn prepare_output_file(&self, path: &str, size: u64) -> Result<Arc<Mutex<BufWriter<File>>>> {
let file = if Path::new(path).exists() {
OpenOptions::new().read(true).write(true).open(path)?
} else {
let file = File::create(path)?;
file.set_len(HEADER_SIZE as u64 + size)?;
file
};
Ok(Arc::new(Mutex::new(BufWriter::with_capacity(
self.config.buffer_size,
file,
))))
}
async fn process_chunks_with_resume(
&self,
chunks: Vec<(u64, u64)>,
file: Arc<Mutex<BufWriter<File>>>,
_file_size: u64,
progress: ProgressTracker,
real_filename: String,
resume_manager: ResumeManager,
) -> Result<()> {
let mut tasks = FuturesUnordered::new();
let log_progress = if self.config.should_log_stats() {
let progress_clone = progress.bar.clone();
let filename = real_filename.clone();
let (log_cancel_tx, mut log_cancel_rx) = tokio::sync::oneshot::channel();
let log_task = tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
let tracker = ProgressTracker {
bar: progress_clone,
};
loop {
tokio::select! {
_ = interval.tick() => {
if let Some(stats) = tracker.get_stats() {
let json_output = json!({
"progress": stats.progress_percent,
"speed_bps": stats.speed_bytes_per_sec,
"downloaded_bytes": stats.bytes_downloaded,
"total_bytes": stats.total_size,
"eta_seconds": stats.eta_seconds,
"elapsed_seconds": stats.elapsed_seconds,
"filename": filename
});
println!("{}", json_output);
}
}
_ = &mut log_cancel_rx => {
break;
}
}
}
});
Some((log_task, log_cancel_tx))
} else {
None
};
let resume_manager = Arc::new(Mutex::new(resume_manager));
for (start, end) in chunks {
let client = self.client.clone();
let url = self.config.url.clone();
let file_clone = Arc::clone(&file);
let pb_clone = progress.bar.clone();
let manager_clone = Arc::clone(&resume_manager);
let headers = self.config.headers.clone();
let force_download = self.config.force_download;
let should_log = self.config.should_log();
let chunk_size = self.config.chunk_size as u64;
let chunk_index = (start / chunk_size) as usize;
tasks.push(tokio::spawn(async move {
let result = Self::download_chunk_with_retry(
client,
url,
start,
end,
file_clone,
pb_clone,
DEFAULT_MAX_RETRIES,
&headers,
force_download,
should_log,
)
.await;
if result.is_ok() {
let mut manager = manager_clone.lock().await;
manager.set_chunk_complete(chunk_index)?;
}
result
}));
if tasks.len() >= self.config.num_connections {
if let Some(result) = tasks.next().await {
result??;
}
}
}
while let Some(result) = tasks.next().await {
result??;
}
{
let mut writer = file.lock().await;
writer.flush()?;
}
progress.finish();
if let Some((log_handle, log_cancel_tx)) = log_progress {
if self.config.should_log_stats() {
let json_output = json!({
"progress": 1.0,
"speed_bps": 0.0,
"downloaded_bytes": _file_size,
"total_bytes": _file_size,
"eta_seconds": 0,
"elapsed_seconds": if let Some(pb) = &progress.bar { pb.elapsed().as_secs() } else { 0 },
"filename": real_filename
});
println!("{}", json_output);
}
let _ = log_cancel_tx.send(());
let _ = log_handle.await;
}
let manager = resume_manager.lock().await;
if manager.is_download_complete() {
if self.config.should_log() {
println!("Download complete, finalizing file...");
}
manager.finalize_download()?;
}
Ok(())
}
async fn download_chunk_with_retry(
client: Client,
url: String,
start: u64,
end: u64,
file: Arc<Mutex<BufWriter<File>>>,
progress_bar: Option<ProgressBar>,
max_retries: usize,
headers: &[String],
force_download: bool,
should_log: bool,
) -> Result<()> {
let mut retries = 0;
loop {
match Self::download_chunk(
client.clone(),
url.clone(),
start,
end,
file.clone(),
progress_bar.clone(),
headers,
force_download,
should_log,
)
.await
{
Ok(_) => return Ok(()),
Err(e) => {
retries += 1;
if retries >= max_retries {
return Err(e);
}
tokio::time::sleep(tokio::time::Duration::from_millis(
RETRY_BACKOFF_MS * (2_u64.pow(retries as u32 - 1)),
))
.await;
}
}
}
}
async fn download_chunk(
client: Client,
url: String,
start: u64,
end: u64,
file: Arc<Mutex<BufWriter<File>>>,
progress_bar: Option<ProgressBar>,
headers: &[String],
force_download: bool,
should_log: bool,
) -> Result<()> {
let mut req = client
.get(&url)
.header("Range", format!("bytes={}-{}", start, end));
for header in headers {
if let Some(idx) = header.find(':') {
let (name, value) = header.split_at(idx);
let value = value[1..].trim();
req = req.header(name.trim(), value);
}
}
let resp = req.send().await?;
if resp.status() != StatusCode::PARTIAL_CONTENT && resp.status() != StatusCode::OK {
if !force_download {
anyhow::bail!("Server does not support Range requests");
} else if should_log {
println!("Server does not support Range requests, ignoring...");
}
}
let mut stream = resp.bytes_stream();
let mut position = start;
let mut total_bytes = 0;
let expected_bytes = end - start + 1;
while let Some(chunk_result) = stream.next().await {
let chunk = chunk_result?;
let chunk_size = chunk.len() as u64;
total_bytes += chunk_size;
if total_bytes > expected_bytes {
let remaining = expected_bytes - (total_bytes - chunk_size);
let mut writer = file.lock().await;
writer.seek(SeekFrom::Start(HEADER_SIZE as u64 + position))?;
writer.write_all(&chunk[..remaining as usize])?;
let tracker = ProgressTracker {
bar: progress_bar.clone(),
};
tracker.increment(remaining);
break;
}
let mut writer = file.lock().await;
writer.seek(SeekFrom::Start(HEADER_SIZE as u64 + position))?;
writer.write_all(&chunk)?;
drop(writer);
position += chunk_size;
let tracker = ProgressTracker {
bar: progress_bar.clone(),
};
tracker.increment(chunk_size);
}
Ok(())
}
async fn get_file_info(&self) -> Result<(u64, Option<String>, String)> {
let mut req = self.client.head(&self.config.url);
for header in &self.config.headers {
if let Some(idx) = header.find(':') {
let (name, value) = header.split_at(idx);
let value = value[1..].trim();
req = req.header(name.trim(), value);
}
}
let resp = req.send().await?;
let accepts_ranges = resp
.headers()
.get("accept-ranges")
.and_then(|v| v.to_str().ok())
.map(|v| v.contains("bytes"))
.unwrap_or(false);
if !accepts_ranges {
let range_check = self
.client
.get(&self.config.url)
.header("Range", "bytes=0-0")
.send()
.await?;
if range_check.status() != StatusCode::PARTIAL_CONTENT {
if !self.config.force_download {
anyhow::bail!(
"Server does not support Range requests, cannot continue with parallel download"
);
} else if self.config.should_log() {
println!("Server does not support Range requests, ignoring...");
}
}
}
let file_size = if let Some(content_length) = resp.headers().get("content-length") {
content_length.to_str()?.parse()?
} else {
anyhow::bail!("Could not determine file size")
};
let etag = if let Some(etag_header) = resp.headers().get("etag") {
etag_header.to_str()?.to_string()
} else {
format!(
"no-etag-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
)
};
let filename = self.extract_filename_from_response(&resp);
Ok((file_size, filename, etag))
}
fn extract_filename_from_response(&self, resp: &reqwest::Response) -> Option<String> {
if let Some(disposition) = resp.headers().get("content-disposition") {
if let Ok(disposition_str) = disposition.to_str() {
if let Some(filename) = Self::parse_content_disposition(disposition_str) {
return Some(filename);
}
}
}
Self::extract_filename_from_url(&self.config.url)
}
fn parse_content_disposition(disposition: &str) -> Option<String> {
if let Some(idx) = disposition.find("filename=") {
let start = idx + 9;
let mut end = disposition.len();
if disposition.as_bytes().get(start) == Some(&b'"') {
let quoted_name = &disposition[start + 1..];
if let Some(quote_end) = quoted_name.find('"') {
return Some(quoted_name[..quote_end].to_string());
}
} else {
if let Some(semicolon) = disposition[start..].find(';') {
end = start + semicolon;
}
return Some(disposition[start..end].to_string());
}
}
None
}
fn extract_filename_from_url(url: &str) -> Option<String> {
if let Ok(parsed_url) = Url::parse(url) {
let path = parsed_url.path();
if let Some(path_filename) = Path::new(path).file_name() {
if let Some(filename_str) = path_filename.to_str() {
if !filename_str.is_empty() {
if let Ok(decoded) = urlencoding::decode(filename_str) {
return Some(decoded.to_string());
}
}
}
}
}
None
}
}
struct ResumeManager {
header: HydraHeader,
file_path: String,
}
impl ResumeManager {
fn try_from_file(
path: &str,
file_size: u64,
etag: &str,
url: &str,
chunk_size: u32,
force_new: bool,
resume_only: bool,
) -> Result<Self> {
if force_new {
if Path::new(path).exists() {
std::fs::remove_file(path)?;
}
return Self::create_new_file(path, file_size, etag, url, chunk_size);
}
if let Ok(file) = File::open(path) {
let mut reader = BufReader::new(file);
match HydraHeader::read_from_file(&mut reader) {
Ok(header) => {
let current_url_hash = Sha256::digest(url.as_bytes());
let url_matches = header.url_hash == current_url_hash.as_slice();
let size_matches = header.file_size == file_size;
if url_matches && size_matches {
return Ok(Self {
header,
file_path: path.to_string(),
});
}
if resume_only {
anyhow::bail!(
"Existing file is not compatible and resume_only option is active"
);
}
std::fs::remove_file(path)?;
}
Err(e) => {
if resume_only {
return Err(anyhow::anyhow!("Could not read file to resume: {}", e));
}
std::fs::remove_file(path)?;
}
}
} else if resume_only {
anyhow::bail!("File not found and resume_only option is active");
}
Self::create_new_file(path, file_size, etag, url, chunk_size)
}
fn create_new_file(
path: &str,
file_size: u64,
etag: &str,
url: &str,
chunk_size: u32,
) -> Result<Self> {
let header = HydraHeader::new(file_size, etag, url, chunk_size);
let file = File::create(path)?;
file.set_len(HEADER_SIZE as u64 + file_size)?;
let mut writer = BufWriter::new(file);
header.write_to_file(&mut writer)?;
writer.flush()?;
Ok(Self {
header,
file_path: path.to_string(),
})
}
fn get_incomplete_chunks(&self) -> Vec<(u64, u64)> {
self.header.get_incomplete_chunks()
}
fn set_chunk_complete(&mut self, chunk_index: usize) -> Result<()> {
self.header.set_chunk_complete(chunk_index)?;
let file = OpenOptions::new().write(true).open(&self.file_path)?;
let mut writer = BufWriter::new(file);
let bitmap_offset = 5 + 1 + 8 + 32 + 32 + 4 + 4;
writer.seek(SeekFrom::Start(bitmap_offset as u64))?;
let bitmap_bytes = self.header.chunks_bitmap.as_raw_slice();
writer.write_all(bitmap_bytes)?;
writer.flush()?;
Ok(())
}
fn is_download_complete(&self) -> bool {
self.header.is_download_complete()
}
fn finalize_download(&self) -> Result<()> {
if !self.is_download_complete() {
anyhow::bail!("Download is not complete");
}
let temp_path = format!("{}.tmp", self.file_path);
let source = File::open(&self.file_path)?;
let dest = File::create(&temp_path)?;
let mut reader = BufReader::with_capacity(FINALIZE_BUFFER_SIZE, source);
let mut writer = BufWriter::with_capacity(FINALIZE_BUFFER_SIZE, dest);
reader.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
std::io::copy(&mut reader, &mut writer)?;
writer.flush()?;
drop(writer);
match std::fs::rename(&temp_path, &self.file_path) {
Ok(_) => Ok(()),
Err(_) => {
let _ = std::fs::remove_file(&self.file_path);
std::fs::rename(&temp_path, &self.file_path)?;
Ok(())
}
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let args = CliArgs::parse();
let config = DownloadConfig {
url: args.url.clone(),
output_path: args.output,
num_connections: args.connections,
chunk_size: args.chunk_size * 1024 * 1024,
buffer_size: args.buffer_size * 1024 * 1024,
verbose: args.verbose,
silent: args.silent,
log: args.log,
force_new: args.force_new,
resume_only: args.resume_only,
headers: args.header,
force_download: args.force_download,
};
if config.force_new && config.resume_only {
eprintln!("Error: --force-new and --resume-only options cannot be used together");
std::process::exit(1);
}
let downloader = Downloader {
client: Client::new(),
config,
};
if downloader.config.should_log() {
println!(
"Starting download with {} connections, chunk size: {}MB, buffer: {}MB",
downloader.config.num_connections, args.chunk_size, args.buffer_size
);
println!("URL: {}", args.url);
if downloader.config.force_new {
println!("Forcing new download, ignoring existing files");
} else if downloader.config.resume_only {
println!("Only resuming existing download");
} else {
println!("Resuming download if possible");
}
}
downloader.download().await?;
Ok(())
}

View File

@@ -2,6 +2,7 @@ const { default: axios } = require("axios");
const util = require("node:util"); const util = require("node:util");
const fs = require("node:fs"); const fs = require("node:fs");
const path = require("node:path"); const path = require("node:path");
const { spawnSync } = require("node:child_process");
const exec = util.promisify(require("node:child_process").exec); const exec = util.promisify(require("node:child_process").exec);
@@ -46,11 +47,76 @@ const downloadLudusavi = async () => {
}); });
}; };
const downloadAria2WindowsAndLinux = async () => {
const file =
process.platform === "win32"
? "aria2-1.37.0-win-64bit-build1.zip"
: "aria2-1.37.0-1-x86_64.pkg.tar.zst";
const downloadUrl =
process.platform === "win32"
? `https://github.com/aria2/aria2/releases/download/release-1.37.0/${file}`
: "https://archlinux.org/packages/extra/x86_64/aria2/download/";
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...`);
if (process.platform === "win32") {
await exec(`npx extract-zip ${file}`);
console.log("Extracted. Renaming folder...");
fs.mkdirSync("aria2");
fs.copyFileSync(
path.join(file.replace(".zip", ""), "aria2c.exe"),
"aria2/aria2c.exe"
);
fs.rmSync(file.replace(".zip", ""), { recursive: true });
} else {
await exec(`tar --zstd -xvf ${file} usr/bin/aria2c`);
console.log("Extracted. Copying binary file...");
fs.mkdirSync("aria2");
fs.copyFileSync("usr/bin/aria2c", "aria2/aria2c");
fs.rmSync("usr", { recursive: true });
}
console.log(`Extracted ${file}, removing compressed downloaded file...`);
fs.rmSync(file);
});
};
const copyAria2Macos = async () => {
console.log("Checking if aria2 is installed...");
const isAria2Installed = spawnSync("which", ["aria2c"]).status;
if (isAria2Installed != 0) {
console.log("Please install aria2");
console.log("brew install aria2");
return;
}
console.log("Copying aria2 binary...");
fs.mkdirSync("aria2");
await exec(`cp $(which aria2c) aria2/aria2c`);
};
const copyAria2 = () => {
if (fs.existsSync("aria2")) {
console.log("Aria2 already exists, skipping download...");
return;
}
if (process.platform == "darwin") {
copyAria2Macos();
} else {
downloadAria2WindowsAndLinux();
}
};
copyAria2();
downloadLudusavi(); downloadLudusavi();
if (process.platform !== "win32") {
const binariesPath = path.join(__dirname, "..", "binaries");
fs.chmodSync(path.join(binariesPath, "7zz"), 0o755);
fs.chmodSync(path.join(binariesPath, "7zzs"), 0o755);
}

File diff suppressed because one or more lines are too long

View File

@@ -7,18 +7,18 @@
"featured": "مميز", "featured": "مميز",
"surprise_me": "مفاجئني", "surprise_me": "مفاجئني",
"no_results": "لم يتم العثور على نتائج", "no_results": "لم يتم العثور على نتائج",
"start_typing": "ابدأ بالكتابة للبحث...", "start_typing": "ابدأ الكتابة للبحث...",
"hot": "الأكثر شهرة الآن", "hot": "الأكثر شيوعًا الآن",
"weekly": "📅 أفضل ألعاب الأسبوع", "weekly": "📅 أفضل ألعاب الأسبوع",
"achievements": "🏆 ألعاب يجب إكمالها" "achievements": "🏆 ألعاب للتغلب عليها"
}, },
"sidebar": { "sidebar": {
"catalogue": "الفهرس", "catalogue": "الكـتالوج",
"downloads": "التنزيلات", "downloads": "التنزيلات",
"settings": "الإعدادات", "settings": "الإعدادات",
"my_library": "مكتبتي", "my_library": "مكتبتي",
"downloading_metadata": "{{title}} (جاري تنزيل البيانات الوصفية...)", "downloading_metadata": "{{title}} (جارٍ تنزيل البيانات الوصفية...)",
"paused": "{{title}} (معلق)", "paused": "{{title}} (معلّق)",
"downloading": "{{title}} ({{percentage}} - جاري التنزيل...)", "downloading": "{{title}} ({{percentage}} - جاري التنزيل...)",
"filter": "تصفية المكتبة", "filter": "تصفية المكتبة",
"home": "الرئيسية", "home": "الرئيسية",
@@ -26,13 +26,12 @@
"game_has_no_executable": "اللعبة لا تحتوي على ملف تشغيل", "game_has_no_executable": "اللعبة لا تحتوي على ملف تشغيل",
"sign_in": "تسجيل الدخول", "sign_in": "تسجيل الدخول",
"friends": "الأصدقاء", "friends": "الأصدقاء",
"need_help": "تحتاج مساعدة؟", "need_help": "تحتاج مساعدة؟"
"favorites": "المفضلة"
}, },
"header": { "header": {
"search": "بحث الألعاب", "search": "ابحث عن الألعاب",
"home": "الرئيسية", "home": "الرئيسية",
"catalogue": "الفهرس", "catalogue": "الكـتالوج",
"downloads": "التنزيلات", "downloads": "التنزيلات",
"search_results": "نتائج البحث", "search_results": "نتائج البحث",
"settings": "الإعدادات", "settings": "الإعدادات",
@@ -41,16 +40,16 @@
}, },
"bottom_panel": { "bottom_panel": {
"no_downloads_in_progress": "لا توجد تنزيلات قيد التقدم", "no_downloads_in_progress": "لا توجد تنزيلات قيد التقدم",
"downloading_metadata": "جاري تنزيل بيانات {{title}} الوصفية...", "downloading_metadata": "جارٍ تنزيل البيانات الوصفية لـ {{title}}...",
"downloading": "جاري تنزيل {{title}}... ({{percentage}} مكتمل) - الوقت المتبقي {{eta}} - السرعة {{speed}}", "downloading": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - الوقت المتبقي {{eta}} - السرعة {{speed}}",
"calculating_eta": "جاري تنزيل {{title}}... ({{percentage}} مكتمل) - جاري حساب الوقت المتبقي...", "calculating_eta": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - جاري حساب الوقت المتبقي...",
"checking_files": "جاري فحص ملفات {{title}}... ({{percentage}} مكتمل)" "checking_files": "جارٍ فحص ملفات {{title}}... ({{percentage}} اكتمال)"
}, },
"catalogue": { "catalogue": {
"search": "تصفية...", "search": "تصفية...",
"developers": "المطورون", "developers": "المطورون",
"genres": "الأنواع", "genres": "الأنواع",
"tags": "الوسوم", "tags": "العلامات",
"publishers": "الناشرون", "publishers": "الناشرون",
"download_sources": "مصادر التنزيل", "download_sources": "مصادر التنزيل",
"result_count": "{{resultCount}} نتيجة", "result_count": "{{resultCount}} نتيجة",
@@ -69,34 +68,34 @@
"cancel": "إلغاء", "cancel": "إلغاء",
"remove": "إزالة", "remove": "إزالة",
"space_left_on_disk": "{{space}} متبقي على القرص", "space_left_on_disk": "{{space}} متبقي على القرص",
"eta": "الانتهاء المتوقع {{eta}}", "eta": "الانتهاء {{eta}}",
"calculating_eta": "جاري حساب الوقت المتبقي...", "calculating_eta": "جارٍ حساب الوقت المتبقي...",
"downloading_metadata": "جاري تنزيل البيانات الوصفية...", "downloading_metadata": "جارٍ تنزيل البيانات الوصفية...",
"filter": "تصفية الإصدارات المعادة", "filter": "تصفية الحزم المعاد تعبئتها",
"requirements": "متطلبات النظام", "requirements": "متطلبات النظام",
"minimum": "الحد الأدنى", "minimum": "الحد الأدنى",
"recommended": ستحسن", "recommended": ُوصى به",
"paused": "معلق", "paused": "معلّق",
"release_date": "تاريخ الإصدار {{date}}", "release_date": "تاريخ الإصدار {{date}}",
"publisher": "نشر بواسطة {{publisher}}", "publisher": "نشر بواسطة {{publisher}}",
"hours": "ساعات", "hours": "ساعات",
"minutes": "دقائق", "minutes": "دقائق",
"amount_hours": "{{amount}} ساعة", "amount_hours": "{{amount}} ساعات",
"amount_minutes": "{{amount}} دقيقة", "amount_minutes": "{{amount}} دقائق",
"accuracy": "دقة {{accuracy}}%", "accuracy": "دقة {{accuracy}}%",
"add_to_library": "إضافة إلى المكتبة", "add_to_library": "إضافة إلى المكتبة",
"remove_from_library": "إزالة من المكتبة", "remove_from_library": "إزالة من المكتبة",
"no_downloads": "لا توجد تنزيلات متاحة", "no_downloads": "لا توجد تنزيلات متاحة",
"play_time": "وقت اللعب {{amount}}", "play_time": "لعب لمدة {{amount}}",
"last_time_played": "آخر مرة لعب {{period}}", "last_time_played": "آخر تشغيل {{period}}",
"not_played_yet": "لم تلعب {{title}} بعد", "not_played_yet": "لم تلعب {{title}} بعد",
"next_suggestion": "الاقتراح التالي", "next_suggestion": "الاقتراح التالي",
"play": "تشغيل", "play": "تشغيل",
"deleting": "جاري حذف المثبت...", "deleting": "جارٍ حذف المثبت...",
"close": "إغلاق", "close": "إغلاق",
"playing_now": "جاري التشغيل الآن", "playing_now": تم التشغيل الآن",
"change": "تغيير", "change": "تغيير",
"repacks_modal_description": "اختر الإصدار المعاد الذي تريد تنزيله", "repacks_modal_description": "اختر الحزمة المعاد تعبئتها التي تريد تنزيلها",
"select_folder_hint": "لتغيير المجلد الافتراضي، انتقل إلى <0>الإعدادات</0>", "select_folder_hint": "لتغيير المجلد الافتراضي، انتقل إلى <0>الإعدادات</0>",
"download_now": "تنزيل الآن", "download_now": "تنزيل الآن",
"no_shop_details": "تعذر الحصول على تفاصيل المتجر.", "no_shop_details": "تعذر الحصول على تفاصيل المتجر.",
@@ -111,12 +110,12 @@
"select_executable": "تحديد", "select_executable": "تحديد",
"no_executable_selected": "لم يتم تحديد ملف تشغيل", "no_executable_selected": "لم يتم تحديد ملف تشغيل",
"open_folder": "فتح المجلد", "open_folder": "فتح المجلد",
"open_download_location": "عرض الملفات المنزلة", "open_download_location": "عرض الملفات المحملة",
"create_shortcut": "إنشاء اختصار على سطح المكتب", "create_shortcut": "إنشاء اختصار على سطح المكتب",
"clear": "مسح", "clear": "مسح",
"remove_files": "إزالة الملفات", "remove_files": "إزالة الملفات",
"remove_from_library_title": "هل أنت متأكد؟", "remove_from_library_title": "هل أنت متأكد؟",
"remove_from_library_description": "سيتم إزالة {{game}} من مكتبتك", "remove_from_library_description": "سيؤدي هذا إلى إزالة {{game}} من مكتبتك",
"options": "خيارات", "options": "خيارات",
"executable_section_title": "ملف التشغيل", "executable_section_title": "ملف التشغيل",
"executable_section_description": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"", "executable_section_description": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"",
@@ -124,35 +123,35 @@
"downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى لهذه اللعبة", "downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى لهذه اللعبة",
"danger_zone_section_title": "منطقة الخطر", "danger_zone_section_title": "منطقة الخطر",
"danger_zone_section_description": "إزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra", "danger_zone_section_description": "إزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra",
"download_in_progress": "جاري التنزيل", "download_in_progress": "تنزيل قيد التقدم",
"download_paused": "التنزيل معلق", "download_paused": "التنزيل معلق",
"last_downloaded_option": "خيار التنزيل الأخير", "last_downloaded_option": "خيار التنزيل الأخير",
"create_shortcut_success": "تم إنشاء الاختصار بنجاح", "create_shortcut_success": "تم إنشاء الاختصار بنجاح",
"create_shortcut_error": "خطأ في إنشاء الاختصار", "create_shortcut_error": "خطأ في إنشاء الاختصار",
"nsfw_content_title": "هذه اللعبة تحتوي على محتوى غير لائق", "nsfw_content_title": "هذه اللعبة تحتوي على محتوى غير لائق",
"nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يكون مناسبًا لجميع الأعمار. هل تريد المتابعة؟", "nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يناسب جميع الأعمار. هل تريد المتابعة؟",
"allow_nsfw_content": "متابعة", "allow_nsfw_content": "متابعة",
"refuse_nsfw_content": "رجوع", "refuse_nsfw_content": "رجوع",
"stats": "الإحصائيات", "stats": "الإحصائيات",
"download_count": "التنزيلات", "download_count": "مرات التنزيل",
"player_count": "اللاعبون النشطون", "player_count": "اللاعبون النشطون",
"download_error": "خيار التنزيل هذا غير متاح", "download_error": "خيار التنزيل هذا غير متاح",
"download": "تنزيل", "download": "تنزيل",
"executable_path_in_use": "مسار التشغيل مستخدم بالفعل بواسطة \"{{game}}\"", "executable_path_in_use": "مسار التشغيل مستخدم بالفعل بواسطة \"{{game}}\"",
"warning": "تحذير:", "warning": "تحذير:",
"hydra_needs_to_remain_open": "لهذا التنزيل، يجب أن يظل Hydra مفتوحًا حتى اكتماله. إذا تم إغلاق Hydra قبل الاكتمال، ستفقد تقدمك.", "hydra_needs_to_remain_open": "لهذا التنزيل، يجب أن يبقى Hydra مفتوحًا حتى اكتماله. إذا أغلق Hydra قبل الاكتمال، ستفقد تقدمك.",
"achievements": "الإنجازات", "achievements": "الإنجازات",
"achievements_count": "الإنجازات {{unlockedCount}}/{{achievementsCount}}", "achievements_count": "الإنجازات {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "حفظ سحابي", "cloud_save": "حفظ سحابي",
"cloud_save_description": "احفظ تقدمك في السحابة واستمر في اللعب من أي جهاز", "cloud_save_description": "احفظ تقدمك على السحابة واستمر في اللعب من أي جهاز",
"backups": "النسخ الاحتياطية", "backups": "النسخ الاحتياطية",
"install_backup": "تثبيت", "install_backup": "تثبيت",
"delete_backup": "حذف", "delete_backup": "حذف",
"create_backup": "نسخة احتياطية جديدة", "create_backup": "نسخة احتياطية جديدة",
"last_backup_date": "آخر نسخة احتياطية في {{date}}", "last_backup_date": "آخر نسخة احتياطية في {{date}}",
"no_backup_preview": "لم يتم العثور على حفظات لهذا العنوان", "no_backup_preview": "لم يتم العثور على حفظات لهذا العنوان",
"restoring_backup": "جاري استعادة النسخة الاحتياطية ({{progress}} مكتمل)...", "restoring_backup": "جارٍ استعادة النسخة الاحتياطية ({{progress}} اكتمال)...",
"uploading_backup": "جاري رفع النسخة الاحتياطية...", "uploading_backup": "جارٍ رفع النسخة الاحتياطية...",
"no_backups": "لم تقم بإنشاء أي نسخ احتياطية لهذه اللعبة بعد", "no_backups": "لم تقم بإنشاء أي نسخ احتياطية لهذه اللعبة بعد",
"backup_uploaded": "تم رفع النسخة الاحتياطية", "backup_uploaded": "تم رفع النسخة الاحتياطية",
"backup_deleted": "تم حذف النسخة الاحتياطية", "backup_deleted": "تم حذف النسخة الاحتياطية",
@@ -165,67 +164,61 @@
"files_automatically_mapped": "تم تعيين الملفات تلقائيًا", "files_automatically_mapped": "تم تعيين الملفات تلقائيًا",
"no_backups_created": "لم يتم إنشاء نسخ احتياطية لهذه اللعبة", "no_backups_created": "لم يتم إنشاء نسخ احتياطية لهذه اللعبة",
"manage_files": "إدارة الملفات", "manage_files": "إدارة الملفات",
"loading_save_preview": "جاري البحث عن حفظات اللعبة...", "loading_save_preview": "جارٍ البحث عن حفظات الألعاب...",
"wine_prefix": "بادئة Wine", "wine_prefix": "بادئة Wine",
"wine_prefix_description": "بادئة Wine المستخدمة لتشغيل هذه اللعبة", "wine_prefix_description": "بادئة Wine المستخدمة لتشغيل هذه اللعبة",
"launch_options": "خيارات التشغيل", "launch_options": "خيارات التشغيل",
"launch_options_description": "يمكن للمستخدمين المتقدمين إدخال تعديلات على خيارات التشغيل (ميزة تجريبية)", "launch_options_description": "يمكن للمستخدمين المتقدمين إدخال تعديلات على خيارات التشغيل (ميزة تجريبية)",
"launch_options_placeholder": ا توجد معلمات محددة", "launch_options_placeholder": م يتم تحديد أي معاملات",
"no_download_option_info": "لا توجد معلومات متاحة", "no_download_option_info": "لا توجد معلومات متاحة",
"backup_deletion_failed": "فشل في حذف النسخة الاحتياطية", "backup_deletion_failed": "فشل حذف النسخة الاحتياطية",
"max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى من النسخ الاحتياطية لهذه اللعبة", "max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى لعدد النسخ الاحتياطية لهذه اللعبة",
"achievements_not_sync": "شاهد كيفية مزامنة إنجازاتك", "achievements_not_sync": "تعرف على كيفية مزامنة إنجازاتك",
"manage_files_description": "إدارة الملفات التي سيتم نسخها احتياطيًا واستعادتها", "manage_files_description": "إدارة الملفات التي سيتم نسخها احتياطيًا واستعادتها",
"select_folder": "حدد المجلد", "select_folder": "حدد المجلد",
"backup_from": "نسخة احتياطية من {{date}}", "backup_from": "نسخة احتياطية من {{date}}",
"custom_backup_location_set": "تم تعيين موقع نسخ احتياطي مخصص", "custom_backup_location_set": "تم تعيين موقع نسخ احتياطي مخصص",
"no_directory_selected": "لم يتم تحديد مجلد", "no_directory_selected": "لم يتم تحديد مجلد",
"no_write_permission": "لا يمكن التنزيل إلى هذا المجلد. انقر هنا للمزيد من المعلومات.", "no_write_permission": "لا يمكن التنزيل إلى هذا المجلد. انقر هنا لمعرفة المزيد.",
"reset_achievements": "إعادة تعيين الإنجازات", "reset_achievements": "إعادة تعيين الإنجازات",
"reset_achievements_description": "سيؤدي هذا إلى إعادة تعيين جميع إنجازات {{game}}", "reset_achievements_description": "سيؤدي هذا إلى إعادة تعيين جميع إنجازات {{game}}",
"reset_achievements_title": "هل أنت متأكد؟", "reset_achievements_title": "هل أنت متأكد؟",
"reset_achievements_success": "تم إعادة تعيين الإنجازات بنجاح", "reset_achievements_success": "تم إعادة تعيين الإنجازات بنجاح",
"reset_achievements_error": "فشل في إعادة تعيين الإنجازات", "reset_achievements_error": "فشل إعادة تعيين الإنجازات"
"download_error_gofile_quota_exceeded": "لقد تجاوزت الحصة الشهرية لـ Gofile. يرجى الانتظار حتى إعادة تعيين الحصة.",
"download_error_real_debrid_account_not_authorized": "حساب Real-Debrid الخاص بك غير مصرح له بإجراء تنزيلات جديدة. يرجى مراجعة إعدادات الحساب والمحاولة مرة أخرى.",
"download_error_not_cached_in_real_debrid": "هذا التنزيل غير متوفر على Real-Debrid وجلب حالة التنزيل من Real-Debrid غير متاح حاليًا.",
"download_error_not_cached_in_torbox": "هذا التنزيل غير متوفر على Torbox وجلب حالة التنزيل من Torbox غير متاح حاليًا.",
"game_removed_from_favorites": "تمت إزالة اللعبة من المفضلة",
"game_added_to_favorites": "تمت إضافة اللعبة إلى المفضلة"
}, },
"activation": { "activation": {
"title": "تفعيل Hydra", "title": "تفعيل Hydra",
"installation_id": "معرف التثبيت:", "installation_id": "معرف التثبيت:",
"enter_activation_code": "أدخل رمز التفعيل الخاص بك", "enter_activation_code": "أدخل رمز التفعيل الخاص بك",
"message": "إذا كنت لا تعرف أين تطلب هذا، فأنت لا يجب أن يكون لديك هذا.", "message": "إذا كنت لا تعرف أين تطلب هذا، فلا يجب أن يكون لديك هذا.",
"activate": "تفعيل", "activate": "تفعيل",
"loading": "جاري التحميل..." "loading": "جارٍ التحميل..."
}, },
"downloads": { "downloads": {
"resume": "استئناف", "resume": "استئناف",
"pause": "إيقاف مؤقت", "pause": "إيقاف مؤقت",
"eta": "الانتهاء المتوقع {{eta}}", "eta": "الانتهاء {{eta}}",
"paused": "معلق", "paused": "معلّق",
"verifying": "جاري التحقق...", "verifying": "جارٍ التحقق...",
"completed": "مكتمل", "completed": "مكتمل",
"removed": "غير منزّل", "removed": "غير محمل",
"cancel": "إلغاء", "cancel": "إلغاء",
"filter": "تصفية الألعاب المنزلة", "filter": "تصفية الألعاب المحملة",
"remove": "إزالة", "remove": "إزالة",
"downloading_metadata": "جاري تنزيل البيانات الوصفية...", "downloading_metadata": "جارٍ تنزيل البيانات الوصفية...",
"deleting": "جاري حذف المثبت...", "deleting": "جارٍ حذف المثبت...",
"delete": "حذف المثبت", "delete": "إزالة المثبت",
"delete_modal_title": "هل أنت متأكد؟", "delete_modal_title": "هل أنت متأكد؟",
"delete_modal_description": "سيؤدي هذا إلى إزالة جميع ملفات التثبيت من جهازك", "delete_modal_description": "سيؤدي هذا إلى إزالة جميع ملفات التثبيت من جهازك",
"install": "تثبيت", "install": "تثبيت",
"download_in_progress": "قيد التقدم", "download_in_progress": "قيد التقدم",
"queued_downloads": "التنزيلات في قائمة الانتظار", "queued_downloads": "التنزيلات في قائمة الانتظار",
"downloads_completed": "مكتملة", "downloads_completed": "مكتمل",
"queued": "في قائمة الانتظار", "queued": "في قائمة الانتظار",
"no_downloads_title": "لا شيء هنا", "no_downloads_title": "فارغ جدًا",
"no_downloads_description": "لم تقم بتنزيل أي شيء باستخدام Hydra بعد، ولكن لم يفت الأوان للبدء.", "no_downloads_description": "لم تقم بتنزيل أي شيء باستخدام Hydra بعد، ولكن لم يفت الأوان للبدء.",
"checking_files": "جاري فحص الملفات...", "checking_files": "جارٍ فحص الملفات...",
"seeding": "جاري التوزيع", "seeding": "التوزيع",
"stop_seeding": "إيقاف التوزيع", "stop_seeding": "إيقاف التوزيع",
"resume_seeding": "استئناف التوزيع", "resume_seeding": "استئناف التوزيع",
"options": "إدارة" "options": "إدارة"
@@ -235,37 +228,31 @@
"change": "تحديث", "change": "تحديث",
"notifications": "الإشعارات", "notifications": "الإشعارات",
"enable_download_notifications": "عند اكتمال التنزيل", "enable_download_notifications": "عند اكتمال التنزيل",
"enable_repack_list_notifications": "عند إضافة إصدار معاد جديد", "enable_repack_list_notifications": "عند إضافة حزمة معاد تعبئتها جديدة",
"real_debrid_api_token_label": "رمز Real-Debrid API", "real_debrid_api_token_label": "رمز واجهة برمجة تطبيقات Real-Debrid",
"quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق", "quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق",
"launch_with_system": "تشغيل Hydra مع بدء النظام", "launch_with_system": "تشغيل Hydra مع بدء النظام",
"general": "عام", "general": "عام",
"behavior": "السلوك", "behavior": "السلوك",
"download_sources": "مصادر التنزيل", "download_sources": "مصادر التنزيل",
"language": "اللغة", "language": "اللغة",
"api_token": "رمز API", "real_debrid_api_token": "رمز API",
"enable_real_debrid": "تفعيل Real-Debrid", "enable_real_debrid": "تفعيل Real-Debrid",
"real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، محدودة فقط بسرعة اتصالك بالإنترنت.", "real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، مقيدة فقط بسرعة الإنترنت لديك.",
"debrid_invalid_token": "رمز API غير صالح", "real_debrid_invalid_token": "رمز API غير صالح",
"debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا</0>", "real_debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا</0>",
"real_debrid_free_account_error": "الحساب \"{{username}}\" حساب مجاني. يرجى الاشتراك في Real-Debrid", "real_debrid_free_account_error": "الحساب \"{{username}}\" هو حساب مجاني. يرجى الاشتراك في Real-Debrid",
"debrid_linked_message": "تم ربط الحساب \"{{username}}\"", "real_debrid_linked_message": "تم ربط الحساب \"{{username}}\"",
"save_changes": "حفظ التغييرات", "save_changes": "حفظ التغييرات",
"changes_saved": "تم حفظ التغييرات بنجاح", "changes_saved": "تم حفظ التغييرات بنجاح",
"download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL للمصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.", "download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL المصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.",
"validate_download_source": "تحقق", "validate_download_source": "تحقق",
"remove_download_source": "إزالة", "remove_download_source": "إزالة",
"removed_download_sources": "تمت إزالة مصادر التنزيل",
"cancel_button_confirmation_delete_all_sources": "لا",
"confirm_button_confirmation_delete_all_sources": "نعم، احذف كل شيء",
"description_confirmation_delete_all_sources": "سوف تقوم بحذف جميع مصادر التنزيل",
"title_confirmation_delete_all_sources": "احذف جميع مصادر التنزيل",
"button_delete_all_sources": "قم بإزالة جميع مصادر التنزيل",
"add_download_source": "إضافة مصدر", "add_download_source": "إضافة مصدر",
"download_count_zero": "لا توجد خيارات تنزيل", "download_count_zero": "لا توجد خيارات تنزيل",
"download_count_one": "{{countFormatted}} خيار تنزيل", "download_count_one": "{{countFormatted}} خيار تنزيل",
"download_count_other": "{{countFormatted}} خيارات تنزيل", "download_count_other": "{{countFormatted}} خيارات تنزيل",
"download_source_url": "عنوان مصدر التنزيل", "download_source_url": "عنوان URL لمصدر التنزيل",
"add_download_source_description": "أدخل عنوان URL لملف .json", "add_download_source_description": "أدخل عنوان URL لملف .json",
"download_source_up_to_date": "محدث", "download_source_up_to_date": "محدث",
"download_source_errored": "خطأ", "download_source_errored": "خطأ",
@@ -285,13 +272,13 @@
"profile_visibility": "رؤية الملف الشخصي", "profile_visibility": "رؤية الملف الشخصي",
"profile_visibility_description": "اختر من يمكنه رؤية ملفك الشخصي ومكتبتك", "profile_visibility_description": "اختر من يمكنه رؤية ملفك الشخصي ومكتبتك",
"required_field": "هذا الحقل مطلوب", "required_field": "هذا الحقل مطلوب",
"source_already_exists": "هذا المصدر مضاف مسبقًا", "source_already_exists": "تمت إضافة هذا المصدر مسبقًا",
"must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالح", "must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالحًا",
"blocked_users": "المستخدمون المحظورون", "blocked_users": "المستخدمون المحظورون",
"user_unblocked": "تم إلغاء حظر المستخدم", "user_unblocked": "تم إلغاء حظر المستخدم",
"enable_achievement_notifications": "عند فتح إنجاز", "enable_achievement_notifications": "عند فتح إنجاز",
"launch_minimized": "تشغيل Hydra مصغرًا", "launch_minimized": "تشغيل Hydra مصغرًا",
"disable_nsfw_alert": "تعطيل تنبيهات المحتوى غير اللائق", "disable_nsfw_alert": "تعطيل تنبيه المحتوى غير اللائق",
"seed_after_download_complete": "التوزيع بعد اكتمال التنزيل", "seed_after_download_complete": "التوزيع بعد اكتمال التنزيل",
"show_hidden_achievement_description": "عرض وصف الإنجازات المخفية قبل فتحها", "show_hidden_achievement_description": "عرض وصف الإنجازات المخفية قبل فتحها",
"account": "الحساب", "account": "الحساب",
@@ -309,47 +296,18 @@
"become_subscriber": "كن مشتركًا في Hydra Cloud", "become_subscriber": "كن مشتركًا في Hydra Cloud",
"subscription_renew_cancelled": "تم تعطيل التجديد التلقائي", "subscription_renew_cancelled": "تم تعطيل التجديد التلقائي",
"subscription_renews_on": "سيتم تجديد اشتراكك في {{date}}", "subscription_renews_on": "سيتم تجديد اشتراكك في {{date}}",
"bill_sent_until": "سيتم إرسال فاتورتك القادمة حتى هذا اليوم", "bill_sent_until": "سيتم إرسال فاتورتك التالية حتى هذا اليوم"
"no_themes": "يبدو أنه ليس لديك أي سمات بعد، لكن لا تقلق، انقر هنا لإنشاء أول تحفة فنية لك.",
"editor_tab_code": "الكود",
"editor_tab_info": "معلومات",
"editor_tab_save": "حفظ",
"web_store": "المتجر الإلكتروني",
"clear_themes": "مسح",
"create_theme": "إنشاء",
"create_theme_modal_title": "إنشاء سمة مخصصة",
"create_theme_modal_description": "إنشاء سمة جديدة لتخصيص مظهر Hydra",
"theme_name": "الاسم",
"insert_theme_name": "أدخل اسم السمة",
"set_theme": "تعيين السمة",
"unset_theme": "إلغاء تعيين السمة",
"delete_theme": "حذف السمة",
"edit_theme": "تعديل السمة",
"delete_all_themes": "حذف جميع السمات",
"delete_all_themes_description": "سيؤدي هذا إلى حذف جميع السمات المخصصة الخاصة بك",
"delete_theme_description": "سيؤدي هذا إلى حذف السمة {{theme}}",
"cancel": "إلغاء",
"appearance": "المظهر",
"enable_torbox": "تفعيل Torbox",
"torbox_description": "TorBox هي خدمة seedbox متميزة تنافس أفضل الخوادم في السوق.",
"torbox_account_linked": "تم ربط حساب TorBox",
"real_debrid_account_linked": "تم ربط حساب Real-Debrid",
"name_min_length": "يجب أن يكون اسم السمة على الأقل 3 أحرف",
"import_theme": "استيراد سمة",
"import_theme_description": "ستقوم باستيراد {{theme}} من متجر السمات",
"error_importing_theme": "خطأ في استيراد السمة",
"theme_imported": "تم استيراد السمة بنجاح"
}, },
"notifications": { "notifications": {
"download_complete": "اكتمل التنزيل", "download_complete": "اكتمل التنزيل",
"game_ready_to_install": "{{title}} جاهز للتثبيت", "game_ready_to_install": "{{title}} جاهز للتثبيت",
"repack_list_updated": "تم تحديث قائمة الإصدارات المعادة", "repack_list_updated": "تم تحديث قائمة الحزم المعاد تعبئتها",
"repack_count_one": "تمت إضافة {{count}} إصدار معاد", "repack_count_one": "تمت إضافة {{count}} حزمة معاد تعبئتها",
"repack_count_other": "تمت إضافة {{count}} إصدارات معادة", "repack_count_other": "تمت إضافة {{count}} حزم معاد تعبئتها",
"new_update_available": "الإصدار {{version}} متوفر", "new_update_available": "الإصدار {{version}} متوفر",
"restart_to_install_update": "أعد تشغيل Hydra لتثبيت التحديث", "restart_to_install_update": "أعد تشغيل Hydra لتثبيت التحديث",
"notification_achievement_unlocked_title": "تم فتح إنجاز لـ {{game}}", "notification_achievement_unlocked_title": "تم فتح إنجاز لـ {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} و {{count}} أخرى تم فتحها" "notification_achievement_unlocked_body": "{{achievement}} و {{count}} آخرين تم فتحهم"
}, },
"system_tray": { "system_tray": {
"open": "فتح Hydra", "open": "فتح Hydra",
@@ -361,7 +319,7 @@
"binary_not_found_modal": { "binary_not_found_modal": {
"title": "البرامج غير مثبتة", "title": "البرامج غير مثبتة",
"description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك", "description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك",
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة Linux الخاصة بك حتى تعمل اللعبة بشكل طبيعي" "instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة لينكس الخاصة بك حتى تعمل اللعبة بشكل طبيعي"
}, },
"modal": { "modal": {
"close": "زر الإغلاق" "close": "زر الإغلاق"
@@ -370,16 +328,16 @@
"toggle_password_visibility": "تبديل رؤية كلمة المرور" "toggle_password_visibility": "تبديل رؤية كلمة المرور"
}, },
"user_profile": { "user_profile": {
"amount_hours": "{{amount}} ساعة", "amount_hours": "{{amount}} ساعات",
"amount_minutes": "{{amount}} دقيقة", "amount_minutes": "{{amount}} دقائق",
"last_time_played": "آخر مرة لعب {{period}}", "last_time_played": "آخر تشغيل {{period}}",
"activity": "النشاط الأخير", "activity": "النشاط الأخير",
"library": "المكتبة", "library": "المكتبة",
"total_play_time": "إجمالي وقت اللعب", "total_play_time": "إجمالي وقت اللعب",
"no_recent_activity_title": "لا شيء هنا...", "no_recent_activity_title": "همم... لا شيء هنا",
"no_recent_activity_description": "لم تلعب أي ألعاب مؤخرًا. حان الوقت لتغيير ذلك!", "no_recent_activity_description": "لم تلعب أي ألعاب مؤخرًا. حان الوقت لتغيير ذلك!",
"display_name": "اسم العرض", "display_name": "اسم العرض",
"saving": "جاري الحفظ", "saving": "جارٍ الحفظ",
"save": "حفظ", "save": "حفظ",
"edit_profile": "تعديل الملف الشخصي", "edit_profile": "تعديل الملف الشخصي",
"saved_successfully": "تم الحفظ بنجاح", "saved_successfully": "تم الحفظ بنجاح",
@@ -388,13 +346,13 @@
"cancel": "إلغاء", "cancel": "إلغاء",
"successfully_signed_out": "تم تسجيل الخروج بنجاح", "successfully_signed_out": "تم تسجيل الخروج بنجاح",
"sign_out": "تسجيل الخروج", "sign_out": "تسجيل الخروج",
"playing_for": "جاري اللعب لمدة {{amount}}", "playing_for": "يلعب لمدة {{amount}}",
"sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. عند تسجيل الخروج، لن تكون مكتبتك مرئية، ولن يتم حفظ أي تقدم. هل تتابع تسجيل الخروج؟", "sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. عند تسجيل الخروج، لن تكون مكتبتك مرئية بعد الآن، ولن يتم حفظ أي تقدم. هل تتابع تسجيل الخروج؟",
"add_friends": "إضافة أصدقاء", "add_friends": "إضافة أصدقاء",
"add": "إضافة", "add": "إضافة",
"friend_code": "رمز الصديق", "friend_code": "رمز الصديق",
"see_profile": "عرض الملف الشخصي", "see_profile": "عرض الملف الشخصي",
"sending": "جاري الإرسال", "sending": "جارٍ الإرسال",
"friend_request_sent": "تم إرسال طلب الصداقة", "friend_request_sent": "تم إرسال طلب الصداقة",
"friends": "الأصدقاء", "friends": "الأصدقاء",
"friends_list": "قائمة الأصدقاء", "friends_list": "قائمة الأصدقاء",
@@ -413,19 +371,19 @@
"blocked_users": "المستخدمون المحظورون", "blocked_users": "المستخدمون المحظورون",
"unblock": "إلغاء الحظر", "unblock": "إلغاء الحظر",
"no_friends_added": "ليس لديك أصدقاء مضافون", "no_friends_added": "ليس لديك أصدقاء مضافون",
"pending": "معلق", "pending": يد الانتظار",
"no_pending_invites": "ليس لديك دعوات معلقة", "no_pending_invites": "ليس لديك دعوات معلقة",
"no_blocked_users": "ليس لديك مستخدمون محظورون", "no_blocked_users": "ليس لديك مستخدمون محظورون",
"friend_code_copied": "تم نسخ رمز الصديق", "friend_code_copied": "تم نسخ رمز الصديق",
"undo_friendship_modal_text": "سيؤدي هذا إلى إلغاء صداقتك مع {{displayName}}", "undo_friendship_modal_text": "سيؤدي هذا إلى إلغاء صداقتك مع {{displayName}}",
"privacy_hint": "لضبط من يمكنه رؤية هذا، انتقل إلى <0>الإعدادات</0>", "privacy_hint": "لضبط من يمكنه رؤية هذا، انتقل إلى <0>الإعدادات</0>",
"locked_profile": "هذا الملف الشخصي خاص", "locked_profile": "هذا الملف الشخصي خاص",
"image_process_failure": "فشل في معالجة الصورة", "image_process_failure": "فشل معالجة الصورة",
"required_field": "هذا الحقل مطلوب", "required_field": "هذا الحقل مطلوب",
"displayname_min_length": "يجب أن يكون اسم العرض على الأقل 3 أحرف", "displayname_min_length": "يجب أن يكون اسم العرض على الأقل 3 أحرف",
"displayname_max_length": "يجب أن لا يتجاوز اسم العرض 50 حرفًا", "displayname_max_length": "يجب ألا يتجاوز اسم العرض 50 حرفًا",
"report_profile": "الإبلاغ عن هذا الملف", "report_profile": "الإبلاغ عن هذا الملف الشخصي",
"report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف؟", "report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف الشخصي؟",
"report_description": "معلومات إضافية", "report_description": "معلومات إضافية",
"report_description_placeholder": "معلومات إضافية", "report_description_placeholder": "معلومات إضافية",
"report": "الإبلاغ", "report": "الإبلاغ",
@@ -435,32 +393,32 @@
"report_reason_spam": "بريد عشوائي", "report_reason_spam": "بريد عشوائي",
"report_reason_other": "أخرى", "report_reason_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": "تم تحديث صورة الخلفية",
"stats": "الإحصائيات", "stats": "الإحصائيات",
"achievements": "الإنجازات", "achievements": "إنجازات",
"games": "الألعاب", "games": "الألعاب",
"top_percentile": "الأعلى {{percentile}}%", "top_percentile": "ال{{percentile}}% الأعلى",
"ranking_updated_weekly": "يتم تحديث التصنيف أسبوعيًا", "ranking_updated_weekly": "يتم تحديث التصنيف أسبوعيًا",
"playing": "جاري لعب {{game}}", "playing": "يلعب {{game}}",
"achievements_unlocked": "الإنجازات المفتوحة", "achievements_unlocked": "الإنجازات المفتوحة",
"earned_points": "النقاط المكتسبة", "earned_points": "النقاط المكتسبة",
"show_achievements_on_profile": "عرض إنجازاتك في ملفك الشخصي", "show_achievements_on_profile": "عرض إنجازاتك على ملفك الشخصي",
"show_points_on_profile": "عرض نقاطك المكتسبة في ملفك الشخصي" "show_points_on_profile": "عرض نقاطك المكتسبة على ملفك الشخصي"
}, },
"achievement": { "achievement": {
"achievement_unlocked": "تم فتح الإنجاز", "achievement_unlocked": "تم فتح الإنجاز",
"user_achievements": "إنجازات {{displayName}}", "user_achievements": "إنجازات {{displayName}}",
"your_achievements": "إنجازاتك", "your_achievements": "إنجازاتك",
"unlocked_at": "تم الفتح في: {{date}}", "unlocked_at": "تم الفتح في: {{date}}",
"subscription_needed": "يحتاج إلى اشتراك Hydra Cloud لعرض هذا المحتوى", "subscription_needed": "يحتاج إلى اشتراك Hydra Cloud لرؤية هذا المحتوى",
"new_achievements_unlocked": "تم فتح {{achievementCount}} إنجازات جديدة من {{gameCount}} ألعاب", "new_achievements_unlocked": "تم فتح {{achievementCount}} إنجازات جديدة من {{gameCount}} ألعاب",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} إنجازات", "achievement_progress": "{{unlockedCount}}/{{totalCount}} إنجازات",
"achievements_unlocked_for_game": "تم فتح {{achievementCount}} إنجازات جديدة لـ {{gameTitle}}", "achievements_unlocked_for_game": "تم فتح {{achievementCount}} إنجازات جديدة لـ {{gameTitle}}",
"hidden_achievement_tooltip": "هذا إنجاز مخفي", "hidden_achievement_tooltip": "هذا إنجاز مخفي",
"achievement_earn_points": "احصل على {{points}} نقاط مع هذا الإنجاز", "achievement_earn_points": "اكسب {{points}} نقطة مع هذا الإنجاز",
"earned_points": "النقاط المكتسبة:", "earned_points": "النقاط المكتسبة:",
"available_points": "النقاط المتاحة:", "available_points": "النقاط المتاحة:",
"how_to_earn_achievements_points": "كيفية كسب نقاط الإنجازات؟" "how_to_earn_achievements_points": "كيفية كسب نقاط الإنجازات؟"
@@ -470,10 +428,10 @@
"subscribe_now": "اشترك الآن", "subscribe_now": "اشترك الآن",
"cloud_saving": "حفظ سحابي", "cloud_saving": "حفظ سحابي",
"cloud_achievements": "احفظ إنجازاتك على السحابة", "cloud_achievements": "احفظ إنجازاتك على السحابة",
"animated_profile_picture": "صورة ملف متحركة", "animated_profile_picture": "صورة ملف شخصي متحركة",
"premium_support": "دعم ممتاز", "premium_support": "دعم ممتاز",
"show_and_compare_achievements": "اعرض وقارن إنجازاتك مع المستخدمين الآخرين", "show_and_compare_achievements": "اعرض وقارن إنجازاتك مع المستخدمين الآخرين",
"animated_profile_banner": "بانر ملف متحرك", "animated_profile_banner": "بانر ملف شخصي متحرك",
"hydra_cloud": "Hydra Cloud", "hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "لقد اكتشفت ميزة Hydra Cloud!", "hydra_cloud_feature_found": "لقد اكتشفت ميزة Hydra Cloud!",
"learn_more": "معرفة المزيد" "learn_more": "معرفة المزيد"

View File

@@ -14,10 +14,8 @@
"paused": "{{title}} (Спынена)", "paused": "{{title}} (Спынена)",
"downloading": "{{title}} ({{percentage}} - Сцягванне…)", "downloading": "{{title}} ({{percentage}} - Сцягванне…)",
"filter": "Фільтар бібліятэкі", "filter": "Фільтар бібліятэкі",
"home": "Галоўная", "home": "Галоўная"
"favorites": "Улюбленыя"
}, },
"header": { "header": {
"search": "Пошук", "search": "Пошук",
"home": "Галоўная", "home": "Галоўная",
@@ -107,12 +105,6 @@
"notifications": "Апавяшчэнні", "notifications": "Апавяшчэнні",
"enable_download_notifications": "Па сканчэнні сцягванні", "enable_download_notifications": "Па сканчэнні сцягванні",
"enable_repack_list_notifications": "Пры даданні новага рэпака", "enable_repack_list_notifications": "Пры даданні новага рэпака",
"cancel_button_confirmation_delete_all_sources": "Няма",
"confirm_button_confirmation_delete_all_sources": "Так,выдаліць усё",
"description_confirmation_delete_all_sources": "Вы выдаліце ​​ўсе крыніцы сцягвання",
"title_confirmation_delete_all_sources": "Выдаліць усе крыніцы сцягвання",
"removed_download_sources": "Крыніцы сцягвання выдалены",
"button_delete_all_sources": "Премахнете всички източници на изтегляне",
"behavior": "Паводзіны", "behavior": "Паводзіны",
"quit_app_instead_hiding": "Закрываць праграму замест таго, каб хаваць яе ў трэй", "quit_app_instead_hiding": "Закрываць праграму замест таго, каб хаваць яе ў трэй",
"launch_with_system": "Запускаць праграму пры запуску сыстэмы" "launch_with_system": "Запускаць праграму пры запуску сыстэмы"

View File

@@ -26,8 +26,7 @@
"game_has_no_executable": "Играта няма избран изпълним файл", "game_has_no_executable": "Играта няма избран изпълним файл",
"sign_in": "Вписване", "sign_in": "Вписване",
"friends": "Приятели", "friends": "Приятели",
"need_help": "Имате нужда от помощ??", "need_help": "Имате нужда от помощ??"
"favorites": "Любими игри"
}, },
"header": { "header": {
"search": "Търсене", "search": "Търсене",
@@ -231,13 +230,13 @@
"behavior": "Поведение", "behavior": "Поведение",
"download_sources": "Източници за изтегляне", "download_sources": "Източници за изтегляне",
"language": "Език", "language": "Език",
"api_token": "API Токен", "real_debrid_api_token": "API Токен",
"enable_real_debrid": "Включи Real-Debrid", "enable_real_debrid": "Включи Real-Debrid",
"real_debrid_description": "Real-Debrid е неограничен даунлоудър, който ви позволява бързо да изтегляте файлове, ограничени само от скоростта на интернет..", "real_debrid_description": "Real-Debrid е неограничен даунлоудър, който ви позволява бързо да изтегляте файлове, ограничени само от скоростта на интернет..",
"debrid_invalid_token": "Невалиден API токен", "real_debrid_invalid_token": "Невалиден API токен",
"debrid_api_token_hint": "Вземете своя API токен <0>тук</0>", "real_debrid_api_token_hint": "Вземете своя API токен <0>тук</0>",
"real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен акаунт. Моля абонирай се за Real-Debrid", "real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен акаунт. Моля абонирай се за Real-Debrid",
"debrid_linked_message": "Акаунтът \"{{username}}\" е свързан", "real_debrid_linked_message": "Акаунтът \"{{username}}\" е свързан",
"save_changes": "Запази промените", "save_changes": "Запази промените",
"changes_saved": "Промените са успешно запазни", "changes_saved": "Промените са успешно запазни",
"download_sources_description": "Hydra ще извлича връзките за изтегляне от тези източници. URL адресът на източника трябва да е директна връзка към .json файл, съдържащ връзките за изтегляне.", "download_sources_description": "Hydra ще извлича връзките за изтегляне от тези източници. URL адресът на източника трябва да е директна връзка към .json файл, съдържащ връзките за изтегляне.",
@@ -253,12 +252,6 @@
"download_source_errored": "Сгрешен", "download_source_errored": "Сгрешен",
"sync_download_sources": "Синхронизирай източниците", "sync_download_sources": "Синхронизирай източниците",
"removed_download_source": "Източника за сваляне е премахнат", "removed_download_source": "Източника за сваляне е премахнат",
"cancel_button_confirmation_delete_all_sources": "не",
"confirm_button_confirmation_delete_all_sources": "Да, удалить все",
"description_confirmation_delete_all_sources": "Вы удалите все источники загрузки",
"title_confirmation_delete_all_sources": "Удалить все источники загрузки",
"removed_download_sources": "Шрифты удалены",
"button_delete_all_sources": "Удалить все источники загрузки",
"added_download_source": "Добавен източник за сваляне", "added_download_source": "Добавен източник за сваляне",
"download_sources_synced": "Всички източници за сваляне са синхронизирани", "download_sources_synced": "Всички източници за сваляне са синхронизирани",
"insert_valid_json_url": "Добавете ваиден JSON линк", "insert_valid_json_url": "Добавете ваиден JSON линк",

View File

@@ -20,12 +20,10 @@
"home": "Inici", "home": "Inici",
"queued": "{{title}} (En espera)", "queued": "{{title}} (En espera)",
"game_has_no_executable": "El joc encara no té un executable seleccionat", "game_has_no_executable": "El joc encara no té un executable seleccionat",
"sign_in": "Entra", "sign_in": "Entra"
"favorites": "Favorits"
}, },
"header": { "header": {
"search": "Cerca jocs", "search": "Cerca jocs",
"home": "Inici", "home": "Inici",
"catalogue": "Catàleg", "catalogue": "Catàleg",
"downloads": "Baixades", "downloads": "Baixades",
@@ -163,24 +161,18 @@
"behavior": "Comportament", "behavior": "Comportament",
"download_sources": "Fonts de descàrrega", "download_sources": "Fonts de descàrrega",
"language": "Idioma", "language": "Idioma",
"api_token": "Testimoni API", "real_debrid_api_token": "Testimoni API",
"enable_real_debrid": "Activa el Real Debrid", "enable_real_debrid": "Activa el Real Debrid",
"real_debrid_description": "Real-Debrid és un programa de descàrrega sense restriccions que us permet descarregar fitxers a l'instant i al màxim de la vostra velocitat d'Internet.", "real_debrid_description": "Real-Debrid és un programa de descàrrega sense restriccions que us permet descarregar fitxers a l'instant i al màxim de la vostra velocitat d'Internet.",
"debrid_invalid_token": "Invalida el testimoni de l'API", "real_debrid_invalid_token": "Invalida el testimoni de l'API",
"debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí</0>.", "real_debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí</0>.",
"real_debrid_free_account_error": "L'usuari \"{{username}}\" és un compte gratuït. Si us plau subscriu-te a Real-Debrid", "real_debrid_free_account_error": "L'usuari \"{{username}}\" és un compte gratuït. Si us plau subscriu-te a Real-Debrid",
"debrid_linked_message": "Compte \"{{username}}\" vinculat", "real_debrid_linked_message": "Compte \"{{username}}\" vinculat",
"save_changes": "Desa els canvis", "save_changes": "Desa els canvis",
"changes_saved": "Els canvis s'han desat correctament", "changes_saved": "Els canvis s'han desat correctament",
"download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.", "download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.",
"validate_download_source": "Valida", "validate_download_source": "Valida",
"remove_download_source": "Elimina", "remove_download_source": "Elimina",
"cancel_button_confirmation_delete_all_sources": "No",
"confirm_button_confirmation_delete_all_sources": "Sí, esborra-ho tot",
"description_confirmation_delete_all_sources": "Eliminareu totes les fonts de descàrrega",
"title_confirmation_delete_all_sources": "Suprimeix totes les fonts de baixada",
"removed_download_sources": "S'han eliminat les fonts de descàrrega",
"button_delete_all_sources": "Elimina totes les fonts de baixada",
"add_download_source": "Afegeix font", "add_download_source": "Afegeix font",
"download_count_zero": "No hi ha baixades a la llista", "download_count_zero": "No hi ha baixades a la llista",
"download_count_one": "{{countFormatted}} a la llista de baixades", "download_count_one": "{{countFormatted}} a la llista de baixades",

View File

@@ -26,8 +26,7 @@
"game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor", "game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor",
"sign_in": "Přihlásit se", "sign_in": "Přihlásit se",
"friends": "Přátelé", "friends": "Přátelé",
"need_help": "Potřebujete pomoc?", "need_help": "Potřebujete pomoc?"
"favorites": "Oblíbené"
}, },
"header": { "header": {
"search": "Vyhledat hry", "search": "Vyhledat hry",
@@ -44,21 +43,11 @@
"downloading_metadata": "Stahuji metadata: {{title}}…", "downloading_metadata": "Stahuji metadata: {{title}}…",
"downloading": "Stahuji {{title}}… ({{percentage}} staženo) - Odhadovaný čas {{eta}} - {{speed}}", "downloading": "Stahuji {{title}}… ({{percentage}} staženo) - Odhadovaný čas {{eta}} - {{speed}}",
"calculating_eta": "Stahuji {{title}}… ({{percentage}} staženo) - Počítám zbývající čas…", "calculating_eta": "Stahuji {{title}}… ({{percentage}} staženo) - Počítám zbývající čas…",
"checking_files": "Kontroluji soubory: {{title}}… ({{percentage}} ověřeno)", "checking_files": "Kontroluji soubory: {{title}}… ({{percentage}} ověřeno)"
"installing_common_redist": "{{log}}…",
"installation_complete": "Instalace dokončena",
"installation_complete_message": "Běžné redistribuovatelné komponenty byly úspěšně nainstalovány"
}, },
"catalogue": { "catalogue": {
"search": "Filtr…", "next_page": "Další strana",
"developers": "Vývojáři", "previous_page": "Předchozí strana"
"genres": "Žánry",
"tags": "Tagy",
"publishers": "Vydavatelé",
"download_sources": "Zdroje stahování",
"result_count": "Výsledky: {{resultCount}}",
"filter_count": "Dostupné: {{filterCount}}",
"clear_filters": "Vyčistit vybrané filtry: {{filterCount}}"
}, },
"game_details": { "game_details": {
"open_download_options": "Otevřít možnosti stahování", "open_download_options": "Otevřít možnosti stahování",
@@ -117,7 +106,6 @@
"open_download_location": "Zobrazit stažené soubory", "open_download_location": "Zobrazit stažené soubory",
"create_shortcut": "Vytvořit zástupce na ploše", "create_shortcut": "Vytvořit zástupce na ploše",
"remove_files": "Odebrat soubory", "remove_files": "Odebrat soubory",
"clear": "Vyčistit",
"remove_from_library_title": "Jste si jisti?", "remove_from_library_title": "Jste si jisti?",
"remove_from_library_description": "Tohle odstraní {{game}} z vaší knihovny", "remove_from_library_description": "Tohle odstraní {{game}} z vaší knihovny",
"options": "Možnosti", "options": "Možnosti",
@@ -171,9 +159,6 @@
"loading_save_preview": "Hledání uložených her...", "loading_save_preview": "Hledání uložených her...",
"wine_prefix": "Wine Prefix", "wine_prefix": "Wine Prefix",
"wine_prefix_description": "Wine Prefix použit pro spuštění této hry", "wine_prefix_description": "Wine Prefix použit pro spuštění této hry",
"launch_options": "Možnosti spuštění",
"launch_options_description": "Pokročilí uživatelé mohou zadat speciální parametry spuštění (experimentální funkce)",
"launch_options_placeholder": "Žádné parametery nebyly specifikovány",
"no_download_option_info": "Žádné informace nejsou dostupny", "no_download_option_info": "Žádné informace nejsou dostupny",
"backup_deletion_failed": "Nepovedlo se odstranit zálohu", "backup_deletion_failed": "Nepovedlo se odstranit zálohu",
"max_number_of_artifacts_reached": "Dosáhli jste maximálního počtu záloh pro tuto hru", "max_number_of_artifacts_reached": "Dosáhli jste maximálního počtu záloh pro tuto hru",
@@ -181,23 +166,7 @@
"manage_files_description": "Spravovat, které soubory budou zálohovány a obnoveny", "manage_files_description": "Spravovat, které soubory budou zálohovány a obnoveny",
"select_folder": "Vybrat složku", "select_folder": "Vybrat složku",
"backup_from": "Zálohy z {{date}}", "backup_from": "Zálohy z {{date}}",
"custom_backup_location_set": "Vlastní umístění záloh nastaveno", "custom_backup_location_set": "Vlastní umístění záloh nastaveno"
"automatic_backup_from": "Automatická záloha z {{date}}",
"enable_automatic_cloud_sync": "Povolit automatické zálohy v cloudu",
"no_directory_selected": "Žádná složka není zvolena",
"no_write_permission": "Nemohu stahovat do této složky. Klikni sem pro více informací.",
"reset_achievements": "Resetovat achievementy",
"reset_achievements_description": "Toto zresetuje všechny achievementy pro hru {{game}}",
"reset_achievements_title": "Jste si jisti?",
"reset_achievements_success": "Achievementy úspěšně resetovány",
"reset_achievements_error": "Nepodařilo se resetovat achievementy",
"download_error_gofile_quota_exceeded": "Překročili jste vaši měsíční GoFile kvótu. Prosím vyčkejte na resetování kvóty.",
"download_error_real_debrid_account_not_authorized": "Váš Real-Debrid účet není autorizován pro vytváření nových stahování. Prosím zkontrolujte nastavení vašeho účtu a zkuste to znovu.",
"download_error_not_cached_in_real_debrid": "Toto stahování není dostupné na Real-Debrid a získávání informací o stahování z Real-Debrid není zatím dostupné.",
"download_error_not_cached_in_torbox": "Toto stahování není dostupné na Torbox a získávání informací o stahování z Torbox není zatím dostupné.",
"game_removed_from_favorites": "Hra odebrána z oblíbených",
"game_added_to_favorites": "Hra přidána do oblíbených",
"automatically_extract_downloaded_files": "Automaticky rozbalit stažené soubory"
}, },
"activation": { "activation": {
"title": "Aktivovat hydru", "title": "Aktivovat hydru",
@@ -230,13 +199,7 @@
"queued": "V řadě", "queued": "V řadě",
"no_downloads_title": "Prázdno..", "no_downloads_title": "Prázdno..",
"no_downloads_description": "Ještě jsi zatím nic nestáhl přes Hydru, ale furt není pozdě začít.", "no_downloads_description": "Ještě jsi zatím nic nestáhl přes Hydru, ale furt není pozdě začít.",
"checking_files": "Kontroluji soubory…", "checking_files": "Kontroluji soubory…"
"seeding": "Seedování",
"stop_seeding": "Zastavování seedování",
"resume_seeding": "Obnovit seedování",
"options": "Spravovat",
"extract": "Rozbalit soubory",
"extracting": "Rozbalování souborů…"
}, },
"settings": { "settings": {
"downloads_path": "Umístění stahování", "downloads_path": "Umístění stahování",
@@ -251,13 +214,13 @@
"behavior": "Chování", "behavior": "Chování",
"download_sources": "Zdroje stahování", "download_sources": "Zdroje stahování",
"language": "Jazyk", "language": "Jazyk",
"api_token": "API Token", "real_debrid_api_token": "API Token",
"enable_real_debrid": "Povolit Real-Debrid", "enable_real_debrid": "Povolit Real-Debrid",
"real_debrid_description": "Real-Debrid je neomezený správce stahování, který umožňuje stahovat soubory v nejvyšší rychlosti vašeho internetu.", "real_debrid_description": "Real-Debrid je neomezený správce stahování, který umožňuje stahovat soubory v nejvyšší rychlosti vašeho internetu.",
"debrid_invalid_token": "Neplatný API token", "real_debrid_invalid_token": "Neplatný API token",
"debrid_api_token_hint": "API token můžeš sehnat <0>zde</0>", "real_debrid_api_token_hint": "API token můžeš sehnat <0>zde</0>",
"real_debrid_free_account_error": "Účet \"{{username}}\" má základní úroveň. Prosím předplaťte si Real-Debrid", "real_debrid_free_account_error": "Účet \"{{username}}\" má základní úroveň. Prosím předplaťte si Real-Debrid",
"debrid_linked_message": "Účet \"{{username}}\" je propojen", "real_debrid_linked_message": "Účet \"{{username}}\" je propojen",
"save_changes": "Uložit změny", "save_changes": "Uložit změny",
"changes_saved": "Změny úspěšně uloženy", "changes_saved": "Změny úspěšně uloženy",
"download_sources_description": "Hydra bude odsud sbírat soubory. Zdrojový odkaz musí být .json soubor obsahující odkazy na soubory.", "download_sources_description": "Hydra bude odsud sbírat soubory. Zdrojový odkaz musí být .json soubor obsahující odkazy na soubory.",
@@ -273,12 +236,6 @@
"download_source_errored": "Chyba", "download_source_errored": "Chyba",
"sync_download_sources": "Synchronizovat zdroje", "sync_download_sources": "Synchronizovat zdroje",
"removed_download_source": "Zdroj odebrán", "removed_download_source": "Zdroj odebrán",
"cancel_button_confirmation_delete_all_sources": "Žádný",
"confirm_button_confirmation_delete_all_sources": "Ano, smazat vše",
"description_confirmation_delete_all_sources": "Smažete všechny zdroje stahování",
"title_confirmation_delete_all_sources": "Odstraňte všechny zdroje stahování",
"removed_download_sources": "Zdroje stahování byly odstraněny",
"button_delete_all_sources": "Odstraňte všechny zdroje stahování",
"added_download_source": "Zdroj přidán", "added_download_source": "Zdroj přidán",
"download_sources_synced": "Všechny zdroje jsou synchronizovány", "download_sources_synced": "Všechny zdroje jsou synchronizovány",
"insert_valid_json_url": "Zadej platnou JSON adresu", "insert_valid_json_url": "Zadej platnou JSON adresu",
@@ -297,65 +254,9 @@
"must_be_valid_url": "Zdroj musí být platký odkaz URL", "must_be_valid_url": "Zdroj musí být platký odkaz URL",
"blocked_users": "Zablokovaní uživatelé", "blocked_users": "Zablokovaní uživatelé",
"user_unblocked": "Uživatel byl odblokován", "user_unblocked": "Uživatel byl odblokován",
"enable_achievement_notifications": "Když je odemčen achievement", "enable_achievement_notifications": "Když je odemknut achievement",
"launch_minimized": "Spustit v minimalizovaném režimu", "launch_minimized": "Spustit v minimalizovaném režimu",
"disable_nsfw_alert": "Deaktivovat upozornění na nevhodný obsah", "disable_nsfw_alert": "Deaktivovat upozornění na nevhodný obsah"
"seed_after_download_complete": "Seedovat až skončí stahování",
"show_hidden_achievement_description": "Zobrazit popisy skrytých achievementů dříve, než jsou odemčeny",
"account": "Účet",
"no_users_blocked": "Nemáte žádného uživatele zablokovaného",
"subscription_active_until": "Vaše Hydra cloud předplatné je platné do {{date}}",
"manage_subscription": "Spravovat předplatné",
"update_email": "Změnit email",
"update_password": "Změnit heslo",
"current_email": "Současný email:",
"no_email_account": "Zatím nemáte nastavený email",
"account_data_updated_successfully": "Data vašeho účtu byly úspěšně upraveny",
"renew_subscription": "Obnovit předplatné Hydra Cloud",
"subscription_expired_at": "Vaše předplatné vypršelo {{date}}",
"no_subscription": "Užijte si Hydru v nejlepší možné podobě",
"become_subscriber": "Připojit se k předplatnému Hydra Cloud",
"subscription_renew_cancelled": "Automatické obnovování je zrušenu",
"subscription_renews_on": "Vaše předplatné se obnoví {{date}}",
"bill_sent_until": "Vaše příští faktura bude odeslána nejpozději do tohoto dne",
"no_themes": "Vypadá to že ještě nemáte žádné vzhledy, ale nebojte, klikněte sem pro vytvoření vašeho prvního mistrovského díla!",
"editor_tab_code": "Kód",
"editor_tab_info": "Info",
"editor_tab_save": "Uložit",
"web_store": "Webový obchod",
"clear_themes": "Vyčistit",
"create_theme": "Vytvořit",
"create_theme_modal_title": "Vytvořit vlastní vzhled",
"create_theme_modal_description": "Vytvořte si vlastní styl, abyste si mohli ozdobit Hydru",
"theme_name": "Název",
"insert_theme_name": "Vložte název vzhledu",
"set_theme": "Nastavit vzhled",
"unset_theme": "Zrušit vzhled",
"delete_theme": "Odstranit vzhled",
"edit_theme": "Upravit vzhled",
"delete_all_themes": "Smazat všechny vzhledy",
"delete_all_themes_description": "Toto smaže všechny vaše vzhledy",
"delete_theme_description": "Toto smaže vzhled {{theme}}",
"cancel": "Zrušit",
"appearance": "Vzhled",
"enable_torbox": "Povolit TorBox",
"torbox_description": "TorBox je prémiový seedbox server který se vyrovná i těm nejlepším seedbox serverům na trhu.",
"torbox_account_linked": "TorBox účet propojen",
"create_real_debrid_account": "Klikni sem pokud ještě nemáš Real-Debrid účet",
"create_torbox_account": "Klikni sem pokud ještě nemáš TorBox účet",
"real_debrid_account_linked": "Real-Debrid účet propojen",
"name_min_length": "Název vzhledu musí být minimálně 3 znaky dlouhý",
"import_theme": "Vložit vzhled",
"import_theme_description": "Chystáte se vložit vzhled {{theme}} z obchodu vzhledů",
"error_importing_theme": "Nastala chyba při vkládání vzhledu",
"theme_imported": "Vzhled úspěšně vložen",
"enable_friend_request_notifications": "Při obdržení žádosti o přátelství",
"enable_auto_install": "Automaticky stahovat aktualizace",
"common_redist": "Běžné redistribuovatelné komponenty",
"common_redist_description": "Běžné redistribuovatelné komponenty jsou potřeba pro spuštění určitých her. Je doporučeno je nainstalovat, aby se předešlo problémům.",
"install_common_redist": "Nainstalovat",
"installing_common_redist": "Instalování…",
"show_download_speed_in_megabytes": "Ukázat rychlost stahování v megabytech"
}, },
"notifications": { "notifications": {
"download_complete": "Stahování dokončeno", "download_complete": "Stahování dokončeno",
@@ -365,20 +266,14 @@
"repack_count_other": "{{count}} repacky přidány", "repack_count_other": "{{count}} repacky přidány",
"new_update_available": "Version {{version}} je dostupná", "new_update_available": "Version {{version}} je dostupná",
"restart_to_install_update": "Restartuj Hydru pro aktualizaci", "restart_to_install_update": "Restartuj Hydru pro aktualizaci",
"notification_achievement_unlocked_title": "Achievement pro {{game}} byl odemčen", "notification_achievement_unlocked_title": "Achievement pro {{game}} byl odemknut",
"notification_achievement_unlocked_body": "{{achievement}} a dalších {{count}} byly odemčeny", "notification_achievement_unlocked_body": "{{achievement}} a dalších {{count}} byly odemknuty"
"new_friend_request_description": "Obdrželi jste novou žádost o přátelství",
"new_friend_request_title": "Nová žádost o přátelství",
"extraction_complete": "Rozbalování dokončeno",
"game_extracted": "{{title}} úspěšně rozbaleno"
}, },
"system_tray": { "system_tray": {
"open": "Otevřít Hydru", "open": "Otevřít Hydru",
"quit": "Odejít" "quit": "Odejít"
}, },
"game_card": { "game_card": {
"available_one": "Dostupné",
"available_other": "Dostupné",
"no_downloads": "Žádné možnosti stahování nenalezeny" "no_downloads": "Žádné možnosti stahování nenalezeny"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
@@ -461,17 +356,7 @@
"your_friend_code": "Tvůj kód přítele:", "your_friend_code": "Tvůj kód přítele:",
"upload_banner": "Nahrát banner profilu", "upload_banner": "Nahrát banner profilu",
"uploading_banner": "Nahrávání banneru", "uploading_banner": "Nahrávání banneru",
"background_image_updated": "Obrázek pozadí byl změněn", "background_image_updated": "Obrázek pozadí byl změněn"
"stats": "Statistiky",
"achievements": "Achievementy",
"games": "Hry",
"top_percentile": "Top {{percentile}}%",
"ranking_updated_weekly": "Žebříčky jsou aktualizovány každý týden",
"playing": "Hraje {{game}}",
"achievements_unlocked": "Achievements odemčen",
"earned_points": "Získané body",
"show_achievements_on_profile": "Zobrazit vaše odemčené achievementy na profilu",
"show_points_on_profile": "Zobrazit vaše získané body na profilu"
}, },
"achievement": { "achievement": {
"achievement_unlocked": "Achievement odemčen", "achievement_unlocked": "Achievement odemčen",
@@ -481,12 +366,7 @@
"subscription_needed": "Je vyžadováno předplatné Hydra Cloud pro zobrazení tohoto obsahu", "subscription_needed": "Je vyžadováno předplatné Hydra Cloud pro zobrazení tohoto obsahu",
"new_achievements_unlocked": "Odemčeno {{achievementCount}} nových achievementů z {{gameCount}} her", "new_achievements_unlocked": "Odemčeno {{achievementCount}} nových achievementů z {{gameCount}} her",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementů", "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementů",
"achievements_unlocked_for_game": "Odemčeno {{achievementCount}} nových achievementů pro {{gameTitle}}", "achievements_unlocked_for_game": "Odemčeno {{achievementCount}} nových achievementů pro {{gameTitle}}"
"hidden_achievement_tooltip": "Toho je skrytý achievement",
"achievement_earn_points": "Získej {{points}} bodů tímto achievementem",
"earned_points": "Získané body",
"available_points": "Dostupné body:",
"how_to_earn_achievements_points": "Jak získat body achievementů?"
}, },
"hydra_cloud": { "hydra_cloud": {
"subscription_tour_title": "Předplatné Hydra Cloud", "subscription_tour_title": "Předplatné Hydra Cloud",
@@ -496,10 +376,6 @@
"animated_profile_picture": "Animované profilové obrázky", "animated_profile_picture": "Animované profilové obrázky",
"premium_support": "Prémiová podpora", "premium_support": "Prémiová podpora",
"show_and_compare_achievements": "Zobraz a porovnej achievementy s ostatními uživateli", "show_and_compare_achievements": "Zobraz a porovnej achievementy s ostatními uživateli",
"animated_profile_banner": "Animovaný banner na profilu", "animated_profile_banner": "Animovaný banner na profilu"
"hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "Právě jste objevili funkci předplatného Hydra Cloud!",
"learn_more": "Zjistit více",
"debrid_description": "Stahovat až 4x rychleji s Nimbus"
} }
} }

View File

@@ -24,12 +24,10 @@
"queued": "{{title}} (I køen)", "queued": "{{title}} (I køen)",
"game_has_no_executable": "Spillet har ikke nogen eksekverbar fil valgt", "game_has_no_executable": "Spillet har ikke nogen eksekverbar fil valgt",
"sign_in": "Log ind", "sign_in": "Log ind",
"friends": "Venner", "friends": "Venner"
"favorites": "Favoritter"
}, },
"header": { "header": {
"search": "Søg efter spil", "search": "Søg efter spil",
"home": "Hjem", "home": "Hjem",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Downloads", "downloads": "Downloads",
@@ -179,13 +177,13 @@
"behavior": "Opførsel", "behavior": "Opførsel",
"download_sources": "Download kilder", "download_sources": "Download kilder",
"language": "Sprog", "language": "Sprog",
"api_token": "API nøgle", "real_debrid_api_token": "API nøgle",
"enable_real_debrid": "Slå Real-Debrid til", "enable_real_debrid": "Slå Real-Debrid til",
"real_debrid_description": "Real-Debrid er en ubegrænset downloader der gør det muligt for dig at downloade filer med det samme og med den bedste udnyttelse af din internet hastighed.", "real_debrid_description": "Real-Debrid er en ubegrænset downloader der gør det muligt for dig at downloade filer med det samme og med den bedste udnyttelse af din internet hastighed.",
"debrid_invalid_token": "Ugyldig API nøgle", "real_debrid_invalid_token": "Ugyldig API nøgle",
"debrid_api_token_hint": "Du kan få din API nøgle <0>her</0>", "real_debrid_api_token_hint": "Du kan få din API nøgle <0>her</0>",
"real_debrid_free_account_error": "Brugeren \"{{username}}\" er en gratis bruger. Venligst abbonér på Real-Debrid", "real_debrid_free_account_error": "Brugeren \"{{username}}\" er en gratis bruger. Venligst abbonér på Real-Debrid",
"debrid_linked_message": "Brugeren \"{{username}}\" er forbundet", "real_debrid_linked_message": "Brugeren \"{{username}}\" er forbundet",
"save_changes": "Gem ændringer", "save_changes": "Gem ændringer",
"changes_saved": "Ændringer gemt successfuldt", "changes_saved": "Ændringer gemt successfuldt",
"download_sources_description": "Hydra vil hente download links fra disse kilder. Kilde URLen skal være et direkte link til en .json fil der indeholder download linkene.", "download_sources_description": "Hydra vil hente download links fra disse kilder. Kilde URLen skal være et direkte link til en .json fil der indeholder download linkene.",
@@ -201,12 +199,6 @@
"download_source_errored": "Fejlede", "download_source_errored": "Fejlede",
"sync_download_sources": "Synkronisér kilder", "sync_download_sources": "Synkronisér kilder",
"removed_download_source": "Download kilde fjernet", "removed_download_source": "Download kilde fjernet",
"cancel_button_confirmation_delete_all_sources": "Ingen",
"confirm_button_confirmation_delete_all_sources": "Ja, slet alt",
"description_confirmation_delete_all_sources": "Du vil slette alle downloadkilder",
"title_confirmation_delete_all_sources": "Slet alle downloadkilder",
"removed_download_sources": "Download kilder fjernet",
"button_delete_all_sources": "Fjern alle downloadkilder",
"added_download_source": "Tilføjede download kilde", "added_download_source": "Tilføjede download kilde",
"download_sources_synced": "Alle download kilder er synkroniserede", "download_sources_synced": "Alle download kilder er synkroniserede",
"insert_valid_json_url": "Indsæt en gyldig JSON url", "insert_valid_json_url": "Indsæt en gyldig JSON url",

View File

@@ -20,12 +20,10 @@
"home": "Home", "home": "Home",
"queued": "{{title}} (In Warteschlange)", "queued": "{{title}} (In Warteschlange)",
"game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt", "game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt",
"sign_in": "Anmelden", "sign_in": "Anmelden"
"favorites": "Favoriten"
}, },
"header": { "header": {
"search": "Spiele suchen", "search": "Spiele suchen",
"home": "Home", "home": "Home",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Downloads", "downloads": "Downloads",
@@ -163,13 +161,13 @@
"behavior": "Verhalten", "behavior": "Verhalten",
"download_sources": "Download-Quellen", "download_sources": "Download-Quellen",
"language": "Sprache", "language": "Sprache",
"api_token": "API Token", "real_debrid_api_token": "API Token",
"enable_real_debrid": "Real-Debrid aktivieren", "enable_real_debrid": "Real-Debrid aktivieren",
"real_debrid_description": "Real-Debrid ist ein unrestriktiver Downloader, der es dir ermöglicht Dateien sofort und mit deiner maximalen Internetgeschwindigkeit herunterzuladen.", "real_debrid_description": "Real-Debrid ist ein unrestriktiver Downloader, der es dir ermöglicht Dateien sofort und mit deiner maximalen Internetgeschwindigkeit herunterzuladen.",
"debrid_invalid_token": "API token nicht gültig", "real_debrid_invalid_token": "API token nicht gültig",
"debrid_api_token_hint": "<0>Hier</0> kannst du dir deinen API Token holen", "real_debrid_api_token_hint": "<0>Hier</0> kannst du dir deinen API Token holen",
"real_debrid_free_account_error": "Das Konto \"{{username}}\" ist ein gratis account. Bitte abonniere Real-Debrid", "real_debrid_free_account_error": "Das Konto \"{{username}}\" ist ein gratis account. Bitte abonniere Real-Debrid",
"debrid_linked_message": "Konto \"{{username}}\" verknüpft", "real_debrid_linked_message": "Konto \"{{username}}\" verknüpft",
"save_changes": "Änderungen speichern", "save_changes": "Änderungen speichern",
"changes_saved": "Änderungen erfolgreich gespeichert", "changes_saved": "Änderungen erfolgreich gespeichert",
"download_sources_description": "Hydra wird die Download-Links von diesen Quellen abrufen. Die Quell-URL muss ein direkter Link zu einer .json Datei, welche die Download-Links enthält, sein.", "download_sources_description": "Hydra wird die Download-Links von diesen Quellen abrufen. Die Quell-URL muss ein direkter Link zu einer .json Datei, welche die Download-Links enthält, sein.",
@@ -185,12 +183,6 @@
"download_source_errored": "Fehlgeschlagen", "download_source_errored": "Fehlgeschlagen",
"sync_download_sources": "Quellen synchronisieren", "sync_download_sources": "Quellen synchronisieren",
"removed_download_source": "Download-Quelle entfernt", "removed_download_source": "Download-Quelle entfernt",
"cancel_button_confirmation_delete_all_sources": "Nein",
"confirm_button_confirmation_delete_all_sources": "Ja, alles löschen",
"description_confirmation_delete_all_sources": "Du löschen alle Downloadquellen",
"title_confirmation_delete_all_sources": "Löschen du alle Downloadquellen",
"removed_download_sources": "Download-Quellen entfernt",
"button_delete_all_sources": "Entfernen Sie alle Downloadquellen",
"added_download_source": "Download-Quelle hinzugefügt", "added_download_source": "Download-Quelle hinzugefügt",
"download_sources_synced": "Alle Download-Quellen sind synchronisiert", "download_sources_synced": "Alle Download-Quellen sind synchronisiert",
"insert_valid_json_url": "Füge eine gültige JSON URL ein", "insert_valid_json_url": "Füge eine gültige JSON URL ein",

View File

@@ -26,8 +26,7 @@
"game_has_no_executable": "Game has no executable selected", "game_has_no_executable": "Game has no executable selected",
"sign_in": "Sign in", "sign_in": "Sign in",
"friends": "Friends", "friends": "Friends",
"need_help": "Need help?", "need_help": "Need help?"
"favorites": "Favorites"
}, },
"header": { "header": {
"search": "Search games", "search": "Search games",
@@ -44,10 +43,7 @@
"downloading_metadata": "Downloading {{title}} metadata…", "downloading_metadata": "Downloading {{title}} metadata…",
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}",
"calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…",
"checking_files": "Checking {{title}} files… ({{percentage}} complete)", "checking_files": "Checking {{title}} files… ({{percentage}} complete)"
"installing_common_redist": "{{log}}…",
"installation_complete": "Installation complete",
"installation_complete_message": "Common redistributables installed successfully"
}, },
"catalogue": { "catalogue": {
"search": "Filter…", "search": "Filter…",
@@ -181,8 +177,6 @@
"manage_files_description": "Manage which files will be backed up and restored", "manage_files_description": "Manage which files will be backed up and restored",
"select_folder": "Select folder", "select_folder": "Select folder",
"backup_from": "Backup from {{date}}", "backup_from": "Backup from {{date}}",
"automatic_backup_from": "Automatic backup from {{date}}",
"enable_automatic_cloud_sync": "Enable automatic cloud sync",
"custom_backup_location_set": "Custom backup location set", "custom_backup_location_set": "Custom backup location set",
"no_directory_selected": "No directory selected", "no_directory_selected": "No directory selected",
"no_write_permission": "Cannot download into this directory. Click here to learn more.", "no_write_permission": "Cannot download into this directory. Click here to learn more.",
@@ -190,14 +184,7 @@
"reset_achievements_description": "This will reset all achievements for {{game}}", "reset_achievements_description": "This will reset all achievements for {{game}}",
"reset_achievements_title": "Are you sure?", "reset_achievements_title": "Are you sure?",
"reset_achievements_success": "Achievements successfully reset", "reset_achievements_success": "Achievements successfully reset",
"reset_achievements_error": "Failed to reset achievements", "reset_achievements_error": "Failed to reset achievements"
"download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.",
"download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.",
"download_error_not_cached_in_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.",
"download_error_not_cached_in_torbox": "This download is not available on Torbox and polling download status from Torbox is not yet available.",
"game_removed_from_favorites": "Game removed from favorites",
"game_added_to_favorites": "Game added to favorites",
"automatically_extract_downloaded_files": "Automatically extract downloaded files"
}, },
"activation": { "activation": {
"title": "Activate Hydra", "title": "Activate Hydra",
@@ -234,9 +221,7 @@
"seeding": "Seeding", "seeding": "Seeding",
"stop_seeding": "Stop seeding", "stop_seeding": "Stop seeding",
"resume_seeding": "Resume seeding", "resume_seeding": "Resume seeding",
"options": "Manage", "options": "Manage"
"extract": "Extract files",
"extracting": "Extracting files…"
}, },
"settings": { "settings": {
"downloads_path": "Downloads path", "downloads_path": "Downloads path",
@@ -251,13 +236,13 @@
"behavior": "Behavior", "behavior": "Behavior",
"download_sources": "Download sources", "download_sources": "Download sources",
"language": "Language", "language": "Language",
"api_token": "API Token", "real_debrid_api_token": "API Token",
"enable_real_debrid": "Enable Real-Debrid", "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 quickly download files, only limited by your internet speed.",
"debrid_invalid_token": "Invalid API token", "real_debrid_invalid_token": "Invalid API token",
"debrid_api_token_hint": "You can get your API token <0>here</0>", "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", "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid",
"debrid_linked_message": "Account \"{{username}}\" linked", "real_debrid_linked_message": "Account \"{{username}}\" linked",
"save_changes": "Save changes", "save_changes": "Save changes",
"changes_saved": "Changes successfully saved", "changes_saved": "Changes successfully saved",
"download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.",
@@ -273,12 +258,6 @@
"download_source_errored": "Errored", "download_source_errored": "Errored",
"sync_download_sources": "Sync sources", "sync_download_sources": "Sync sources",
"removed_download_source": "Download source removed", "removed_download_source": "Download source removed",
"removed_download_sources": "Download sources removed",
"cancel_button_confirmation_delete_all_sources": "No",
"confirm_button_confirmation_delete_all_sources": "Yes, delete everything",
"title_confirmation_delete_all_sources": "Delete all download sources",
"description_confirmation_delete_all_sources": "You will delete all download sources",
"button_delete_all_sources": "Remove all",
"added_download_source": "Added download source", "added_download_source": "Added download source",
"download_sources_synced": "All download sources are synced", "download_sources_synced": "All download sources are synced",
"insert_valid_json_url": "Insert a valid JSON url", "insert_valid_json_url": "Insert a valid JSON url",
@@ -317,45 +296,7 @@
"become_subscriber": "Be Hydra Cloud", "become_subscriber": "Be Hydra Cloud",
"subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renew_cancelled": "Automatic renewal is disabled",
"subscription_renews_on": "Your subscription renews on {{date}}", "subscription_renews_on": "Your subscription renews on {{date}}",
"bill_sent_until": "Your next bill will be sent until this day", "bill_sent_until": "Your next bill will be sent until this day"
"no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.",
"editor_tab_code": "Code",
"editor_tab_info": "Info",
"editor_tab_save": "Save",
"web_store": "Web store",
"clear_themes": "Clear",
"create_theme": "Create",
"create_theme_modal_title": "Create custom theme",
"create_theme_modal_description": "Create a new theme to customize Hydra's appearance",
"theme_name": "Name",
"insert_theme_name": "Insert theme name",
"set_theme": "Set theme",
"unset_theme": "Unset theme",
"delete_theme": "Delete theme",
"edit_theme": "Edit theme",
"delete_all_themes": "Delete all themes",
"delete_all_themes_description": "This will delete all your custom themes",
"delete_theme_description": "This will delete the theme {{theme}}",
"cancel": "Cancel",
"appearance": "Appearance",
"enable_torbox": "Enable Torbox",
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
"torbox_account_linked": "TorBox account linked",
"create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet",
"create_torbox_account": "Click here if you don't have a TorBox account yet",
"real_debrid_account_linked": "Real-Debrid account linked",
"name_min_length": "Theme name must be at least 3 characters long",
"import_theme": "Import theme",
"import_theme_description": "You will import {{theme}} from the theme store",
"error_importing_theme": "Error importing theme",
"theme_imported": "Theme imported successfully",
"enable_friend_request_notifications": "When a friend request is received",
"enable_auto_install": "Download updates automatically",
"common_redist": "Common redistributables",
"common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.",
"install_common_redist": "Install",
"installing_common_redist": "Installing…",
"show_download_speed_in_megabytes": "Show download speed in megabytes per second"
}, },
"notifications": { "notifications": {
"download_complete": "Download complete", "download_complete": "Download complete",
@@ -366,19 +307,13 @@
"new_update_available": "Version {{version}} available", "new_update_available": "Version {{version}} available",
"restart_to_install_update": "Restart Hydra to install the update", "restart_to_install_update": "Restart Hydra to install the update",
"notification_achievement_unlocked_title": "Achievement unlocked for {{game}}", "notification_achievement_unlocked_title": "Achievement unlocked for {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked", "notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked"
"new_friend_request_description": "You have received a new friend request",
"new_friend_request_title": "New friend request",
"extraction_complete": "Extraction complete",
"game_extracted": "{{title}} extracted successfully"
}, },
"system_tray": { "system_tray": {
"open": "Open Hydra", "open": "Open Hydra",
"quit": "Quit" "quit": "Quit"
}, },
"game_card": { "game_card": {
"available_one": "Available",
"available_other": "Available",
"no_downloads": "No downloads available" "no_downloads": "No downloads available"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
@@ -499,7 +434,6 @@
"animated_profile_banner": "Animated profile banner", "animated_profile_banner": "Animated profile banner",
"hydra_cloud": "Hydra Cloud", "hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!", "hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!",
"learn_more": "Learn More", "learn_more": "Learn More"
"debrid_description": "Download up to 4x faster with Nimbus"
} }
} }

View File

@@ -26,8 +26,7 @@
"game_has_no_executable": "El juego no tiene un ejecutable seleccionado", "game_has_no_executable": "El juego no tiene un ejecutable seleccionado",
"sign_in": "Iniciar sesión", "sign_in": "Iniciar sesión",
"friends": "Amigos", "friends": "Amigos",
"need_help": "¿Necesitas ayuda?", "need_help": "¿Necesitas ayuda?"
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Buscar juegos", "search": "Buscar juegos",
@@ -174,26 +173,9 @@
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados", "manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
"select_folder": "Seleccionar carpeta", "select_folder": "Seleccionar carpeta",
"backup_from": "Copia de seguridad de {{date}}", "backup_from": "Copia de seguridad de {{date}}",
"automatic_backup_from": "Copia de seguridad automática de {{date}}",
"enable_automatic_cloud_sync": "Habilitar sincronización automática en la nube",
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad", "custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
"clear": "Limpiar", "clear": "Limpiar",
"no_directory_selected": "No se seleccionó un directorio", "no_directory_selected": "No se seleccionó un directorio"
"launch_options": "Opciones de Inicio",
"launch_options_description": "Los usuarios avanzados pueden introducir sus propias modificaciones de opciones de inicio (característica experimental)",
"launch_options_placeholder": "Sin parámetro específicado",
"no_write_permission": "No se puede descargar en este directorio. Presiona aquí para aprender más.",
"reset_achievements": "Reiniciar logros",
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
"reset_achievements_title": "¿Estás seguro?",
"reset_achievements_success": "Logros reiniciados exitosamente",
"reset_achievements_error": "Se produjo un error al reiniciar los logros",
"download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de Gofile. Por favor espera a que se reinicie la cuota.",
"download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nueva descargas. Por favor, revisa los ajustes de tu cuenta e intenta de nuevo.",
"download_error_not_cached_in_real_debrid": "Esta descarga no está disponible en Real-Debrid y el estado de descarga del sondeo de Real-Debrid aún no está disponible.",
"download_error_not_cached_in_torbox": "Esta descarga no está disponible en Torbox y el estado de descarga del sondeo aún no está disponible.",
"game_added_to_favorites": "Juego añadido a favoritos",
"game_removed_from_favorites": "Juego removido de favoritos"
}, },
"activation": { "activation": {
"title": "Activar Hydra", "title": "Activar Hydra",
@@ -245,13 +227,13 @@
"behavior": "Otros", "behavior": "Otros",
"download_sources": "Fuentes de descarga", "download_sources": "Fuentes de descarga",
"language": "Idioma", "language": "Idioma",
"api_token": "Token API", "real_debrid_api_token": "Token API",
"enable_real_debrid": "Activar Real-Debrid", "enable_real_debrid": "Activar Real-Debrid",
"real_debrid_description": "Real-Debrid es una forma de descargar sin restricciones archivos instantáneamente con la máxima velocidad de tu internet.", "real_debrid_description": "Real-Debrid es una forma de descargar sin restricciones archivos instantáneamente con la máxima velocidad de tu internet.",
"debrid_invalid_token": "Token de API inválido", "real_debrid_invalid_token": "Token de API inválido",
"debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí</0>", "real_debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí</0>",
"real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratuita. Por favor, suscríbete a Real-Debrid", "real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratuita. Por favor, suscríbete a Real-Debrid",
"debrid_linked_message": "Cuenta \"{{username}}\" vinculada", "real_debrid_linked_message": "Cuenta \"{{username}}\" vinculada",
"save_changes": "Guardar cambios", "save_changes": "Guardar cambios",
"changes_saved": "Ajustes guardados exitosamente", "changes_saved": "Ajustes guardados exitosamente",
"download_sources_description": "Hydra buscará los enlaces de descarga de estas fuentes. La URL de origen debe ser un enlace directo a un archivo .json que contenga los enlaces de descarga", "download_sources_description": "Hydra buscará los enlaces de descarga de estas fuentes. La URL de origen debe ser un enlace directo a un archivo .json que contenga los enlaces de descarga",
@@ -267,12 +249,6 @@
"download_source_errored": "Error", "download_source_errored": "Error",
"sync_download_sources": "Sincronizar fuentes", "sync_download_sources": "Sincronizar fuentes",
"removed_download_source": "Fuente de descarga eliminada", "removed_download_source": "Fuente de descarga eliminada",
"cancel_button_confirmation_delete_all_sources": "No",
"confirm_button_confirmation_delete_all_sources": "Sí, eliminar todo",
"description_confirmation_delete_all_sources": "Eliminarás todas las fuentes de descarga",
"title_confirmation_delete_all_sources": "Eliminar todas las fuentes de descarga",
"removed_download_sources": "Fuentes de descarga eliminadas",
"button_delete_all_sources": "Eliminar todas las fuentes de descarga",
"added_download_source": "Fuente de descarga añadida", "added_download_source": "Fuente de descarga añadida",
"download_sources_synced": "Todas las fuentes de descargas están actualizadas.", "download_sources_synced": "Todas las fuentes de descargas están actualizadas.",
"insert_valid_json_url": "Introduce una URL JSON válida", "insert_valid_json_url": "Introduce una URL JSON válida",
@@ -295,54 +271,7 @@
"launch_minimized": "Iniciar Hydra minimizado", "launch_minimized": "Iniciar Hydra minimizado",
"disable_nsfw_alert": "Desactivar alerta NSFW", "disable_nsfw_alert": "Desactivar alerta NSFW",
"seed_after_download_complete": "Realizar seeding después de que se completa la descarga", "seed_after_download_complete": "Realizar seeding después de que se completa la descarga",
"show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos", "show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos"
"account": "Cuenta",
"account_data_updated_successfully": "Datos de la cuenta actualizados",
"bill_sent_until": "Tú próxima factura se enviará el {{date}}",
"current_email": "Correo actual:",
"manage_subscription": "Gestionar suscripción",
"no_email_account": "No has configurado un correo aún",
"no_subscription": "Disfruta Hydra de la mejor manera",
"no_users_blocked": "No tienes usuarios bloqueados",
"renew_subscription": "Renovar Hydra Cloud",
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
"subscription_renew_cancelled": "Está desactivada la renovación automática",
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
"update_email": "Actualizar correo",
"update_password": "Actualizar contraseña",
"appearance": "Apariencia",
"become_subscriber": "Sé Hydra Cloud",
"cancel": "Cancelar",
"clear_themes": "Limpiar",
"create_theme": "Crear",
"create_theme_modal_description": "Crea un nuevo tema para personalizar la apariencia de Hydra",
"create_theme_modal_title": "Crear tema personalizado",
"delete_all_themes": "Eliminar todos los temas",
"delete_all_themes_description": "Esto eliminará todos tus temas personalizados",
"delete_theme": "Eliminar tema",
"delete_theme_description": "Esto eliminará el tema {{theme}}",
"edit_theme": "Editar tema",
"editor_tab_code": "Código",
"editor_tab_info": "Info",
"editor_tab_save": "Guardar",
"enable_torbox": "Habilitar Torbox",
"error_importing_theme": "Error al importar el tema",
"import_theme": "Importar tema",
"import_theme_description": "Vas a importar el tema {{theme}} desde la tienda de temas",
"insert_theme_name": "Introducí el nombre del tema",
"name_min_length": "El tema tiene que tener 3 carácteres de largo mínimo",
"no_themes": "Parece que no tenés ningún tema aún, pero no te preocupes, presiona acá para crear tu primer tema.",
"real_debrid_account_linked": "Cuenta de Real-Debrid vinculada",
"set_theme": "Establecer tema",
"theme_imported": "Tema importado exitosamente",
"theme_name": "Nombre",
"torbox_account_linked": "Cuenta de TorBox vinculada",
"torbox_description": "TorBox es tu servicio premium de seedbox que rivaliza incluso a los mejores servidores del mercado.",
"unset_theme": "Desactivar tema",
"web_store": "Tienda Web",
"enable_friend_request_notifications": "Cuando se recibe una solicitud de amistad",
"enable_auto_install": "Descargar actualizaciones automáticamente"
}, },
"notifications": { "notifications": {
"download_complete": "Descarga completada", "download_complete": "Descarga completada",
@@ -353,17 +282,13 @@
"new_update_available": "Version {{version}} disponible", "new_update_available": "Version {{version}} disponible",
"restart_to_install_update": "Reinicia Hydra para instalar la actualización", "restart_to_install_update": "Reinicia Hydra para instalar la actualización",
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}", "notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados", "notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados"
"new_friend_request_title": "Nueva solicitud de amistad",
"new_friend_request_description": "Has recibido una nueva solicitud de amistad"
}, },
"system_tray": { "system_tray": {
"open": "Abrir Hydra", "open": "Abrir Hydra",
"quit": "Salir" "quit": "Salir"
}, },
"game_card": { "game_card": {
"available_one": "Disponible",
"available_other": "Disponibles",
"no_downloads": "No hay descargas disponibles" "no_downloads": "No hay descargas disponibles"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {

View File

@@ -25,8 +25,7 @@
"queued": "{{title}} (Järjekorras)", "queued": "{{title}} (Järjekorras)",
"game_has_no_executable": "Mängul pole käivitusfaili valitud", "game_has_no_executable": "Mängul pole käivitusfaili valitud",
"sign_in": "Logi sisse", "sign_in": "Logi sisse",
"friends": "Sõbrad", "friends": "Sõbrad"
"favorites": "Lemmikud"
}, },
"header": { "header": {
"search": "Otsi mänge", "search": "Otsi mänge",
@@ -214,13 +213,13 @@
"behavior": "Käitumine", "behavior": "Käitumine",
"download_sources": "Allalaadimise allikad", "download_sources": "Allalaadimise allikad",
"language": "Keel", "language": "Keel",
"api_token": "API Võti", "real_debrid_api_token": "API Võti",
"enable_real_debrid": "Luba Real-Debrid", "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_description": "Real-Debrid on piiranguteta allalaadija, mis võimaldab sul faile alla laadida koheselt ja sinu internetiühenduse parima kiirusega.",
"debrid_invalid_token": "Vigane API võti", "real_debrid_invalid_token": "Vigane API võti",
"debrid_api_token_hint": "Sa saad oma API võtme <0>siit</0>", "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_free_account_error": "Konto \"{{username}}\" on tasuta konto. Palun telli Real-Debrid",
"debrid_linked_message": "Konto \"{{username}}\" ühendatud", "real_debrid_linked_message": "Konto \"{{username}}\" ühendatud",
"save_changes": "Salvesta muudatused", "save_changes": "Salvesta muudatused",
"changes_saved": "Muudatused edukalt salvestatud", "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.", "download_sources_description": "Hydra laeb allalaadimise lingid nendest allikatest. Allika URL peab olema otsene link .json failile, mis sisaldab allalaadimise linke.",
@@ -236,12 +235,6 @@
"download_source_errored": "Vigane", "download_source_errored": "Vigane",
"sync_download_sources": "Sünkroniseeri allikad", "sync_download_sources": "Sünkroniseeri allikad",
"removed_download_source": "Allalaadimise allikas eemaldatud", "removed_download_source": "Allalaadimise allikas eemaldatud",
"cancel_button_confirmation_delete_all_sources": "Ei",
"confirm_button_confirmation_delete_all_sources": "Jah, kustuta kõik",
"description_confirmation_delete_all_sources": "Kustutate kõik allalaadimisallikad",
"title_confirmation_delete_all_sources": "Kustutage kõik allalaadimisallikad",
"removed_download_sources": "Allalaadimise allikas eemaldati",
"button_delete_all_sources": "Eemaldage kõik allalaadimisallikad",
"added_download_source": "Allalaadimise allikas lisatud", "added_download_source": "Allalaadimise allikas lisatud",
"download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud", "download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud",
"insert_valid_json_url": "Sisesta kehtiv JSON url", "insert_valid_json_url": "Sisesta kehtiv JSON url",

View File

@@ -14,10 +14,8 @@
"paused": "{{title}} (متوقف شده)", "paused": "{{title}} (متوقف شده)",
"downloading": "{{title}} ({{percentage}} - در حال دانلود…)", "downloading": "{{title}} ({{percentage}} - در حال دانلود…)",
"filter": "فیلتر کردن کتابخانه", "filter": "فیلتر کردن کتابخانه",
"home": "خانه", "home": "خانه"
"favorites": "علاقه‌مندی‌ها"
}, },
"header": { "header": {
"search": "جستجوی بازی‌ها", "search": "جستجوی بازی‌ها",
"home": "خانه", "home": "خانه",
@@ -111,14 +109,8 @@
"launch_with_system": "زمانی که سیستم روشن می‌شود، هایدرا را باز کن", "launch_with_system": "زمانی که سیستم روشن می‌شود، هایدرا را باز کن",
"general": "کلی", "general": "کلی",
"behavior": "رفتار", "behavior": "رفتار",
"cancel_button_confirmation_delete_all_sources": "خیر",
"confirm_button_confirmation_delete_all_sources": "ہاں، سب کچھ حذف کر دیں۔",
"description_confirmation_delete_all_sources": "آپ ڈاؤن لوڈ کے تمام ذرائع کو حذف کر دیں گے۔",
"title_confirmation_delete_all_sources": "تمام منابع دانلود را حذف کنید",
"removed_download_sources": "منابع دانلود حذف شد",
"button_delete_all_sources": "تمام منابع دانلود را حذف کنید",
"enable_real_debrid": "فعال‌سازی Real-Debrid", "enable_real_debrid": "فعال‌سازی Real-Debrid",
"debrid_api_token_hint": "کلید API خود را از <ب0>اینجا</0> بگیرید.", "real_debrid_api_token_hint": "کلید API خود را از <ب0>اینجا</0> بگیرید.",
"save_changes": "ذخیره تغییرات" "save_changes": "ذخیره تغییرات"
}, },
"notifications": { "notifications": {

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (En pause)", "paused": "{{title}} (En pause)",
"downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)", "downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)",
"filter": "Filtrer la bibliothèque", "filter": "Filtrer la bibliothèque",
"home": "Page daccueil", "home": "Page daccueil"
"favorites": "Favoris"
}, },
"header": { "header": {
"search": "Recherche", "search": "Recherche",
"catalogue": "Catalogue", "catalogue": "Catalogue",
"downloads": "Téléchargements", "downloads": "Téléchargements",
"search_results": "Résultats de la recherche", "search_results": "Résultats de la recherche",
@@ -99,13 +97,7 @@
"notifications": "Notifications", "notifications": "Notifications",
"enable_download_notifications": "Quand un téléchargement est terminé", "enable_download_notifications": "Quand un téléchargement est terminé",
"enable_repack_list_notifications": "Quand un nouveau repack est ajouté", "enable_repack_list_notifications": "Quand un nouveau repack est ajouté",
"language": "Langue", "language": "Langue"
"cancel_button_confirmation_delete_all_sources": "Non",
"confirm_button_confirmation_delete_all_sources": "Oui, tout supprimer",
"description_confirmation_delete_all_sources": "Vous supprimerez toutes les sources de téléchargement",
"title_confirmation_delete_all_sources": "Supprimer toutes les sources de téléchargement",
"removed_download_sources": "Sources de téléchargement supprimées",
"button_delete_all_sources": "Supprimer toutes les sources de téléchargement"
}, },
"notifications": { "notifications": {
"download_complete": "Téléchargement terminé", "download_complete": "Téléchargement terminé",

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (Szünet)", "paused": "{{title}} (Szünet)",
"downloading": "{{title}} ({{percentage}} - Letöltés…)", "downloading": "{{title}} ({{percentage}} - Letöltés…)",
"filter": "Könyvtár szűrése", "filter": "Könyvtár szűrése",
"home": "Főoldal", "home": "Főoldal"
"favorites": "Kedvenc játékok"
}, },
"header": { "header": {
"search": "Keresés", "search": "Keresés",
"home": "Főoldal", "home": "Főoldal",
"catalogue": "Katalógus", "catalogue": "Katalógus",
"downloads": "Letöltések", "downloads": "Letöltések",
@@ -106,12 +104,6 @@
"change": "Frissítés", "change": "Frissítés",
"notifications": "Értesítések", "notifications": "Értesítések",
"enable_download_notifications": "Amikor egy letöltés befejeződik", "enable_download_notifications": "Amikor egy letöltés befejeződik",
"cancel_button_confirmation_delete_all_sources": "Nem",
"confirm_button_confirmation_delete_all_sources": "Igen, törölj mindent",
"description_confirmation_delete_all_sources": "Törölni fog minden letöltési forrást",
"title_confirmation_delete_all_sources": "Törölje az összes letöltési forrást",
"removed_download_sources": "Betűtípusok eltávolítva",
"button_delete_all_sources": "Távolítsa el az összes letöltési forrást",
"enable_repack_list_notifications": "Amikor egy új repack hozzáadásra kerül" "enable_repack_list_notifications": "Amikor egy új repack hozzáadásra kerül"
}, },
"notifications": { "notifications": {

View File

@@ -20,12 +20,10 @@
"home": "Beranda", "home": "Beranda",
"queued": "{{title}} (Antrian)", "queued": "{{title}} (Antrian)",
"game_has_no_executable": "Game tidak punya file eksekusi yang dipilih", "game_has_no_executable": "Game tidak punya file eksekusi yang dipilih",
"sign_in": "Masuk", "sign_in": "Masuk"
"favorites": "Favorit"
}, },
"header": { "header": {
"search": "Cari game", "search": "Cari game",
"home": "Beranda", "home": "Beranda",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Unduhan", "downloads": "Unduhan",
@@ -163,24 +161,18 @@
"behavior": "Perilaku", "behavior": "Perilaku",
"download_sources": "Sumber unduhan", "download_sources": "Sumber unduhan",
"language": "Bahasa", "language": "Bahasa",
"api_token": "Token API", "real_debrid_api_token": "Token API",
"enable_real_debrid": "Aktifkan Real-Debrid", "enable_real_debrid": "Aktifkan Real-Debrid",
"real_debrid_description": "Real-Debrid adalah downloader tanpa batas yang memungkinkan kamu untuk mengunduh file dengan cepat dan pada kecepatan terbaik dari Internet kamu.", "real_debrid_description": "Real-Debrid adalah downloader tanpa batas yang memungkinkan kamu untuk mengunduh file dengan cepat dan pada kecepatan terbaik dari Internet kamu.",
"debrid_invalid_token": "Token API tidak valid", "real_debrid_invalid_token": "Token API tidak valid",
"debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini</0>", "real_debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini</0>",
"real_debrid_free_account_error": "Akun \"{{username}}\" adalah akun gratis. Silakan berlangganan Real-Debrid", "real_debrid_free_account_error": "Akun \"{{username}}\" adalah akun gratis. Silakan berlangganan Real-Debrid",
"debrid_linked_message": "Akun \"{{username}}\" terhubung", "real_debrid_linked_message": "Akun \"{{username}}\" terhubung",
"save_changes": "Simpan perubahan", "save_changes": "Simpan perubahan",
"changes_saved": "Perubahan disimpan berhasil", "changes_saved": "Perubahan disimpan berhasil",
"download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.", "download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.",
"validate_download_source": "Validasi", "validate_download_source": "Validasi",
"remove_download_source": "Hapus", "remove_download_source": "Hapus",
"cancel_button_confirmation_delete_all_sources": "TIDAK",
"confirm_button_confirmation_delete_all_sources": "Ya, hapus semuanya",
"description_confirmation_delete_all_sources": "Anda akan menghapus semua sumber unduhan",
"title_confirmation_delete_all_sources": "Hapus semua sumber unduhan",
"removed_download_sources": "Font dihapus",
"button_delete_all_sources": "Hapus semua sumber unduhan",
"add_download_source": "Tambahkan sumber", "add_download_source": "Tambahkan sumber",
"download_count_zero": "Tidak ada unduhan dalam daftar", "download_count_zero": "Tidak ada unduhan dalam daftar",
"download_count_one": "{{countFormatted}} unduhan dalam daftar", "download_count_one": "{{countFormatted}} unduhan dalam daftar",

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (In pausa)", "paused": "{{title}} (In pausa)",
"downloading": "{{title}} ({{percentage}} - Download…)", "downloading": "{{title}} ({{percentage}} - Download…)",
"filter": "Filtra libreria", "filter": "Filtra libreria",
"home": "Home", "home": "Home"
"favorites": "Preferiti"
}, },
"header": { "header": {
"search": "Cerca", "search": "Cerca",
"home": "Home", "home": "Home",
"catalogue": "Catalogo", "catalogue": "Catalogo",
"downloads": "Download", "downloads": "Download",
@@ -120,13 +118,7 @@
"general": "Generale", "general": "Generale",
"behavior": "Comportamento", "behavior": "Comportamento",
"enable_real_debrid": "Abilita Real Debrid", "enable_real_debrid": "Abilita Real Debrid",
"cancel_button_confirmation_delete_all_sources": "NO", "real_debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here</0>",
"confirm_button_confirmation_delete_all_sources": "Sì, cancella tutto",
"description_confirmation_delete_all_sources": "Eliminerai tutte le fonti di download",
"title_confirmation_delete_all_sources": "Elimina tutte le fonti di download",
"removed_download_sources": "Fonti di download rimosse",
"button_delete_all_sources": "Rimuovi tutte le fonti di download",
"debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here</0>",
"save_changes": "Salva modifiche" "save_changes": "Salva modifiche"
}, },
"notifications": { "notifications": {

View File

@@ -20,10 +20,8 @@
"home": "Басты бет", "home": "Басты бет",
"queued": "{{title}} (Кезекте)", "queued": "{{title}} (Кезекте)",
"game_has_no_executable": "Ойынды іске қосу файлы таңдалмаған", "game_has_no_executable": "Ойынды іске қосу файлы таңдалмаған",
"sign_in": "Кіру", "sign_in": "Кіру"
"favorites": "Таңдаулылар"
}, },
"header": { "header": {
"search": "Іздеу", "search": "Іздеу",
"home": "Басты бет", "home": "Басты бет",
@@ -161,24 +159,18 @@
"behavior": "Мінез-құлық", "behavior": "Мінез-құлық",
"download_sources": "Жүктеу көздері", "download_sources": "Жүктеу көздері",
"language": "Тіл", "language": "Тіл",
"api_token": "API Кілті", "real_debrid_api_token": "API Кілті",
"enable_real_debrid": "Real-Debrid-ті қосу", "enable_real_debrid": "Real-Debrid-ті қосу",
"real_debrid_description": "Real-Debrid - бұл шектеусіз жүктеуші, ол интернетте орналастырылған файлдарды тез жүктеуге немесе жеке желі арқылы кез келген блоктарды айналып өтіп, оларды бірден плеерге беруге мүмкіндік береді.", "real_debrid_description": "Real-Debrid - бұл шектеусіз жүктеуші, ол интернетте орналастырылған файлдарды тез жүктеуге немесе жеке желі арқылы кез келген блоктарды айналып өтіп, оларды бірден плеерге беруге мүмкіндік береді.",
"debrid_invalid_token": "Қате API кілті", "real_debrid_invalid_token": "Қате API кілті",
"debrid_api_token_hint": "API кілтін <0>осы жерден</0> алуға болады", "real_debrid_api_token_hint": "API кілтін <0>осы жерден</0> алуға болады",
"real_debrid_free_account_error": "\"{{username}}\" аккаунты жазылымға ие емес. Real-Debrid жазылымын алыңыз", "real_debrid_free_account_error": "\"{{username}}\" аккаунты жазылымға ие емес. Real-Debrid жазылымын алыңыз",
"debrid_linked_message": "\"{{username}}\" аккаунты байланған", "real_debrid_linked_message": "\"{{username}}\" аккаунты байланған",
"save_changes": "Өзгерістерді сақтау", "save_changes": "Өзгерістерді сақтау",
"changes_saved": "Өзгерістер сәтті сақталды", "changes_saved": "Өзгерістер сәтті сақталды",
"download_sources_description": "Hydra осы көздерден жүктеу сілтемелерін алады. URL-да жүктеу сілтемелері бар .json файлына тікелей сілтеме болуы керек.", "download_sources_description": "Hydra осы көздерден жүктеу сілтемелерін алады. URL-да жүктеу сілтемелері бар .json файлына тікелей сілтеме болуы керек.",
"validate_download_source": "Тексеру", "validate_download_source": "Тексеру",
"remove_download_source": "Жою", "remove_download_source": "Жою",
"cancel_button_confirmation_delete_all_sources": "Жоқ",
"confirm_button_confirmation_delete_all_sources": "Иә, бәрін жойыңыз",
"description_confirmation_delete_all_sources": "Барлық жүктеу көздерін жоясыз",
"title_confirmation_delete_all_sources": "Барлық жүктеу көздерін жойыңыз",
"removed_download_sources": "Қаріптер жойылды",
"button_delete_all_sources": "Барлық жүктеу көздерін жойыңыз",
"add_download_source": "Жүктеу көзін қосу", "add_download_source": "Жүктеу көзін қосу",
"download_count_zero": "Жүктеулер тізімінде жоқ", "download_count_zero": "Жүктеулер тізімінде жоқ",
"download_count_one": "{{countFormatted}} жүктеу тізімде", "download_count_one": "{{countFormatted}} жүктеу тізімде",

View File

@@ -14,10 +14,8 @@
"paused": "{{title}} (일시 정지됨)", "paused": "{{title}} (일시 정지됨)",
"downloading": "{{title}} ({{percentage}} - 다운로드 중…)", "downloading": "{{title}} ({{percentage}} - 다운로드 중…)",
"filter": "라이브러리 정렬", "filter": "라이브러리 정렬",
"home": "홈", "home": "홈"
"favorites": "즐겨찾기"
}, },
"header": { "header": {
"search": "게임 검색하기", "search": "게임 검색하기",
"home": "홈", "home": "홈",
@@ -111,14 +109,8 @@
"launch_with_system": "컴퓨터가 시작되었을 때 Hydra 실행", "launch_with_system": "컴퓨터가 시작되었을 때 Hydra 실행",
"general": "일반", "general": "일반",
"behavior": "행동", "behavior": "행동",
"cancel_button_confirmation_delete_all_sources": "아니요",
"confirm_button_confirmation_delete_all_sources": "네, 모두 삭제합니다",
"description_confirmation_delete_all_sources": "모든 다운로드 소스를 삭제합니다.",
"title_confirmation_delete_all_sources": "모든 다운로드 소스 삭제",
"removed_download_sources": "제거된 글꼴",
"button_delete_all_sources": "모든 다운로드 소스 제거",
"enable_real_debrid": "Real-Debrid 활성화", "enable_real_debrid": "Real-Debrid 활성화",
"debrid_api_token_hint": "API 키를 <0>이곳</0>에서 얻으세요.", "real_debrid_api_token_hint": "API 키를 <0>이곳</0>에서 얻으세요.",
"save_changes": "변경 사항 저장" "save_changes": "변경 사항 저장"
}, },
"notifications": { "notifications": {

View File

@@ -24,12 +24,10 @@
"queued": "{{title}} (I køen)", "queued": "{{title}} (I køen)",
"game_has_no_executable": "Spillet har ikke noen kjørbar fil valgt", "game_has_no_executable": "Spillet har ikke noen kjørbar fil valgt",
"sign_in": "Logge inn", "sign_in": "Logge inn",
"friends": "Venner", "friends": "Venner"
"favorites": "Favoritter"
}, },
"header": { "header": {
"search": "Søk efter spill", "search": "Søk efter spill",
"home": "Hjem", "home": "Hjem",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Nedlastinger", "downloads": "Nedlastinger",
@@ -179,13 +177,13 @@
"behavior": "Oppførsel", "behavior": "Oppførsel",
"download_sources": "Nedlastingskilder", "download_sources": "Nedlastingskilder",
"language": "Språk", "language": "Språk",
"api_token": "API nøkkel", "real_debrid_api_token": "API nøkkel",
"enable_real_debrid": "Slå på Real-Debrid", "enable_real_debrid": "Slå på Real-Debrid",
"real_debrid_description": "Real-Debrid er en ubegrenset nedlaster som gør det mulig for deg å laste ned filer med en gang og med den beste utnyttelsen av internethastigheten din.", "real_debrid_description": "Real-Debrid er en ubegrenset nedlaster som gør det mulig for deg å laste ned filer med en gang og med den beste utnyttelsen av internethastigheten din.",
"debrid_invalid_token": "Ugyldig API nøkkel", "real_debrid_invalid_token": "Ugyldig API nøkkel",
"debrid_api_token_hint": "Du kan få API nøkkelen din <0>her</0>", "real_debrid_api_token_hint": "Du kan få API nøkkelen din <0>her</0>",
"real_debrid_free_account_error": "Brukeren \"{{username}}\" er en gratis bruker. Vennligst abboner på Real-Debrid", "real_debrid_free_account_error": "Brukeren \"{{username}}\" er en gratis bruker. Vennligst abboner på Real-Debrid",
"debrid_linked_message": "Brukeren \"{{username}}\" er forbunnet", "real_debrid_linked_message": "Brukeren \"{{username}}\" er forbunnet",
"save_changes": "Lagre endringer", "save_changes": "Lagre endringer",
"changes_saved": "Lagring av endringer vellykket", "changes_saved": "Lagring av endringer vellykket",
"download_sources_description": "Hydra vil hente nedlastingslenker fra disse kildene. Kilde URLen skal være en direkte lenke til en .json fil som inneholder nedlastingslenkene.", "download_sources_description": "Hydra vil hente nedlastingslenker fra disse kildene. Kilde URLen skal være en direkte lenke til en .json fil som inneholder nedlastingslenkene.",
@@ -201,12 +199,6 @@
"download_source_errored": "Mislyktes", "download_source_errored": "Mislyktes",
"sync_download_sources": "Synkroniser kilder", "sync_download_sources": "Synkroniser kilder",
"removed_download_source": "Nedlastingskilde fjernet", "removed_download_source": "Nedlastingskilde fjernet",
"cancel_button_confirmation_delete_all_sources": "Ingen",
"confirm_button_confirmation_delete_all_sources": "Ja, slett alt",
"description_confirmation_delete_all_sources": "Du vil slette alle nedlastingskilder",
"title_confirmation_delete_all_sources": "Slett alle nedlastingskilder",
"removed_download_sources": "Fonter fjernet",
"button_delete_all_sources": "Fjern alle nedlastingskilder",
"added_download_source": "La til Nedlastingskilde", "added_download_source": "La til Nedlastingskilde",
"download_sources_synced": "Alle nedlastingskilder er synkroniserte", "download_sources_synced": "Alle nedlastingskilder er synkroniserte",
"insert_valid_json_url": "Innsett en gyldig JSON url", "insert_valid_json_url": "Innsett en gyldig JSON url",

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (Gepauzeerd)", "paused": "{{title}} (Gepauzeerd)",
"downloading": "{{title}} ({{percentage}} - Downloading…)", "downloading": "{{title}} ({{percentage}} - Downloading…)",
"filter": "Filter Bibliotheek", "filter": "Filter Bibliotheek",
"home": "Home", "home": "Home"
"favorites": "Favorieten"
}, },
"header": { "header": {
"search": "Zoek spellen", "search": "Zoek spellen",
"home": "Home", "home": "Home",
"catalogue": "Bibliotheek", "catalogue": "Bibliotheek",
"downloads": "Downloads", "downloads": "Downloads",
@@ -112,14 +110,8 @@
"launch_with_system": "Start Hydra bij het opstarten van het systeem", "launch_with_system": "Start Hydra bij het opstarten van het systeem",
"general": "Algemeen", "general": "Algemeen",
"behavior": "Gedrag", "behavior": "Gedrag",
"cancel_button_confirmation_delete_all_sources": "Nee",
"confirm_button_confirmation_delete_all_sources": "Ja, verwijder alles",
"description_confirmation_delete_all_sources": "Je verwijdert alle downloadbronnen",
"title_confirmation_delete_all_sources": "Verwijder alle downloadbronnen",
"removed_download_sources": "Downloadbronnen verwijderd",
"button_delete_all_sources": "Verwijder alle downloadbronnen",
"enable_real_debrid": "Enable Real-Debrid", "enable_real_debrid": "Enable Real-Debrid",
"debrid_api_token_hint": "U kunt uw API-sleutel <0>hier</0> verkrijgen.", "real_debrid_api_token_hint": "U kunt uw API-sleutel <0>hier</0> verkrijgen.",
"save_changes": "Wijzigingen opslaan" "save_changes": "Wijzigingen opslaan"
}, },
"notifications": { "notifications": {

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (Zatrzymano)", "paused": "{{title}} (Zatrzymano)",
"downloading": "{{title}} ({{percentage}} - Pobieranie…)", "downloading": "{{title}} ({{percentage}} - Pobieranie…)",
"filter": "Filtruj biblioteke", "filter": "Filtruj biblioteke",
"home": "Główna", "home": "Główna"
"favorites": "Ulubione"
}, },
"header": { "header": {
"search": "Szukaj", "search": "Szukaj",
"home": "Główna", "home": "Główna",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Pobrane", "downloads": "Pobrane",
@@ -120,13 +118,8 @@
"general": "Ogólne", "general": "Ogólne",
"behavior": "Zachowania", "behavior": "Zachowania",
"language": "Język", "language": "Język",
"cancel_button_confirmation_delete_all_sources": "NIE",
"confirm_button_confirmation_delete_all_sources": "Tak, usuń wszystko",
"description_confirmation_delete_all_sources": "Usuniesz wszystkie źródła pobierania",
"title_confirmation_delete_all_sources": "Usuń wszystkie źródła pobierania",
"button_delete_all_sources": "Usuń wszystkie źródła pobierania",
"enable_real_debrid": "Włącz Real-Debrid", "enable_real_debrid": "Włącz Real-Debrid",
"debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj</0>", "real_debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj</0>",
"save_changes": "Zapisz zmiany" "save_changes": "Zapisz zmiany"
}, },
"notifications": { "notifications": {

View File

@@ -26,8 +26,7 @@
"game_has_no_executable": "Jogo não possui executável selecionado", "game_has_no_executable": "Jogo não possui executável selecionado",
"sign_in": "Login", "sign_in": "Login",
"friends": "Amigos", "friends": "Amigos",
"need_help": "Precisa de ajuda?", "need_help": "Precisa de ajuda?"
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Buscar jogos", "search": "Buscar jogos",
@@ -44,10 +43,7 @@
"downloading_metadata": "Baixando metadados de {{title}}…", "downloading_metadata": "Baixando metadados de {{title}}…",
"downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}", "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
"calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…", "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…",
"checking_files": "Verificando arquivos de {{title}}…", "checking_files": "Verificando arquivos de {{title}}…"
"installing_common_redist": "{{log}}…",
"installation_complete": "Instalação concluída",
"installation_complete_message": "Componentes recomendados instalados com sucesso"
}, },
"game_details": { "game_details": {
"open_download_options": "Ver opções de download", "open_download_options": "Ver opções de download",
@@ -167,8 +163,6 @@
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", "max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
"achievements_not_sync": "Veja como exibir suas conquistas no perfil", "achievements_not_sync": "Veja como exibir suas conquistas no perfil",
"backup_from": "Backup de {{date}}", "backup_from": "Backup de {{date}}",
"automatic_backup_from": "Backup automático de {{date}}",
"enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem",
"custom_backup_location_set": "Localização customizada selecionada", "custom_backup_location_set": "Localização customizada selecionada",
"select_folder": "Selecione a pasta", "select_folder": "Selecione a pasta",
"manage_files_description": "Gerencie quais arquivos serão feitos backup", "manage_files_description": "Gerencie quais arquivos serão feitos backup",
@@ -178,15 +172,7 @@
"reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}", "reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}",
"reset_achievements_title": "Tem certeza?", "reset_achievements_title": "Tem certeza?",
"reset_achievements_success": "Conquistas resetadas com sucesso", "reset_achievements_success": "Conquistas resetadas com sucesso",
"reset_achievements_error": "Falha ao resetar conquistas", "reset_achievements_error": "Falha ao resetar conquistas"
"no_write_permission": "Não é possível baixar nesse diretório. Clique aqui para saber mais.",
"download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.",
"download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.",
"download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
"download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível.",
"game_removed_from_favorites": "Jogo removido dos favoritos",
"game_added_to_favorites": "Jogo adicionado aos favoritos",
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados"
}, },
"activation": { "activation": {
"title": "Ativação", "title": "Ativação",
@@ -223,9 +209,7 @@
"seeding": "Semeando", "seeding": "Semeando",
"stop_seeding": "Parar de semear", "stop_seeding": "Parar de semear",
"resume_seeding": "Semear", "resume_seeding": "Semear",
"options": "Gerenciar", "options": "Gerenciar"
"extract": "Extrair arquivos",
"extracting": "Extraindo arquivos…"
}, },
"settings": { "settings": {
"downloads_path": "Diretório dos downloads", "downloads_path": "Diretório dos downloads",
@@ -240,13 +224,13 @@
"behavior": "Comportamento", "behavior": "Comportamento",
"download_sources": "Fontes de download", "download_sources": "Fontes de download",
"language": "Idioma", "language": "Idioma",
"api_token": "Token de API", "real_debrid_api_token": "Token de API",
"enable_real_debrid": "Habilitar Real-Debrid", "enable_real_debrid": "Habilitar Real-Debrid",
"debrid_api_token_hint": "Você pode obter seu token de API <0>aqui</0>", "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui</0>",
"real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.",
"debrid_invalid_token": "Token de API inválido", "real_debrid_invalid_token": "Token de API inválido",
"real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid",
"debrid_linked_message": "Conta \"{{username}}\" vinculada", "real_debrid_linked_message": "Conta \"{{username}}\" vinculada",
"save_changes": "Salvar mudanças", "save_changes": "Salvar mudanças",
"changes_saved": "Ajustes salvos com sucesso", "changes_saved": "Ajustes salvos com sucesso",
"download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.",
@@ -262,12 +246,6 @@
"download_source_errored": "Falhou", "download_source_errored": "Falhou",
"sync_download_sources": "Sincronizar", "sync_download_sources": "Sincronizar",
"removed_download_source": "Fonte removida", "removed_download_source": "Fonte removida",
"removed_download_sources": "Fontes removidas",
"cancel_button_confirmation_delete_all_sources": "Não",
"confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo",
"title_confirmation_delete_all_sources": "Remover todas as fontes de download",
"description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?",
"button_delete_all_sources": "Remover todas",
"added_download_source": "Fonte adicionada", "added_download_source": "Fonte adicionada",
"download_sources_synced": "As fontes foram sincronizadas", "download_sources_synced": "As fontes foram sincronizadas",
"insert_valid_json_url": "Insira a url de um JSON válido", "insert_valid_json_url": "Insira a url de um JSON válido",
@@ -306,43 +284,7 @@
"become_subscriber": "Seja Hydra Cloud", "become_subscriber": "Seja Hydra Cloud",
"subscription_renew_cancelled": "A renovação automática está desativada", "subscription_renew_cancelled": "A renovação automática está desativada",
"subscription_renews_on": "Sua assinatura renova dia {{date}}", "subscription_renews_on": "Sua assinatura renova dia {{date}}",
"bill_sent_until": "Sua próxima cobrança será enviada até esse dia", "bill_sent_until": "Sua próxima cobrança será enviada até esse dia"
"no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.",
"editor_tab_save": "Salvar",
"web_store": "Loja de temas",
"clear_themes": "Limpar",
"create_theme": "Criar",
"create_theme_modal_title": "Criar tema customizado",
"create_theme_modal_description": "Criar novo tema para customizar a aparência do Hydra",
"theme_name": "Nome",
"insert_theme_name": "Insira o nome do tema",
"set_theme": "Habilitar tema",
"unset_theme": "Desabilitar tema",
"delete_theme": "Deletar tema",
"edit_theme": "Editar tema",
"delete_all_themes": "Deletar todos os temas",
"delete_all_themes_description": "Isso irá deletar todos os seus temas",
"delete_theme_description": "Isso irá deletar o tema {{theme}}",
"cancel": "Cancelar",
"appearance": "Aparência",
"enable_torbox": "Habilitar Torbox",
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
"torbox_account_linked": "Conta do TorBox vinculada",
"create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid",
"create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox",
"real_debrid_account_linked": "Conta Real-Debrid associada",
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
"import_theme": "Importar tema",
"import_theme_description": "Você irá importar {{theme}} da loja de temas",
"error_importing_theme": "Erro ao importar tema",
"theme_imported": "Tema importado com sucesso",
"enable_friend_request_notifications": "Quando um pedido de amizade é recebido",
"enable_auto_install": "Baixar atualizações automaticamente",
"common_redist": "Componentes recomendados",
"common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.",
"install_common_redist": "Instalar",
"installing_common_redist": "Instalando…",
"show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo"
}, },
"notifications": { "notifications": {
"download_complete": "Download concluído", "download_complete": "Download concluído",
@@ -351,19 +293,13 @@
"repack_count_one": "{{count}} novo repack", "repack_count_one": "{{count}} novo repack",
"repack_count_other": "{{count}} novos repacks", "repack_count_other": "{{count}} novos repacks",
"new_update_available": "Versão {{version}} disponível", "new_update_available": "Versão {{version}} disponível",
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão", "restart_to_install_update": "Reinicie o Hydra para instalar a nova versão"
"new_friend_request_title": "Novo pedido de amizade",
"new_friend_request_description": "Você recebeu um novo pedido de amizade",
"extraction_complete": "Extração concluída",
"game_extracted": "{{title}} extraído com sucesso"
}, },
"system_tray": { "system_tray": {
"open": "Abrir Hydra", "open": "Abrir Hydra",
"quit": "Fechar" "quit": "Fechar"
}, },
"game_card": { "game_card": {
"available_one": "Disponível",
"available_other": "Disponíveis",
"no_downloads": "Sem downloads disponíveis" "no_downloads": "Sem downloads disponíveis"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
@@ -494,7 +430,6 @@
"animated_profile_banner": "Banner animado no perfil", "animated_profile_banner": "Banner animado no perfil",
"cloud_saving": "Saves de jogos em nuvem", "cloud_saving": "Saves de jogos em nuvem",
"hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!", "hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!",
"learn_more": "Saiba mais", "learn_more": "Saiba mais"
"debrid_description": "Baixe até 4x mais rápido com Nimbus"
} }
} }

View File

@@ -25,8 +25,7 @@
"queued": "{{title}} (Na fila)", "queued": "{{title}} (Na fila)",
"game_has_no_executable": "O jogo não tem um executável selecionado", "game_has_no_executable": "O jogo não tem um executável selecionado",
"sign_in": "Iniciar sessão", "sign_in": "Iniciar sessão",
"friends": "Amigos", "friends": "Amigos"
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Procurar jogos", "search": "Procurar jogos",
@@ -102,7 +101,7 @@
"open_download_location": "Ver ficheiros transferidos", "open_download_location": "Ver ficheiros transferidos",
"create_shortcut": "Criar atalho no ambiente de trabalho", "create_shortcut": "Criar atalho no ambiente de trabalho",
"remove_files": "Remover ficheiros", "remove_files": "Remover ficheiros",
"options": "Opções", "options": "Gerir",
"remove_from_library_description": "Isto vai remover {{game}} da tua biblioteca", "remove_from_library_description": "Isto vai remover {{game}} da tua biblioteca",
"remove_from_library_title": "Tens a certeza?", "remove_from_library_title": "Tens a certeza?",
"executable_section_title": "Executável", "executable_section_title": "Executável",
@@ -158,27 +157,7 @@
"no_download_option_info": "Sem informações disponíveis", "no_download_option_info": "Sem informações disponíveis",
"backup_deletion_failed": "Falha ao apagar o backup", "backup_deletion_failed": "Falha ao apagar o backup",
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", "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", "achievements_not_sync": "As tuas conquistas não estão sincronizadas"
"backup_from": "Backup de {{date}}",
"automatic_backup_from": "Backup automático de {{date}}",
"enable_automatic_cloud_sync": "Ativar sincronização automática na nuvem",
"custom_backup_location_set": "Localização customizada selecionada",
"select_folder": "Selecione a pasta",
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
"clear": "Limpar",
"no_directory_selected": "Nenhum diretório selecionado",
"reset_achievements": "Repor conquistas",
"reset_achievements_description": "Isto irá apagar todas as conquistas de {{game}}",
"reset_achievements_title": "Tem certeza?",
"reset_achievements_success": "Conquistas repostas com sucesso",
"reset_achievements_error": "Falha ao repor conquistas",
"no_write_permission": "Não é possível descarregar neste diretório. Clique aqui para saber mais.",
"download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde o reset da cota.",
"download_error_real_debrid_account_not_authorized": "A sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique a sua assinatura e tente novamente.",
"download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
"download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível.",
"game_removed_from_favorites": "Jogo removido dos favoritos",
"game_added_to_favorites": "Jogo adicionado aos favoritos"
}, },
"activation": { "activation": {
"title": "Ativação", "title": "Ativação",
@@ -211,11 +190,7 @@
"queued": "Na fila", "queued": "Na fila",
"no_downloads_title": "Nada por aqui…", "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 descarregaste nada pelo Hydra, mas nunca é tarde para começar.",
"checking_files": "A verificar ficheiros…", "checking_files": "A verificar ficheiros…"
"seeding": "A semear",
"stop_seeding": "Parar de semear",
"resume_seeding": "Semear",
"options": "Opções"
}, },
"settings": { "settings": {
"downloads_path": "Local das transferências", "downloads_path": "Local das transferências",
@@ -230,13 +205,13 @@
"behavior": "Comportamento", "behavior": "Comportamento",
"download_sources": "Fontes de transferência", "download_sources": "Fontes de transferência",
"language": "Idioma", "language": "Idioma",
"api_token": "Token de API", "real_debrid_api_token": "Token de API",
"enable_real_debrid": "Ativar Real-Debrid", "enable_real_debrid": "Ativar Real-Debrid",
"debrid_api_token_hint": "Podes obter o teu token de API <0>aqui</0>", "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_description": "O Real-Debrid é um downloader sem restrições que permite descarregar ficheiros instantaneamente e com a melhor velocidade da tua Internet.",
"debrid_invalid_token": "Token de API inválido", "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_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, subscreve o Real-Debrid",
"debrid_linked_message": "Conta \"{{username}}\" associada", "real_debrid_linked_message": "Conta \"{{username}}\" associada",
"save_changes": "Guardar alterações", "save_changes": "Guardar alterações",
"changes_saved": "Alterações guardadas com sucesso", "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.", "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.",
@@ -255,12 +230,6 @@
"download_source_errored": "Falhou", "download_source_errored": "Falhou",
"sync_download_sources": "Sincronizar", "sync_download_sources": "Sincronizar",
"removed_download_source": "Fonte removida", "removed_download_source": "Fonte removida",
"cancel_button_confirmation_delete_all_sources": "Não",
"confirm_button_confirmation_delete_all_sources": "Sim, apague tudo",
"description_confirmation_delete_all_sources": "Irá remover todas as fontes de download",
"title_confirmation_delete_all_sources": "Remover todas as fontes de download",
"removed_download_sources": "Fontes de download removidas",
"button_delete_all_sources": "Remover todas",
"added_download_source": "Fonte adicionada", "added_download_source": "Fonte adicionada",
"download_sources_synced": "As fontes foram sincronizadas", "download_sources_synced": "As fontes foram sincronizadas",
"insert_valid_json_url": "Insere o URL de um JSON válido", "insert_valid_json_url": "Insere o URL de um JSON válido",
@@ -279,57 +248,7 @@
"must_be_valid_url": "A fonte deve ser um URL válido", "must_be_valid_url": "A fonte deve ser um URL válido",
"blocked_users": "Utilizadores bloqueados", "blocked_users": "Utilizadores bloqueados",
"user_unblocked": "Utilizador desbloqueado", "user_unblocked": "Utilizador desbloqueado",
"enable_achievement_notifications": "Quando uma conquista é desbloqueada", "enable_achievement_notifications": "Quando uma conquista é desbloqueada"
"enable_friend_request_notifications": "Quando um pedido de amizade é recebido",
"launch_minimized": "Iniciar Hydra minimizado",
"disable_nsfw_alert": "Desativar alertas NSFW",
"seed_after_download_complete": "Semear após concluir o download",
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de as desbloquear",
"account": "Conta",
"no_users_blocked": "Não tem utilizadores bloqueados",
"subscription_active_until": "O teu Hydra Cloud está ativo até {{date}}",
"manage_subscription": "Gerir subscrições",
"update_email": "Atualizar email",
"update_password": "Atualizar password",
"current_email": "Email atual:",
"no_email_account": "Ainda não adicionou nenhum email à sua conta",
"account_data_updated_successfully": "Dados da conta atualizados com sucesso",
"renew_subscription": "Renovar Hydra Cloud",
"subscription_expired_at": "A sua subscrição expirou a {{date}}",
"no_subscription": "Aproveite o Hydra da melhor forma possível",
"become_subscriber": "Subscreva o Hydra Cloud",
"subscription_renew_cancelled": "A renovação automática está desativada",
"subscription_renews_on": "Sua assinatura renova dia {{date}}",
"bill_sent_until": "A próxima cobrança será enviada até esse dia",
"no_themes": "Parece que ainda não tem nenhum tema. Não se preocupe, clique aqui para criar a sua primeira obra de arte.",
"editor_tab_code": "Código",
"editor_tab_info": "Info",
"editor_tab_save": "Guardar",
"web_store": "Loja de temas",
"clear_themes": "Limpar",
"create_theme": "Criar",
"create_theme_modal_title": "Criar tema customizado",
"create_theme_modal_description": "Criar um novo tema para customizar a aparência do Hydra",
"theme_name": "Nome",
"insert_theme_name": "Insira o nome do tema",
"set_theme": "Definir tema",
"unset_theme": "Redefinir tema",
"delete_theme": "Apagar tema",
"edit_theme": "Editar tema",
"delete_all_themes": "Apagar todos os temas",
"delete_all_themes_description": "Isto irá apagar todos os seus temas",
"delete_theme_description": "Isto irá apagar o tema {{theme}}",
"cancel": "Cancelar",
"appearance": "Aparência",
"enable_torbox": "Ativar Torbox",
"torbox_description": "TorBox é um serviço de seedbox premium sendo um dos melhores servidores do mercado.",
"torbox_account_linked": "Conta do TorBox associada",
"real_debrid_account_linked": "Conta Real-Debrid associada",
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
"import_theme": "Importar tema",
"import_theme_description": "Irá importar {{theme}} da loja de temas",
"error_importing_theme": "Erro ao importar tema",
"theme_imported": "Tema importado com sucesso"
}, },
"notifications": { "notifications": {
"download_complete": "Transferência concluída", "download_complete": "Transferência concluída",
@@ -338,17 +257,13 @@
"repack_count_one": "{{count}} novo repack", "repack_count_one": "{{count}} novo repack",
"repack_count_other": "{{count}} novos repacks", "repack_count_other": "{{count}} novos repacks",
"new_update_available": "Versão {{version}} disponível", "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": "Reinicia o Hydra para instalar a nova versão"
"new_friend_request_title": "Novo pedido de amizade",
"new_friend_request_description": "Recebeste um novo pedido de amizade"
}, },
"system_tray": { "system_tray": {
"open": "Abrir o Hydra", "open": "Abrir o Hydra",
"quit": "Sair" "quit": "Sair"
}, },
"game_card": { "game_card": {
"available_one": "Disponível",
"available_other": "Disponíveis",
"no_downloads": "Sem downloads disponíveis" "no_downloads": "Sem downloads disponíveis"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
@@ -358,16 +273,7 @@
}, },
"catalogue": { "catalogue": {
"next_page": "Página seguinte", "next_page": "Página seguinte",
"previous_page": "Página anterior", "previous_page": "Página anterior"
"search": "Filtrar…",
"developers": "Desenvolvedores",
"genres": "Géneros",
"tags": "Marcadores",
"publishers": "Distribuidoras",
"download_sources": "Fontes de download",
"result_count": "{{resultCount}} resultados",
"filter_count": "{{filterCount}} disponíveis",
"clear_filters": "Limpar {{filterCount}} selecionados"
}, },
"modal": { "modal": {
"close": "Botão de fechar" "close": "Botão de fechar"
@@ -444,17 +350,7 @@
"profile_reported": "Perfil denunciado", "profile_reported": "Perfil denunciado",
"your_friend_code": "O teu código de amigo:", "your_friend_code": "O teu código de amigo:",
"upload_banner": "Fazer upload do banner", "upload_banner": "Fazer upload do banner",
"uploading_banner": "A fazer upload do banner…", "uploading_banner": "A fazer upload do banner…"
"background_image_updated": "Imagem de fundo salva",
"stats": "Estatísticas",
"achievements": "conquistas",
"games": "Jogos",
"ranking_updated_weekly": "O ranking é atualizado semanalmente",
"playing": "A jogar {{game}}",
"achievements_unlocked": "Conquistas desbloqueadas",
"earned_points": "Pontos ganhos",
"show_achievements_on_profile": "Mostre as suas conquistas no perfil",
"show_points_on_profile": "Mostre os seus pontos ganhos no perfil"
}, },
"achievement": { "achievement": {
"achievement_unlocked": "Conquista desbloqueada", "achievement_unlocked": "Conquista desbloqueada",
@@ -462,25 +358,15 @@
"user_achievements": "Conquistas de {{displayName}}", "user_achievements": "Conquistas de {{displayName}}",
"unlocked_at": "Desbloqueada em: {{date}}", "unlocked_at": "Desbloqueada em: {{date}}",
"subscription_needed": "Precisas de uma subscrição Hydra Cloud para visualizar este conteúdo", "subscription_needed": "Precisas de uma subscrição Hydra Cloud para visualizar este conteúdo",
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos"
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}",
"hidden_achievement_tooltip": "Esta é uma conquista oculta",
"achievement_earn_points": "Ganhe {{points}} pontos com essa conquista",
"earned_points": "Pontos ganhos:",
"available_points": "Pontos disponíveis:",
"how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?"
}, },
"hydra_cloud": { "hydra_cloud": {
"subscription_tour_title": "Subscrição Hydra Cloud", "subscription_tour_title": "Subscrição Hydra Cloud",
"subscribe_now": "Subscreve agora", "subscribe_now": "Subscreve agora",
"cloud_achievements": "Gravação de conquistas na nuvem", "cloud_achievements": "Gravação de conquistas na nuvem",
"animated_profile_picture": "Fotos de perfil animadas", "animated_profile_picture": "Fotos de perfil animadas",
"premium_support": "Suporte Premium", "premium_support": "Apoio Premium",
"show_and_compare_achievements": "Mostra e compara as tuas conquistas com as de outros utilizadores", "show_and_compare_achievements": "Mostra e compara as tuas conquistas com as de outros utilizadores",
"animated_profile_banner": "Banner animado no perfil", "animated_profile_banner": "Banner animado no perfil"
"cloud_saving": "Progresso dos jogos na nuvem",
"hydra_cloud_feature_found": "Descubriste uma funcionalidade Hydra Cloud!",
"learn_more": "Saber mais"
} }
} }

View File

@@ -14,12 +14,10 @@
"paused": "{{title}} (Pauzat)", "paused": "{{title}} (Pauzat)",
"downloading": "{{title}} ({{percentage}} - Se descarcă...)", "downloading": "{{title}} ({{percentage}} - Se descarcă...)",
"filter": "Filtrează biblioteca", "filter": "Filtrează biblioteca",
"home": "Acasă", "home": "Acasă"
"favorites": "Favorite"
}, },
"header": { "header": {
"search": "Caută jocuri", "search": "Caută jocuri",
"home": "Acasă", "home": "Acasă",
"catalogue": "Catalog", "catalogue": "Catalog",
"downloads": "Descărcări", "downloads": "Descărcări",
@@ -126,19 +124,13 @@
"general": "General", "general": "General",
"behavior": "Comportament", "behavior": "Comportament",
"language": "Limbă", "language": "Limbă",
"api_token": "Token API", "real_debrid_api_token": "Token API",
"cancel_button_confirmation_delete_all_sources": "Nu",
"confirm_button_confirmation_delete_all_sources": "Da, șterge totul",
"description_confirmation_delete_all_sources": "Veți șterge toate sursele de descărcare",
"title_confirmation_delete_all_sources": "Ștergeți toate sursele de descărcare",
"removed_download_sources": "Sursele de descărcare au fost eliminate",
"button_delete_all_sources": "Eliminați toate sursele de descărcare",
"enable_real_debrid": "Activează Real-Debrid", "enable_real_debrid": "Activează Real-Debrid",
"real_debrid_description": "Real-Debrid este un descărcător fără restricții care îți permite să descarci fișiere instantaneu și la cea mai bună viteză a internetului tău.", "real_debrid_description": "Real-Debrid este un descărcător fără restricții care îți permite să descarci fișiere instantaneu și la cea mai bună viteză a internetului tău.",
"debrid_invalid_token": "Token API invalid", "real_debrid_invalid_token": "Token API invalid",
"debrid_api_token_hint": "Poți obține token-ul tău API <0>aici</0>", "real_debrid_api_token_hint": "Poți obține token-ul tău API <0>aici</0>",
"real_debrid_free_account_error": "Contul \"{{username}}\" este un cont gratuit. Te rugăm să te abonezi la Real-Debrid", "real_debrid_free_account_error": "Contul \"{{username}}\" este un cont gratuit. Te rugăm să te abonezi la Real-Debrid",
"debrid_linked_message": "Contul \"{{username}}\" a fost legat", "real_debrid_linked_message": "Contul \"{{username}}\" a fost legat",
"save_changes": "Salvează modificările", "save_changes": "Salvează modificările",
"changes_saved": "Modificările au fost salvate cu succes" "changes_saved": "Modificările au fost salvate cu succes"
}, },

View File

@@ -10,7 +10,7 @@
"hot": "Сейчас популярно", "hot": "Сейчас популярно",
"start_typing": "Начинаю вводить текст...", "start_typing": "Начинаю вводить текст...",
"weekly": "📅 Лучшие игры недели", "weekly": "📅 Лучшие игры недели",
"achievements": "🏆 Игры с достижениями" "achievements": "🏆 Игры, в которых нужно победить"
}, },
"sidebar": { "sidebar": {
"catalogue": "Каталог", "catalogue": "Каталог",
@@ -26,8 +26,7 @@
"game_has_no_executable": "Файл запуска игры не выбран", "game_has_no_executable": "Файл запуска игры не выбран",
"sign_in": "Войти", "sign_in": "Войти",
"friends": "Друзья", "friends": "Друзья",
"need_help": "Нужна помощь?", "need_help": "Нужна помощь?"
"favorites": "Избранное"
}, },
"header": { "header": {
"search": "Поиск", "search": "Поиск",
@@ -36,7 +35,7 @@
"downloads": "Загрузки", "downloads": "Загрузки",
"search_results": "Результаты поиска", "search_results": "Результаты поиска",
"settings": "Настройки", "settings": "Настройки",
"version_available_install": "Доступна версия {{version}}. Нажмите здесь для установки.", "version_available_install": "Доступна версия {{version}}. Нажмите здесь для перезапуска и установки.",
"version_available_download": "Доступна версия {{version}}. Нажмите здесь для загрузки." "version_available_download": "Доступна версия {{version}}. Нажмите здесь для загрузки."
}, },
"bottom_panel": { "bottom_panel": {
@@ -44,16 +43,13 @@
"downloading_metadata": "Загрузка метаданных {{title}}…", "downloading_metadata": "Загрузка метаданных {{title}}…",
"downloading": "Загрузка {{title}}… ({{percentage}} завершено) - Окончание {{eta}} - {{speed}}", "downloading": "Загрузка {{title}}… ({{percentage}} завершено) - Окончание {{eta}} - {{speed}}",
"calculating_eta": "Загрузка {{title}}… ({{percentage}} завершено) - Подсчёт оставшегося времени…", "calculating_eta": "Загрузка {{title}}… ({{percentage}} завершено) - Подсчёт оставшегося времени…",
"checking_files": "Проверка файлов {{title}}… ({{percentage}} завершено)", "checking_files": "Проверка файлов {{title}}… ({{percentage}} завершено)"
"installing_common_redist": "{{log}}…",
"installation_complete": "Установка завершена",
"installation_complete_message": "Библиотеки успешно установлены"
}, },
"catalogue": { "catalogue": {
"search": "Фильтр…", "search": "Фильтр…",
"developers": "Разработчики", "developers": "Разработчики",
"genres": "Жанры", "genres": "Жанры",
"tags": "Теги", "tags": "Маркеры",
"publishers": "Издательства", "publishers": "Издательства",
"download_sources": "Источники загрузки", "download_sources": "Источники загрузки",
"result_count": "{{resultCount}} результатов", "result_count": "{{resultCount}} результатов",
@@ -181,23 +177,12 @@
"manage_files_description": "Управляйте файлами, которые будут сохраняться и восстанавливаться", "manage_files_description": "Управляйте файлами, которые будут сохраняться и восстанавливаться",
"select_folder": "Выбрать папку", "select_folder": "Выбрать папку",
"backup_from": "Резервная копия от {{date}}", "backup_from": "Резервная копия от {{date}}",
"automatic_backup_from": "Автоматическая резервная копия от {{date}}",
"enable_automatic_cloud_sync": "Включить автоматическую синхронизацию в облаке",
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии", "custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии",
"no_directory_selected": "Не выбран каталог", "no_directory_selected": "Не выбран каталог",
"no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.", "no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.",
"reset_achievements": "Сброс достижений",
"reset_achievements_description": "Это сбросит все достижения для {{game}}",
"reset_achievements_title": "Вы уверены?", "reset_achievements_title": "Вы уверены?",
"reset_achievements_success": "Достижения успешно сброшены", "reset_achievements_success": "Достижения успешно сброшены",
"reset_achievements_error": "Не удалось сбросить достижения", "reset_achievements_error": "Не удалось сбросить достижения"
"download_error_gofile_quota_exceeded": "Вы превысили месячную квоту Gofile. Пожалуйста, подождите, пока квота не будет восстановлена.",
"download_error_real_debrid_account_not_authorized": "Ваш аккаунт Real-Debrid не авторизован для осуществления новых загрузок. Пожалуйста, проверьте настройки учетной записи и повторите попытку.",
"download_error_not_cached_in_real_debrid": "Эта загрузка недоступна на Real-Debrid, и получение статуса загрузки с Real-Debrid пока недоступно.",
"download_error_not_cached_in_torbox": "Эта загрузка недоступна на Torbox, и получить статус загрузки с Torbox пока невозможно.",
"game_added_to_favorites": "Игра добавлена в избранное",
"game_removed_from_favorites": "Игра удалена из избранного",
"automatically_extract_downloaded_files": "Автоматическая распаковка загруженных файлов"
}, },
"activation": { "activation": {
"title": "Активировать Hydra", "title": "Активировать Hydra",
@@ -234,9 +219,7 @@
"seeding": "Раздача", "seeding": "Раздача",
"stop_seeding": "Остановить раздачу", "stop_seeding": "Остановить раздачу",
"resume_seeding": "Продолжить раздачу", "resume_seeding": "Продолжить раздачу",
"options": "Управлять", "options": "Управлять"
"extract": "Распаковать файлы",
"extracting": "Распаковка файлов…"
}, },
"settings": { "settings": {
"downloads_path": "Путь загрузок", "downloads_path": "Путь загрузок",
@@ -254,13 +237,13 @@
"behavior": "Поведение", "behavior": "Поведение",
"download_sources": "Источники загрузки", "download_sources": "Источники загрузки",
"language": "Язык", "language": "Язык",
"api_token": "API Ключ", "real_debrid_api_token": "API Ключ",
"enable_real_debrid": "Включить Real-Debrid", "enable_real_debrid": "Включить Real-Debrid",
"real_debrid_description": "Real-Debrid - это неограниченный загрузчик, который позволяет быстро скачивать файлы, размещенные в Интернете, или мгновенно передавать их в плеер через частную сеть, позволяющую обходить любые блокировки.", "real_debrid_description": "Real-Debrid - это неограниченный загрузчик, который позволяет быстро скачивать файлы, размещенные в Интернете, или мгновенно передавать их в плеер через частную сеть, позволяющую обходить любые блокировки.",
"debrid_invalid_token": "Неверный API ключ", "real_debrid_invalid_token": "Неверный API ключ",
"debrid_api_token_hint": "API ключ можно получить <0>здесь</0>", "real_debrid_api_token_hint": "API ключ можно получить <0>здесь</0>",
"real_debrid_free_account_error": "Аккаунт \"{{username}}\" - не имеет подписки. Пожалуйста, оформите подписку на Real-Debrid", "real_debrid_free_account_error": "Аккаунт \"{{username}}\" - не имеет подписки. Пожалуйста, оформите подписку на Real-Debrid",
"debrid_linked_message": "Привязан аккаунт \"{{username}}\"", "real_debrid_linked_message": "Привязан аккаунт \"{{username}}\"",
"save_changes": "Сохранить изменения", "save_changes": "Сохранить изменения",
"changes_saved": "Изменения успешно сохранены", "changes_saved": "Изменения успешно сохранены",
"download_sources_description": "Hydra будет получать ссылки на загрузки из этих источников. URL должна содержать прямую ссылку на .json-файл с ссылками для загрузок.", "download_sources_description": "Hydra будет получать ссылки на загрузки из этих источников. URL должна содержать прямую ссылку на .json-файл с ссылками для загрузок.",
@@ -275,15 +258,9 @@
"download_source_up_to_date": "Обновлён", "download_source_up_to_date": "Обновлён",
"download_source_errored": "Ошибка", "download_source_errored": "Ошибка",
"sync_download_sources": "Обновить источники", "sync_download_sources": "Обновить источники",
"removed_download_source": "Источник удален", "removed_download_source": "Источник загрузок удален",
"cancel_button_confirmation_delete_all_sources": "Нет", "added_download_source": "Источник загрузок добавлен",
"confirm_button_confirmation_delete_all_sources": "Да, удалить все", "download_sources_synced": "Все источники загрузок синхронизированы",
"description_confirmation_delete_all_sources": "Вы удалите все источники",
"title_confirmation_delete_all_sources": "Удалить все источники",
"removed_download_sources": "Источники удалены",
"button_delete_all_sources": "Удалить все источники",
"added_download_source": "Источник добавлен",
"download_sources_synced": "Все источники обновлены",
"insert_valid_json_url": "Вставьте действительный URL JSON-файла", "insert_valid_json_url": "Вставьте действительный URL JSON-файла",
"found_download_option_zero": "Не найдено вариантов загрузки", "found_download_option_zero": "Не найдено вариантов загрузки",
"found_download_option_one": "Найден {{countFormatted}} вариант загрузки", "found_download_option_one": "Найден {{countFormatted}} вариант загрузки",
@@ -291,7 +268,7 @@
"import": "Импортировать", "import": "Импортировать",
"blocked_users": "Заблокированные пользователи", "blocked_users": "Заблокированные пользователи",
"friends_only": "Только для друзей", "friends_only": "Только для друзей",
"must_be_valid_url": "У источника должен быть правильный URL", "must_be_valid_url": "Источник должен быть действительным URL-адресом.",
"privacy": "Конфиденциальность", "privacy": "Конфиденциальность",
"private": "Частный", "private": "Частный",
"profile_visibility": "Видимость профиля", "profile_visibility": "Видимость профиля",
@@ -317,45 +294,7 @@
"become_subscriber": "Станьте обладателем Hydra Cloud", "become_subscriber": "Станьте обладателем Hydra Cloud",
"subscription_renew_cancelled": "Автоматическое продление отключено", "subscription_renew_cancelled": "Автоматическое продление отключено",
"subscription_renews_on": "Ваша подписка продлевается на {{date}}", "subscription_renews_on": "Ваша подписка продлевается на {{date}}",
"bill_sent_until": "Ваш следующий счет будет отправлен до этого дня", "bill_sent_until": "Ваш следующий счет будет отправлен до этого дня"
"no_themes": "Похоже, что у вас еще нет тем, но не волнуйтесь, нажмите здесь, чтобы создать свой первый шедевр",
"editor_tab_code": "Код",
"editor_tab_info": "Информация",
"editor_tab_save": "Сохранить",
"web_store": "Веб-магазин",
"clear_themes": "Очистить",
"create_theme": "Создать",
"create_theme_modal_title": "Создать пользовательскую тему",
"create_theme_modal_description": "Создать новую тему для настройки внешнего вида Hydra",
"theme_name": "Название",
"insert_theme_name": "Вставить название темы",
"set_theme": "Установить тему",
"unset_theme": "Снять тему",
"delete_theme": "Удалить тему",
"edit_theme": "Редактировать тему",
"delete_all_themes": "Удалить все темы",
"delete_all_themes_description": "Это удалит все ваши пользовательские темы",
"delete_theme_description": "Это приведет к удалению темы {{theme}}",
"cancel": "Отменить",
"appearance": "Внешний вид",
"enable_torbox": "Включить Torbox",
"torbox_description": "TorBox - это ваш премиум-сервис, конкурирующий даже с лучшими серверами на рынке.",
"torbox_account_linked": "Аккаунт TorBox привязан",
"real_debrid_account_linked": "Аккаунт Real-Debrid привязан",
"create_real_debrid_account": "Нажмите здесь, если у вас еще нет аккаунта Real-Debrid",
"create_torbox_account": "Нажмите здесь, если у вас еще нет учетной записи TorBox",
"name_min_length": "Название темы должно содержать не менее 3 символов",
"import_theme": "Импортировать тему",
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
"error_importing_theme": "Ошибка при импорте темы",
"theme_imported": "Тема успешно импортирована",
"enable_friend_request_notifications": "При получении запроса на добавление в друзья",
"enable_auto_install": "Загружать обновления автоматически",
"common_redist": "Библиотеки",
"common_redist_description": "Для запуска некоторых игр требуются библиотеки. Во избежание проблем рекомендуется установить их.",
"install_common_redist": "Установить",
"installing_common_redist": "Установка…",
"show_download_speed_in_megabytes": "Показать скорость загрузки в мегабайтах в секунду"
}, },
"notifications": { "notifications": {
"download_complete": "Загрузка завершена", "download_complete": "Загрузка завершена",
@@ -366,19 +305,13 @@
"new_update_available": "Доступна новая версия {{version}}", "new_update_available": "Доступна новая версия {{version}}",
"restart_to_install_update": "Перезапустите Hydra для установки обновления", "restart_to_install_update": "Перезапустите Hydra для установки обновления",
"notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}", "notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}",
"notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}", "notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}"
"new_friend_request_title": "Новый запрос на добавление в друзья",
"new_friend_request_description": "Вы получили новый запрос на добавление в друзья",
"extraction_complete": "Распаковка завершена",
"game_extracted": "{{title}} успешно распакован"
}, },
"system_tray": { "system_tray": {
"open": "Открыть Hydra", "open": "Открыть Hydra",
"quit": "Выйти" "quit": "Выйти"
}, },
"game_card": { "game_card": {
"available_one": "Доступный",
"available_other": "Доступный",
"no_downloads": "Нет доступных источников" "no_downloads": "Нет доступных источников"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
@@ -463,13 +396,11 @@
"uploading_banner": "Загрузка баннера...", "uploading_banner": "Загрузка баннера...",
"background_image_updated": "Фоновое изображение обновлено", "background_image_updated": "Фоновое изображение обновлено",
"stats": "Статистика", "stats": "Статистика",
"achievements": "Достижения",
"games": "Игры", "games": "Игры",
"top_percentile": "Топ {{percentile}}%", "top_percentile": "Топ {{percentile}}%",
"ranking_updated_weekly": "Рейтинг обновляется еженедельно", "ranking_updated_weekly": "Рейтинг обновляется еженедельно",
"playing": "Играет в {{game}}", "playing": "Играет в {{game}}",
"achievements_unlocked": "Достижения разблокированы", "achievements_unlocked": "Достижения разблокированы",
"earned_points": "Заработано очков:",
"show_achievements_on_profile": "Покажите свои достижения в профиле", "show_achievements_on_profile": "Покажите свои достижения в профиле",
"show_points_on_profile": "Показывать заработанные очки в своем профиле" "show_points_on_profile": "Показывать заработанные очки в своем профиле"
}, },
@@ -499,7 +430,6 @@
"animated_profile_banner": "Анимированный баннер профиля", "animated_profile_banner": "Анимированный баннер профиля",
"hydra_cloud": "Hydra Cloud", "hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "Вы только что открыли для себя функцию Hydra Cloud!", "hydra_cloud_feature_found": "Вы только что открыли для себя функцию Hydra Cloud!",
"learn_more": "Подробнее", "learn_more": "Подробнее"
"debrid_description": "Скачивайте в 4 раза быстрее с Nimbus"
} }
} }

View File

@@ -16,7 +16,7 @@
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "İndirilenler", "downloads": "İndirilenler",
"settings": "Ayarlar", "settings": "Ayarlar",
"my_library": "Kütüphane", "my_library": "Kütüphanem",
"downloading_metadata": "{{title}} (Meta verileri indiriliyor…)", "downloading_metadata": "{{title}} (Meta verileri indiriliyor…)",
"paused": "{{title}} (Durduruldu)", "paused": "{{title}} (Durduruldu)",
"downloading": "{{title}} ({{percentage}} - İndiriliyor…)", "downloading": "{{title}} ({{percentage}} - İndiriliyor…)",
@@ -26,8 +26,7 @@
"game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi", "game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi",
"sign_in": "Giriş yap", "sign_in": "Giriş yap",
"friends": "Arkadaşlar", "friends": "Arkadaşlar",
"need_help": "Yardıma mı ihtiyacınız var?", "need_help": "Yardıma mı ihtiyacınız var?"
"favorites": "Favoriler"
}, },
"header": { "header": {
"search": "Oyunları ara", "search": "Oyunları ara",
@@ -36,18 +35,15 @@
"downloads": "İndirilenler", "downloads": "İndirilenler",
"search_results": "Arama sonuçları", "search_results": "Arama sonuçları",
"settings": "Ayarlar", "settings": "Ayarlar",
"version_available_install": "{{version}} sürümü mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.", "version_available_install": "Sürüm {{version}} mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.",
"version_available_download": "{{version}} sürümü mevcut. İndirmek için buraya tıklayın." "version_available_download": "Sürüm {{version}} mevcut. İndirmek için buraya tıklayın."
}, },
"bottom_panel": { "bottom_panel": {
"no_downloads_in_progress": "Devam eden indirme yok", "no_downloads_in_progress": "Devam eden indirme yok",
"downloading_metadata": "{{title}} meta verileri indiriliyor…", "downloading_metadata": "{{title}} meta verileri indiriliyor…",
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlanma: {{eta}} - Hız: {{speed}}", "downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlama: {{eta}} - Hız: {{speed}}",
"calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…", "calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…",
"checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)", "checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)"
"installing_common_redist": "{{log}}…",
"installation_complete": "İndirme tamamlandı",
"installation_complete_message": "Genel bağımlılıklar başarıyla yüklendi."
}, },
"catalogue": { "catalogue": {
"search": "Filtrele…", "search": "Filtrele…",
@@ -119,14 +115,14 @@
"clear": "Temizle", "clear": "Temizle",
"remove_files": "Dosyaları kaldır", "remove_files": "Dosyaları kaldır",
"remove_from_library_title": "Emin misiniz?", "remove_from_library_title": "Emin misiniz?",
"remove_from_library_description": "Bu işlem sonrasında {{game}} oyunu kütüphanenizden kaldıracaktır", "remove_from_library_description": "Bu işlem {{game}} oyununu kütüphanenizden kaldıracaktır",
"options": "Seçenekler", "options": "Seçenekler",
"executable_section_title": "Çalıştırılabilir dosya", "executable_section_title": "Çalıştırılabilir dosya",
"executable_section_description": "\"Oyna\" butonuna tıklandığında çalıştırılacak dosyanın yolu", "executable_section_description": "\"Oyna\" tıklandığında çalıştırılacak dosyanın yolu",
"downloads_secion_title": "İndirmeler", "downloads_secion_title": "İndirmeler",
"downloads_section_description": "Bu oyun için güncellemeleri veya diğer sürümleri kontrol edin", "downloads_section_description": "Bu oyun için güncellemeleri veya diğer sürümleri kontrol edin",
"danger_zone_section_title": "Tehlike bölgesi", "danger_zone_section_title": "Tehlike bölgesi",
"danger_zone_section_description": "Bu oyunu kütüphanenizden kaldırın veya Hydra tarafından indirilen dosyaları silin.", "danger_zone_section_description": "Bu oyunu kütüphanenizden veya Hydra tarafından indirilen dosyaları kaldırın",
"download_in_progress": "İndirme devam ediyor", "download_in_progress": "İndirme devam ediyor",
"download_paused": "İndirme durduruldu", "download_paused": "İndirme durduruldu",
"last_downloaded_option": "Son indirilen seçenek", "last_downloaded_option": "Son indirilen seçenek",
@@ -144,8 +140,8 @@
"executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya", "executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya",
"warning": "Uyarı:", "warning": "Uyarı:",
"hydra_needs_to_remain_open": "Bu indirmenin tamamlanması için Hydra açık kalmalıdır. Eğer Hydra kapanırsa, ilerleme kaydedilmez.", "hydra_needs_to_remain_open": "Bu indirmenin tamamlanması için Hydra açık kalmalıdır. Eğer Hydra kapanırsa, ilerleme kaydedilmez.",
"achievements": "Başarımlar", "achievements": "Başarılar",
"achievements_count": "Başarımlar {{unlockedCount}}/{{achievementsCount}}", "achievements_count": "Başarılar {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Bulut kaydı", "cloud_save": "Bulut kaydı",
"cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin", "cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin",
"backups": "Yedekler", "backups": "Yedekler",
@@ -153,15 +149,15 @@
"delete_backup": "Sil", "delete_backup": "Sil",
"create_backup": "Yeni yedek oluştur", "create_backup": "Yeni yedek oluştur",
"last_backup_date": "{{date}} tarihindeki son yedek", "last_backup_date": "{{date}} tarihindeki son yedek",
"no_backup_preview": "Bu oyun için bir kayıt dosyası bulunamadı", "no_backup_preview": "Bu oyun için kayıtlı oyun bulunamadı",
"restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…", "restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…",
"uploading_backup": "Yedek yükleniyor…", "uploading_backup": "Yedek yükleniyor…",
"no_backups": "Bu oyun için henüz bir yedek oluşturmadınız", "no_backups": "Bu oyun için henüz bir yedek oluşturmadınız",
"backup_uploaded": "Yedek yüklendi", "backup_uploaded": "Yedek yüklendi",
"backup_deleted": "Yedek silindi", "backup_deleted": "Yedek silindi",
"backup_restored": "Yedek geri yüklendi", "backup_restored": "Yedek geri yüklendi",
"see_all_achievements": "Tüm başarımları gör", "see_all_achievements": "Tüm başarıları gör",
"sign_in_to_see_achievements": "Başarımları görmek için oturum açın", "sign_in_to_see_achievements": "Başarıları görmek için giriş yapın",
"mapping_method_automatic": "Otomatik", "mapping_method_automatic": "Otomatik",
"mapping_method_manual": "Manuel", "mapping_method_manual": "Manuel",
"mapping_method_label": "Eşleme yöntemi", "mapping_method_label": "Eşleme yöntemi",
@@ -172,38 +168,29 @@
"wine_prefix": "Wine Prefix", "wine_prefix": "Wine Prefix",
"wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix", "wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix",
"launch_options": "Başlatma Seçenekleri", "launch_options": "Başlatma Seçenekleri",
"launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine parametreler girebilir (deneysel özellik)", "launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine değişiklikler girebilir (deneysel özellik)",
"launch_options_placeholder": "Belirtilen bir parametre yok", "launch_options_placeholder": "Belirtilen bir parametre yok",
"no_download_option_info": "Bilgi mevcut değil", "no_download_option_info": "Bilgi mevcut değil",
"backup_deletion_failed": "Yedek silinemedi", "backup_deletion_failed": "Yedek silinemedi",
"max_number_of_artifacts_reached": "Bu oyun için maksimum yedek sayısına ulaşıldı", "max_number_of_artifacts_reached": "Bu oyun için maksimum yedek sayısına ulaşıldı",
"achievements_not_sync": "Başarımlarınızı senkronize etmeyi öğrenin", "achievements_not_sync": "Başarılarınızı senkronize etmeyi öğrenin",
"manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin", "manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin",
"select_folder": "Klasör seç", "select_folder": "Klasör seç",
"backup_from": "{{date}} tarihinden yedek", "backup_from": "{{date}} tarihinden yedek",
"automatic_backup_from": "{{date}} tarihinden otomatik kayıt",
"enable_automatic_cloud_sync": "Otomatik bulut kaydı senkronizasyonunu aktifleştir",
"custom_backup_location_set": "Özel yedekleme konumu ayarlandı", "custom_backup_location_set": "Özel yedekleme konumu ayarlandı",
"no_directory_selected": "Bir dizin seçilmedi", "no_directory_selected": "Bir dizin seçilmedi",
"no_write_permission": "Bu dizine indirme yapılamaz. Daha fazla bilgi için buraya tıklayın.", "no_write_permission": "Bu dizine indirme yapılamaz. Daha fazla bilgi için buraya tıklayın.",
"reset_achievements": "Başarımları sıfırla", "reset_achievements": "Başarıları sıfırla",
"reset_achievements_description": "Bu işlem {{game}} için tüm başarımları sıfırlar", "reset_achievements_description": "Bu işlem {{game}} için tüm başarıları sıfırlar",
"reset_achievements_title": "Emin misiniz?", "reset_achievements_title": "Emin misiniz?",
"reset_achievements_success": "Başarımlar başarıyla sıfırlandı", "reset_achievements_success": "Başarılar başarıyla sıfırlandı",
"reset_achievements_error": "Başarımlar sıfırlanamadı", "reset_achievements_error": "Başarılar sıfırlanamadı"
"download_error_gofile_quota_exceeded": "Gofile aylık kotanızı doldurdunuz. Kotanın yenilenmesini bekleyin.",
"download_error_real_debrid_account_not_authorized": "Real-Debrid hesabınız yeni indirme işlemleri yapmak için yetkilendirilmemiş. Lütfen hesap ayarlarınızı kontrol edip tekrar deneyin.",
"download_error_not_cached_in_real_debrid": "Bu indirme Real-Debrid üzerinde mevcut değil ve Real-Debrid'den indirme durumu henüz sorgulanamıyor.",
"download_error_not_cached_in_torbox": "Bu indirme Torbox'ta mevcut değil ve Torbox'tan indirme durumu henüz sorgulanamıyor.",
"game_removed_from_favorites": "Oyun favorilerden silindi",
"game_added_to_favorites": "Oyun favorilere eklendi",
"automatically_extract_downloaded_files": "Yüklenmiş dosyaları otomatik olarak çıkart"
}, },
"activation": { "activation": {
"title": "Hydra'yı Aktive Et", "title": "Hydra'yı Aktive Et",
"installation_id": "Kurulum Kimliği:", "installation_id": "Kurulum Kimliği:",
"enter_activation_code": "Aktivasyon kodunuzu girin", "enter_activation_code": "Aktivasyon kodunuzu girin",
"message": "Bunu nasıl edineceğini bilmiyorsan, buna sahip olmamalısın.", "message": "Bunu nereden soracağınızı bilmiyorsanız, bu sizin için olmamalı.",
"activate": "Aktive Et", "activate": "Aktive Et",
"loading": "Yükleniyor…" "loading": "Yükleniyor…"
}, },
@@ -218,7 +205,7 @@
"cancel": "İptal Et", "cancel": "İptal Et",
"filter": "İndirilen oyunları filtrele", "filter": "İndirilen oyunları filtrele",
"remove": "Kaldır", "remove": "Kaldır",
"downloading_metadata": "Meta verileri indiriliyor…", "downloading_metadata": "Metadata indiriliyor…",
"deleting": "Yükleyici siliniyor…", "deleting": "Yükleyici siliniyor…",
"delete": "Yükleyiciyi kaldır", "delete": "Yükleyiciyi kaldır",
"delete_modal_title": "Emin misiniz?", "delete_modal_title": "Emin misiniz?",
@@ -234,9 +221,7 @@
"seeding": "Paylaşılıyor", "seeding": "Paylaşılıyor",
"stop_seeding": "Paylaşımı durdur", "stop_seeding": "Paylaşımı durdur",
"resume_seeding": "Paylaşımı sürdür", "resume_seeding": "Paylaşımı sürdür",
"options": "Yönet", "options": "Yönet"
"extract": "Dosyaları çıkart",
"extracting": "Dosyalar çıkartılıyor…"
}, },
"settings": { "settings": {
"downloads_path": "İndirme yolu", "downloads_path": "İndirme yolu",
@@ -245,31 +230,25 @@
"enable_download_notifications": "Bir indirme tamamlandığında", "enable_download_notifications": "Bir indirme tamamlandığında",
"enable_repack_list_notifications": "Yeni bir repack eklendiğinde", "enable_repack_list_notifications": "Yeni bir repack eklendiğinde",
"real_debrid_api_token_label": "Real-Debrid API anahtarı", "real_debrid_api_token_label": "Real-Debrid API anahtarı",
"quit_app_instead_hiding": "Hydra'yı kapatınca sistem tepsisine gitmesin", "quit_app_instead_hiding": "Hydra'yı kapatırken gizlemeyin",
"launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır", "launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır",
"general": "Genel", "general": "Genel",
"behavior": "Davranış", "behavior": "Davranış",
"download_sources": "İndirme kaynakları", "download_sources": "İndirme kaynakları",
"language": "Dil", "language": "Dil",
"api_token": "API Anahtarı", "real_debrid_api_token": "API Anahtarı",
"enable_real_debrid": "Real-Debrid'i Etkinleştir", "enable_real_debrid": "Real-Debrid'i Etkinleştir",
"real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak hızlı dosya indirmenizi sağlayan sınırsız bir indirici.", "real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak hızlı dosya indirmenizi sağlayan sınırsız bir indirici.",
"debrid_invalid_token": "Geçersiz API anahtarı", "real_debrid_invalid_token": "Geçersiz API anahtarı",
"debrid_api_token_hint": "API anahtarınızı <0>buradan</0> alabilirsiniz", "real_debrid_api_token_hint": "API anahtarınızı <0>buradan</0> alabilirsiniz",
"real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsiz bir hesaptır. Lütfen Real-Debrid abonesi olun", "real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsiz bir hesaptır. Lütfen Real-Debrid abonesi olun",
"debrid_linked_message": "\"{{username}}\" hesabı bağlandı", "real_debrid_linked_message": "\"{{username}}\" hesabı bağlandı",
"save_changes": "Değişiklikleri Kaydet", "save_changes": "Değişiklikleri Kaydet",
"changes_saved": "Değişiklikler başarıyla kaydedildi", "changes_saved": "Değişiklikler başarıyla kaydedildi",
"download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacak. Kaynak URL, indirme bağlantılarını içeren bir .json dosyasına doğrudan bir bağlantı olmalıdır.", "download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacak. Kaynak URL, indirme bağlantılarını içeren bir .json dosyasına doğrudan bir bağlantı olmalıdır.",
"validate_download_source": "Doğrula", "validate_download_source": "Doğrula",
"remove_download_source": "Kaldır", "remove_download_source": "Kaldır",
"add_download_source": "Kaynak ekle", "add_download_source": "Kaynak ekle",
"cancel_button_confirmation_delete_all_sources": "Hayır",
"confirm_button_confirmation_delete_all_sources": "Evet, her şeyi sil",
"description_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sileceksiniz",
"title_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sil",
"removed_download_sources": "Yazı tipleri kaldırıldı",
"button_delete_all_sources": "Tüm indirme kaynaklarını kaldır",
"download_count_zero": "İndirme seçeneği yok", "download_count_zero": "İndirme seçeneği yok",
"download_count_one": "{{countFormatted}} indirme seçeneği", "download_count_one": "{{countFormatted}} indirme seçeneği",
"download_count_other": "{{countFormatted}} indirme seçeneği", "download_count_other": "{{countFormatted}} indirme seçeneği",
@@ -297,64 +276,11 @@
"must_be_valid_url": "Kaynak geçerli bir URL olmalıdır", "must_be_valid_url": "Kaynak geçerli bir URL olmalıdır",
"blocked_users": "Engellenen kullanıcılar", "blocked_users": "Engellenen kullanıcılar",
"user_unblocked": "Kullanıcının engeli kaldırıldı", "user_unblocked": "Kullanıcının engeli kaldırıldı",
"enable_achievement_notifications": "Bir başarım kilidi açıldığında", "enable_achievement_notifications": "Bir başarı kilidi açıldığında",
"launch_minimized": "Hydra'yı küçültülmüş başlat", "launch_minimized": "Hydra'yı küçültülmüş başlat",
"disable_nsfw_alert": "NSFW uyarısını devre dışı bırak", "disable_nsfw_alert": "NSFW uyarısını devre dışı bırak",
"seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş", "seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş",
"show_hidden_achievement_description": "Gizli başarımıklamalarını kilitlenmeden önce göster", "show_hidden_achievement_description": "Gizli başarııklamalarını kilitlenmeden önce göster"
"account": "Hesap",
"no_users_blocked": "Hiçbir kullanıcıyı engellemediniz",
"subscription_active_until": "Hydra Cloud'unuz {{date}} tarihine kadar aktif",
"manage_subscription": "Aboneliği yönet",
"update_email": "E-posta'yı güncelle",
"update_password": "Şifreyi güncelle",
"current_email": "Aktif e-posta'nız",
"no_email_account": "Henüz ayarlanmış bir e-postanız yok",
"account_data_updated_successfully": "Hesap bilgileri başarıyla güncellendi",
"renew_subscription": "Hydra Cloud'u yenile",
"subscription_expired_at": "Aboneliğiniz {{date}} tarihinde sona erdi",
"no_subscription": "Hydra'yı en iyi şekilde deneyimleyin",
"become_subscriber": "Hydra Cloud'lu ol",
"subscription_renew_cancelled": "Otomatik yenileme devre dışı",
"subscription_renews_on": "Aboneliğiniz {{date}} tarihinde yenilenecek",
"bill_sent_until": "Bir sonraki faturanız bu tarihe kadar gönderilecek",
"no_themes": "Henüz bir temanız yok gibi görünüyor, ama endişelenmeyin, ilk şaheserinizi oluşturmak için buraya tıklayın.",
"editor_tab_code": "Kod",
"editor_tab_info": "Bilgi",
"editor_tab_save": "Kaydet",
"web_store": "İnternet mağazası",
"clear_themes": "Temizle",
"create_theme": "Oluştur",
"create_theme_modal_title": "Tema oluştur",
"create_theme_modal_description": "Hydra'nın görünümünü özelleştirmek için yeni bir tema oluştur",
"theme_name": "İsim",
"insert_theme_name": "Tema ismini gir",
"set_theme": "Temayı seç",
"unset_theme": "Tema seçimini kaldır",
"delete_theme": "Temayı sil",
"edit_theme": "Temayı düzenle",
"delete_all_themes": "Tüm temaları sil",
"delete_all_themes_description": "Bu tüm temalarınızı silecektir",
"delete_theme_description": "Bu {{theme}} temasını silecektir",
"cancel": "İptal",
"appearance": "Görünüm",
"enable_torbox": "Torbox'u etkinleştir",
"torbox_description": "TorBox, piyasadaki en iyi sunucularla bile rekabet edebilen premium seedbox hizmetinizdir.",
"torbox_account_linked": "TorBox hesabı bağlandı",
"create_real_debrid_account": "Henüz bir Real-Debrid hesabınız yoksa buraya tıklayın",
"create_torbox_account": "Henüz bir TorBox hesabınız yoksa buraya tıklayın",
"real_debrid_account_linked": "Real-Debrid hesabı bağlandı",
"name_min_length": "Tema ismi en az 3 karakter uzunluğunda olmalıdır",
"import_theme": "Temayı içe aktar",
"import_theme_description": "{{theme}} teması, tema mağazasından içeri aktarılacak",
"error_importing_theme": "Temayı içe aktarmada bir sorun oluştu",
"theme_imported": "Tema başarıyla içe aktarıldı",
"enable_friend_request_notifications": "Bir arkadaşlık isteği alındığında",
"enable_auto_install": "Güncellemeleri otomatik yükle",
"common_redist": "Ortak bağımlılıklar",
"common_redist_description": "Bazı oyunların çalışabilmesi için genel bağımlılıklar gereklidir. Sorun yaşamamak için bunların yüklenmesi önerilir.",
"install_common_redist": "Yükle",
"installing_common_redist": "Yükleniyor…"
}, },
"notifications": { "notifications": {
"download_complete": "İndirme tamamlandı", "download_complete": "İndirme tamamlandı",
@@ -362,23 +288,17 @@
"repack_list_updated": "Repack listesi güncellendi", "repack_list_updated": "Repack listesi güncellendi",
"repack_count_one": "{{count}} repack eklendi", "repack_count_one": "{{count}} repack eklendi",
"repack_count_other": "{{count}} repack eklendi", "repack_count_other": "{{count}} repack eklendi",
"new_update_available": "{{version}} sürümü mevcut", "new_update_available": "Sürüm {{version}} mevcut",
"restart_to_install_update": "Güncellemeyi yüklemek için Hydra'yı yeniden başlatın", "restart_to_install_update": "Güncellemeyi yüklemek için Hydra'yı yeniden başlatın",
"notification_achievement_unlocked_title": "{{game}} için başarım kilidi açıldı", "notification_achievement_unlocked_title": "{{game}} için başarı kilidi açıldı",
"notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarımıldı", "notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarılarıldı"
"new_friend_request_description": "Yeni bir arkadaşlık isteğin var",
"new_friend_request_title": "Yeni arkadaşlık isteği",
"extraction_complete": ıkartma tamamlandı",
"game_extracted": "{{title}} başarıyla çıkartıldı"
}, },
"system_tray": { "system_tray": {
"open": "Hydra'yı Aç", "open": "Hydra'yı Aç",
"quit": ık" "quit": ık"
}, },
"game_card": { "game_card": {
"no_downloads": "İndirilebilir içerik bulunmuyor", "no_downloads": "İndirilebilir içerik bulunmuyor"
"available_one": "Mevcut",
"available_other": "Mevcut"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
"title": "Programlar Yüklü Değil", "title": "Programlar Yüklü Değil",
@@ -411,7 +331,7 @@
"successfully_signed_out": "Başarıyla çıkış yapıldı", "successfully_signed_out": "Başarıyla çıkış yapıldı",
"sign_out": ıkış yap", "sign_out": ıkış yap",
"playing_for": "{{amount}} oynanıyor", "playing_for": "{{amount}} oynanıyor",
"sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Oturumu kapattığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Oturumu kapatmaya devam etmek istiyor musunuz?", "sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Çıkış yaptığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Çıkışa devam etmek istiyor musunuz?",
"add_friends": "Arkadaş Ekle", "add_friends": "Arkadaş Ekle",
"add": "Ekle", "add": "Ekle",
"friend_code": "Arkadaş kodu", "friend_code": "Arkadaş kodu",
@@ -462,39 +382,39 @@
"uploading_banner": "Afiş yükleniyor…", "uploading_banner": "Afiş yükleniyor…",
"background_image_updated": "Arka plan görüntüsü güncellendi", "background_image_updated": "Arka plan görüntüsü güncellendi",
"stats": "İstatistikler", "stats": "İstatistikler",
"achievements": "Başarımlar", "achievements": "Başarılar",
"games": "Oyunlar", "games": "Oyunlar",
"top_percentile": "En üst {{percentile}}%", "top_percentile": "En üst {{percentile}}%",
"ranking_updated_weekly": "Sıralama haftalık olarak güncellenir", "ranking_updated_weekly": "Sıralama haftalık olarak güncellenir",
"playing": "{{game}} oynanıyor", "playing": "{{game}} oynanıyor",
"achievements_unlocked": "Başarımlar açıldı", "achievements_unlocked": "Başarılar açıldı",
"earned_points": "Kazanılan puanlar", "earned_points": "Kazanılan puanlar",
"show_achievements_on_profile": "Başarımlarınızı profilinizde gösterin", "show_achievements_on_profile": "Başarılarınızı profilinizde gösterin",
"show_points_on_profile": "Kazandığınız puanları profilinizde gösterin" "show_points_on_profile": "Kazandığınız puanları profilinizde gösterin"
}, },
"achievement": { "achievement": {
"achievement_unlocked": "Başarımıldı", "achievement_unlocked": "Başarııldı",
"user_achievements": "{{displayName}} oyununun Başarımları", "user_achievements": "{{displayName}}'in Başarıları",
"your_achievements": "Başarımlarınız", "your_achievements": "Başarılarınız",
"unlocked_at": "Açılma zamanı: {{date}}", "unlocked_at": "Açılma zamanı: {{date}}",
"subscription_needed": "Bu içeriği görmek için bir Hydra Cloud aboneliği gereklidir", "subscription_needed": "Bu içeriği görmek için bir Hydra Cloud aboneliği gereklidir",
"new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarımıldı", "new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarııldı",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarım", "achievement_progress": "{{unlockedCount}}/{{totalCount}} başarı",
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarımıldı", "achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarııldı",
"hidden_achievement_tooltip": "Bu gizli bir başarımdır", "hidden_achievement_tooltip": "Bu gizli bir başarıdır",
"achievement_earn_points": "Bu başarım ile {{points}} puan kazanın", "achievement_earn_points": "Bu başarı ile {{points}} puan kazanın",
"earned_points": "Kazanılan puanlar:", "earned_points": "Kazanılan puanlar:",
"available_points": "Mevcut puanlar:", "available_points": "Mevcut puanlar:",
"how_to_earn_achievements_points": "Başarım puanları nasıl kazanılır?" "how_to_earn_achievements_points": "Başarı puanları nasıl kazanılır?"
}, },
"hydra_cloud": { "hydra_cloud": {
"subscription_tour_title": "Hydra Cloud Aboneliği", "subscription_tour_title": "Hydra Cloud Aboneliği",
"subscribe_now": "Şimdi abone olun", "subscribe_now": "Şimdi abone olun",
"cloud_saving": "Bulut kaydetme", "cloud_saving": "Bulut kaydetme",
"cloud_achievements": "Başarımlarınızı buluta kaydedin", "cloud_achievements": "Başarılarınızı buluta kaydedin",
"animated_profile_picture": "Animasyonlu profil resimleri", "animated_profile_picture": "Animasyonlu profil resimleri",
"premium_support": "Premium Destek", "premium_support": "Premium Destek",
"show_and_compare_achievements": "Başarımlarınızı diğer kullanıcılarla karşılaştırın ve gösterin", "show_and_compare_achievements": "Başarılarınızı diğer kullanıcılarla karşılaştırın ve gösterin",
"animated_profile_banner": "Animasyonlu profil afişi", "animated_profile_banner": "Animasyonlu profil afişi",
"hydra_cloud": "Hydra Cloud", "hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!", "hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!",

View File

@@ -20,12 +20,10 @@
"home": "Головна", "home": "Головна",
"game_has_no_executable": "Не було вибрано файл для запуску гри", "game_has_no_executable": "Не було вибрано файл для запуску гри",
"queued": "{{title}} в черзі", "queued": "{{title}} в черзі",
"sign_in": "Увійти", "sign_in": "Увійти"
"favorites": "Улюблені"
}, },
"header": { "header": {
"search": "Пошук", "search": "Пошук",
"home": "Головна", "home": "Головна",
"catalogue": "Каталог", "catalogue": "Каталог",
"downloads": "Завантаження", "downloads": "Завантаження",
@@ -176,19 +174,13 @@
"import": "Імпортувати", "import": "Імпортувати",
"insert_valid_json_url": "Вставте дійсний URL JSON-файлу", "insert_valid_json_url": "Вставте дійсний URL JSON-файлу",
"language": "Мова", "language": "Мова",
"cancel_button_confirmation_delete_all_sources": "немає", "real_debrid_api_token": "API-токен",
"confirm_button_confirmation_delete_all_sources": "Так, видалити все", "real_debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
"description_confirmation_delete_all_sources": "Ви видалите всі джерела завантаження",
"title_confirmation_delete_all_sources": "Видалити всі джерела завантаження",
"removed_download_sources": "Джерела завантажень видалено",
"button_delete_all_sources": "Видаліть усі джерела завантаження",
"api_token": "API-токен",
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
"real_debrid_api_token_label": "Real-Debrid API-токен", "real_debrid_api_token_label": "Real-Debrid API-токен",
"real_debrid_description": "Real-Debrid — це необмежений завантажувач, який дозволяє швидко завантажувати файли, розміщені в Інтернеті, або миттєво передавати їх у плеєр через приватну мережу, що дозволяє обходити будь-які блокування.", "real_debrid_description": "Real-Debrid — це необмежений завантажувач, який дозволяє швидко завантажувати файли, розміщені в Інтернеті, або миттєво передавати їх у плеєр через приватну мережу, що дозволяє обходити будь-які блокування.",
"real_debrid_free_account_error": "Акаунт \"{{username}}\" - не має наявної підписки. Будь ласка, оформіть підписку на Real-Debrid", "real_debrid_free_account_error": "Акаунт \"{{username}}\" - не має наявної підписки. Будь ласка, оформіть підписку на Real-Debrid",
"debrid_invalid_token": "Невірний API-токен", "real_debrid_invalid_token": "Невірний API-токен",
"debrid_linked_message": "Акаунт \"{{username}}\" привязаний", "real_debrid_linked_message": "Акаунт \"{{username}}\" привязаний",
"remove_download_source": "Видалити", "remove_download_source": "Видалити",
"removed_download_source": "Джерело завантажень було видалено", "removed_download_source": "Джерело завантажень було видалено",
"save_changes": "Зберегти зміни", "save_changes": "Зберегти зміни",

View File

@@ -25,8 +25,7 @@
"queued": "{{title}} (已加入下载队列)", "queued": "{{title}} (已加入下载队列)",
"game_has_no_executable": "未选择游戏的可执行文件", "game_has_no_executable": "未选择游戏的可执行文件",
"sign_in": "登入", "sign_in": "登入",
"friends": "好友", "friends": "好友"
"favorites": "收藏"
}, },
"header": { "header": {
"search": "搜索游戏", "search": "搜索游戏",
@@ -214,24 +213,18 @@
"behavior": "行为", "behavior": "行为",
"download_sources": "下载源", "download_sources": "下载源",
"language": "语言", "language": "语言",
"api_token": "API 令牌", "real_debrid_api_token": "API 令牌",
"enable_real_debrid": "启用 Real-Debrid", "enable_real_debrid": "启用 Real-Debrid",
"real_debrid_description": "Real-Debrid 是一个无限制的下载器,允许您以最快的互联网速度即时下载文件。", "real_debrid_description": "Real-Debrid 是一个无限制的下载器,允许您以最快的互联网速度即时下载文件。",
"debrid_invalid_token": "无效的 API 令牌", "real_debrid_invalid_token": "无效的 API 令牌",
"debrid_api_token_hint": "您可以从<0>这里</0>获取API密钥.", "real_debrid_api_token_hint": "您可以从<0>这里</0>获取API密钥.",
"real_debrid_free_account_error": "账户 \"{{username}}\" 是免费账户。请订阅 Real-Debrid", "real_debrid_free_account_error": "账户 \"{{username}}\" 是免费账户。请订阅 Real-Debrid",
"debrid_linked_message": "账户 \"{{username}}\" 已链接", "real_debrid_linked_message": "账户 \"{{username}}\" 已链接",
"save_changes": "保存更改", "save_changes": "保存更改",
"changes_saved": "更改已成功保存", "changes_saved": "更改已成功保存",
"download_sources_description": "Hydra 将从这些源获取下载链接。源 URL 必须是直接链接到包含下载链接的 .json 文件。", "download_sources_description": "Hydra 将从这些源获取下载链接。源 URL 必须是直接链接到包含下载链接的 .json 文件。",
"validate_download_source": "验证", "validate_download_source": "验证",
"remove_download_source": "移除", "remove_download_source": "移除",
"cancel_button_confirmation_delete_all_sources": "不",
"confirm_button_confirmation_delete_all_sources": "是的,删除所有内容",
"description_confirmation_delete_all_sources": "您将删除所有下载源",
"title_confirmation_delete_all_sources": "删除所有下载源",
"removed_download_sources": "已删除字体",
"button_delete_all_sources": "删除所有下载源",
"add_download_source": "添加源", "add_download_source": "添加源",
"download_count_zero": "列表中无下载", "download_count_zero": "列表中无下载",
"download_count_one": "列表中有 {{countFormatted}} 个下载", "download_count_one": "列表中有 {{countFormatted}} 个下载",

View File

@@ -7,17 +7,13 @@ export const defaultDownloadsPath = app.getPath("downloads");
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging"); export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
export const levelDatabasePath = path.join( export const databaseDirectory = path.join(app.getPath("appData"), "hydra");
app.getPath("userData"), export const databasePath = path.join(
`hydra-db${isStaging ? "-staging" : ""}` databaseDirectory,
isStaging ? "hydra_test.db" : "hydra.db"
); );
export const commonRedistPath = path.join( export const logsPath = path.join(app.getPath("appData"), "hydra", "logs");
app.getPath("userData"),
"CommonRedist"
);
export const logsPath = path.join(app.getPath("userData"), "logs");
export const seedsPath = app.isPackaged export const seedsPath = app.isPackaged
? path.join(process.resourcesPath, "seeds") ? path.join(process.resourcesPath, "seeds")
@@ -30,5 +26,3 @@ export const achievementSoundPath = app.isPackaged
export const backupsPath = path.join(app.getPath("userData"), "Backups"); export const backupsPath = path.join(app.getPath("userData"), "Backups");
export const appVersion = app.getVersion() + (isStaging ? "-staging" : ""); export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
export const MAIN_LOOP_INTERVAL = 1500;

27
src/main/data-source.ts Normal file
View File

@@ -0,0 +1,27 @@
import { DataSource } from "typeorm";
import {
DownloadQueue,
Game,
GameShopCache,
UserPreferences,
UserAuth,
GameAchievement,
UserSubscription,
} from "@main/entity";
import { databasePath } from "./constants";
export const dataSource = new DataSource({
type: "better-sqlite3",
entities: [
Game,
UserAuth,
UserPreferences,
UserSubscription,
GameShopCache,
DownloadQueue,
GameAchievement,
],
synchronize: false,
database: databasePath,
});

View File

@@ -0,0 +1,25 @@
import {
Entity,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
JoinColumn,
} from "typeorm";
import type { Game } from "./game.entity";
@Entity("download_queue")
export class DownloadQueue {
@PrimaryGeneratedColumn()
id: number;
@OneToOne("Game", "downloadQueue")
@JoinColumn()
game: Game;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -0,0 +1,19 @@
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

@@ -0,0 +1,35 @@
import {
Entity,
PrimaryColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from "typeorm";
import type { GameShop } from "@types";
@Entity("game_shop_cache")
export class GameShopCache {
@PrimaryColumn("text", { unique: true })
objectID: string;
@Column("text")
shop: GameShop;
@Column("text", { nullable: true })
serializedData: string;
/**
* @deprecated Use IndexedDB's `howLongToBeatEntries` instead
*/
@Column("text", { nullable: true })
howLongToBeatSerializedData: string;
@Column("text", { nullable: true })
language: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -0,0 +1,90 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
} from "typeorm";
import type { GameShop, GameStatus } from "@types";
import { Downloader } from "@shared";
import type { DownloadQueue } from "./download-queue.entity";
@Entity("game")
export class Game {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { unique: true })
objectID: string;
@Column("text", { unique: true, nullable: true })
remoteId: string | null;
@Column("text")
title: string;
@Column("text", { nullable: true })
iconUrl: string | null;
@Column("text", { nullable: true })
folderName: string | null;
@Column("text", { nullable: true })
downloadPath: string | null;
@Column("text", { nullable: true })
executablePath: string | null;
@Column("text", { nullable: true })
launchOptions: string | null;
@Column("text", { nullable: true })
winePrefixPath: string | null;
@Column("int", { default: 0 })
playTimeInMilliseconds: number;
@Column("text")
shop: GameShop;
@Column("text", { nullable: true })
status: GameStatus | null;
@Column("int", { default: Downloader.Torrent })
downloader: Downloader;
/**
* Progress is a float between 0 and 1
*/
@Column("float", { default: 0 })
progress: number;
@Column("int", { default: 0 })
bytesDownloaded: number;
@Column("datetime", { nullable: true })
lastTimePlayed: Date | null;
@Column("float", { default: 0 })
fileSize: number;
@Column("text", { nullable: true })
uri: string | null;
@OneToOne("DownloadQueue", "game")
downloadQueue: DownloadQueue;
@Column("boolean", { default: false })
isDeleted: boolean;
@Column("boolean", { default: false })
shouldSeed: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

8
src/main/entity/index.ts Normal file
View File

@@ -0,0 +1,8 @@
export * from "./game.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-queue.entity";

View File

@@ -0,0 +1,45 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
} from "typeorm";
import { UserSubscription } from "./user-subscription.entity";
@Entity("user_auth")
export class UserAuth {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { default: "" })
userId: string;
@Column("text", { default: "" })
displayName: string;
@Column("text", { nullable: true })
profileImageUrl: string | null;
@Column("text", { nullable: true })
backgroundImageUrl: string | null;
@Column("text", { default: "" })
accessToken: string;
@Column("text", { default: "" })
refreshToken: string;
@Column("int", { default: 0 })
tokenExpirationTimestamp: number;
@OneToOne("UserSubscription", "user")
subscription: UserSubscription | null;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -0,0 +1,55 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from "typeorm";
@Entity("user_preferences")
export class UserPreferences {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { nullable: true })
downloadsPath: string | null;
@Column("text", { default: "en" })
language: string;
@Column("text", { nullable: true })
realDebridApiToken: string | null;
@Column("boolean", { default: false })
downloadNotificationsEnabled: boolean;
@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;
@Column("boolean", { default: false })
disableNsfwAlert: boolean;
@Column("boolean", { default: true })
seedAfterDownloadComplete: boolean;
@Column("boolean", { default: false })
showHiddenAchievementsDescription: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -0,0 +1,42 @@
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,13 +1,10 @@
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { userAuthRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { db, levelKeys } from "@main/level";
import type { Auth } from "@types";
const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => { const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
const auth = await db.get<string, Auth>(levelKeys.auth, { const auth = await userAuthRepository.findOne({ where: { id: 1 } });
valueEncoding: "json",
});
if (!auth) return null; if (!auth) return null;
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload; const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;

View File

@@ -1,29 +1,35 @@
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services"; import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services";
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level"; import { dataSource } from "@main/data-source";
import { DownloadQueue, Game, UserAuth, UserSubscription } from "@main/entity";
import { PythonRPC } from "@main/services/python-rpc";
const signOut = async (_event: Electron.IpcMainInvokeEvent) => { const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
const databaseOperations = db const databaseOperations = dataSource
.batch([ .transaction(async (transactionalEntityManager) => {
{ await transactionalEntityManager.getRepository(DownloadQueue).delete({});
type: "del",
key: levelKeys.auth, await transactionalEntityManager.getRepository(Game).delete({});
},
{ await transactionalEntityManager
type: "del", .getRepository(UserAuth)
key: levelKeys.user, .delete({ id: 1 });
},
]) await transactionalEntityManager
.getRepository(UserSubscription)
.delete({ id: 1 });
})
.then(() => { .then(() => {
/* Removes all games being played */ /* Removes all games being played */
gamesPlaytime.clear(); gamesPlaytime.clear();
return Promise.all([gamesSublevel.clear(), downloadsSublevel.clear()]);
}); });
/* Cancels any ongoing downloads */ /* Cancels any ongoing downloads */
DownloadManager.cancelDownload(); DownloadManager.cancelDownload();
/* Disconnects libtorrent */
PythonRPC.kill();
HydraApi.handleSignOut(); HydraApi.handleSignOut();
await Promise.all([ await Promise.all([

View File

@@ -1,8 +1,47 @@
import type { AppUpdaterEvent } from "@types";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { UpdateManager } from "@main/services/update-manager"; import updater, { UpdateInfo } from "electron-updater";
import { WindowManager } from "@main/services";
import { app } from "electron";
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
const { autoUpdater } = updater;
const sendEvent = (event: AppUpdaterEvent) => {
WindowManager.mainWindow?.webContents.send("autoUpdaterEvent", event);
};
const sendEventsForDebug = false;
const isAutoInstallAvailable =
process.platform !== "darwin" && process.env.PORTABLE_EXECUTABLE_FILE == null;
const mockValuesForDebug = () => {
sendEvent({ type: "update-available", info: { version: "1.3.0" } });
sendEvent({ type: "update-downloaded" });
};
const newVersionInfo = { version: "" };
const checkForUpdates = async (_event: Electron.IpcMainInvokeEvent) => { const checkForUpdates = async (_event: Electron.IpcMainInvokeEvent) => {
return UpdateManager.checkForUpdates(); autoUpdater
.once("update-available", (info: UpdateInfo) => {
sendEvent({ type: "update-available", info });
newVersionInfo.version = info.version;
})
.once("update-downloaded", () => {
sendEvent({ type: "update-downloaded" });
publishNotificationUpdateReadyToInstall(newVersionInfo.version);
});
if (app.isPackaged) {
autoUpdater.autoDownload = isAutoInstallAvailable;
autoUpdater.checkForUpdates();
} else if (sendEventsForDebug) {
mockValuesForDebug();
}
return isAutoInstallAvailable;
}; };
registerEvent("checkForUpdates", checkForUpdates); registerEvent("checkForUpdates", checkForUpdates);

View File

@@ -4,15 +4,11 @@ import updater from "electron-updater";
const { autoUpdater } = updater; const { autoUpdater } = updater;
export const restartAndInstallUpdate = () => { const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
autoUpdater.removeAllListeners(); autoUpdater.removeAllListeners();
if (app.isPackaged) { if (app.isPackaged) {
autoUpdater.quitAndInstall(false); autoUpdater.quitAndInstall(false);
} }
}; };
const restartAndInstallUpdateEvent = async ( registerEvent("restartAndInstallUpdate", restartAndInstallUpdate);
_event: Electron.IpcMainInvokeEvent
) => restartAndInstallUpdate();
registerEvent("restartAndInstallUpdate", restartAndInstallUpdateEvent);

View File

@@ -1,10 +1,10 @@
import { getSteamAppDetails, logger } from "@main/services"; import { gameShopCacheRepository } from "@main/repository";
import { getSteamAppDetails } from "@main/services";
import type { ShopDetails, GameShop } from "@types"; import type { ShopDetails, GameShop, SteamAppDetails } from "@types";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { steamGamesWorker } from "@main/workers"; import { steamGamesWorker } from "@main/workers";
import { gamesShopCacheSublevel, levelKeys } from "@main/level";
const getLocalizedSteamAppDetails = async ( const getLocalizedSteamAppDetails = async (
objectId: string, objectId: string,
@@ -39,27 +39,35 @@ const getGameShopDetails = async (
language: string language: string
): Promise<ShopDetails | null> => { ): Promise<ShopDetails | null> => {
if (shop === "steam") { if (shop === "steam") {
const cachedData = await gamesShopCacheSublevel.get( const cachedData = await gameShopCacheRepository.findOne({
levelKeys.gameShopCacheItem(shop, objectId, language) where: { objectID: objectId, language },
); });
const appDetails = getLocalizedSteamAppDetails(objectId, language).then( const appDetails = getLocalizedSteamAppDetails(objectId, language).then(
(result) => { (result) => {
if (result) { if (result) {
gamesShopCacheSublevel gameShopCacheRepository.upsert(
.put(levelKeys.gameShopCacheItem(shop, objectId, language), result) {
.catch((err) => { objectID: objectId,
logger.error("Could not cache game details", err); shop: "steam",
}); language,
serializedData: JSON.stringify(result),
},
["objectID"]
);
} }
return result; return result;
} }
); );
if (cachedData) { const cachedGame = cachedData?.serializedData
? (JSON.parse(cachedData?.serializedData) as SteamAppDetails)
: null;
if (cachedGame) {
return { return {
...cachedData, ...cachedGame,
objectId, objectId,
} as ShopDetails; } as ShopDetails;
} }

View File

@@ -1,14 +1,14 @@
import { db, levelKeys } from "@main/level";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services"; import { HydraApi } from "@main/services";
import { userPreferencesRepository } from "@main/repository";
import type { TrendingGame } from "@types"; import type { TrendingGame } from "@types";
const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => { const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => {
const language = await db const userPreferences = await userPreferencesRepository.findOne({
.get<string, string>(levelKeys.language, { where: { id: 1 },
valueEncoding: "utf-8", });
})
.then((language) => language || "en"); const language = userPreferences?.language || "en";
const trendingGames = await HydraApi.get<TrendingGame[]>( const trendingGames = await HydraApi.get<TrendingGame[]>(
"/games/trending", "/games/trending",

View File

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

View File

@@ -1,8 +1,44 @@
import { CloudSync } from "@main/services"; import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
import { registerEvent } from "../register-event"; 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 type { GameShop } from "@types";
import i18next, { t } from "i18next"; import axios from "axios";
import { formatDate } from "date-fns"; 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 ( const uploadSaveGame = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
@@ -10,17 +46,66 @@ const uploadSaveGame = async (
shop: GameShop, shop: GameShop,
downloadOptionTitle: string | null downloadOptionTitle: string | null
) => { ) => {
const { language } = i18next; const game = await gameRepository.findOne({
where: {
objectID: objectId,
shop,
},
});
return CloudSync.uploadSaveGame( const bundleLocation = await bundleBackup(
objectId,
shop, shop,
downloadOptionTitle, objectId,
t("backup_from", { game?.winePrefixPath ?? null
ns: "game_details",
date: formatDate(new Date(), language),
})
); );
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) => {
logger.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); registerEvent("uploadSaveGame", uploadSaveGame);

View File

@@ -1,13 +0,0 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const createDownloadSources = async (
_event: Electron.IpcMainInvokeEvent,
urls: string[]
) => {
await HydraApi.post("/profile/download-sources", {
urls,
});
};
registerEvent("createDownloadSources", createDownloadSources);

View File

@@ -1,8 +0,0 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) => {
return HydraApi.get("/profile/download-sources");
};
registerEvent("getDownloadSources", getDownloadSources);

View File

@@ -1,18 +0,0 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const removeDownloadSource = async (
_event: Electron.IpcMainInvokeEvent,
url?: string,
removeAll = false
) => {
const params = new URLSearchParams({
all: removeAll.toString(),
});
if (url) params.set("url", url);
return HydraApi.delete(`/profile/download-sources?${params.toString()}`);
};
registerEvent("removeDownloadSource", removeDownloadSource);

View File

@@ -1,21 +1,15 @@
import fs from "node:fs"; import fs from "node:fs";
import path from "node:path";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
const checkFolderWritePermission = async ( const checkFolderWritePermission = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
testPath: string path: string
) => { ) =>
const testFilePath = path.join(testPath, ".hydra-write-test"); new Promise((resolve) => {
fs.access(path, fs.constants.W_OK, (err) => {
try { resolve(!err);
fs.writeFileSync(testFilePath, ""); });
fs.rmSync(testFilePath); });
return true;
} catch (err) {
return false;
}
};
registerEvent("checkFolderWritePermission", checkFolderWritePermission); registerEvent("checkFolderWritePermission", checkFolderWritePermission);

View File

@@ -0,0 +1,44 @@
import { Document as YMLDocument } from "yaml";
import { Game } from "@main/entity";
import path from "node:path";
export const generateYML = (game: Game) => {
const slugifiedGameTitle = game.title.replace(/\s/g, "-").toLocaleLowerCase();
const doc = new YMLDocument({
name: game.title,
game_slug: slugifiedGameTitle,
slug: `${slugifiedGameTitle}-installer`,
version: "Installer",
runner: "wine",
script: {
game: {
prefix: "$GAMEDIR",
arch: "win64",
working_dir: "$GAMEDIR",
},
installer: [
{
task: {
name: "create_prefix",
arch: "win64",
prefix: "$GAMEDIR",
},
},
{
task: {
executable: path.join(
game.downloadPath!,
game.folderName!,
"setup.exe"
),
name: "wineexec",
prefix: "$GAMEDIR",
},
},
],
},
});
return doc.toString();
};

View File

@@ -1,16 +1,15 @@
import { userPreferencesRepository } from "@main/repository";
import { defaultDownloadsPath } from "@main/constants"; import { defaultDownloadsPath } from "@main/constants";
import { db, levelKeys } from "@main/level";
import type { UserPreferences } from "@types";
export const getDownloadsPath = async () => { export const getDownloadsPath = async () => {
const userPreferences = await db.get<string, UserPreferences | null>( const userPreferences = await userPreferencesRepository.findOne({
levelKeys.userPreferences, where: {
{ id: 1,
valueEncoding: "json", },
} });
);
if (userPreferences?.downloadsPath) return userPreferences.downloadsPath; if (userPreferences && userPreferences.downloadsPath)
return userPreferences.downloadsPath;
return defaultDownloadsPath; return defaultDownloadsPath;
}; };

View File

@@ -1,7 +0,0 @@
export const parseLaunchOptions = (params?: string | null): string[] => {
if (!params) {
return [];
}
return params.split(" ");
};

View File

@@ -13,14 +13,11 @@ import "./catalogue/get-developers";
import "./hardware/get-disk-free-space"; import "./hardware/get-disk-free-space";
import "./hardware/check-folder-write-permission"; import "./hardware/check-folder-write-permission";
import "./library/add-game-to-library"; import "./library/add-game-to-library";
import "./library/add-game-to-favorites";
import "./library/remove-game-from-favorites";
import "./library/create-game-shortcut"; import "./library/create-game-shortcut";
import "./library/close-game"; import "./library/close-game";
import "./library/delete-game-folder"; import "./library/delete-game-folder";
import "./library/get-game-by-object-id"; import "./library/get-game-by-object-id";
import "./library/get-library"; import "./library/get-library";
import "./library/extract-game-download";
import "./library/open-game"; import "./library/open-game";
import "./library/open-game-executable-path"; import "./library/open-game-executable-path";
import "./library/open-game-installer"; import "./library/open-game-installer";
@@ -32,29 +29,23 @@ import "./library/remove-game";
import "./library/remove-game-from-library"; import "./library/remove-game-from-library";
import "./library/select-game-wine-prefix"; import "./library/select-game-wine-prefix";
import "./library/reset-game-achievements"; import "./library/reset-game-achievements";
import "./library/toggle-automatic-cloud-sync";
import "./misc/open-checkout"; import "./misc/open-checkout";
import "./misc/open-external"; import "./misc/open-external";
import "./misc/show-open-dialog"; import "./misc/show-open-dialog";
import "./misc/get-features"; import "./misc/get-features";
import "./misc/show-item-in-folder"; import "./misc/show-item-in-folder";
import "./misc/get-badges";
import "./misc/install-common-redist";
import "./misc/can-install-common-redist";
import "./torrenting/cancel-game-download"; import "./torrenting/cancel-game-download";
import "./torrenting/pause-game-download"; import "./torrenting/pause-game-download";
import "./torrenting/resume-game-download"; import "./torrenting/resume-game-download";
import "./torrenting/start-game-download"; import "./torrenting/start-game-download";
import "./torrenting/pause-game-seed"; import "./torrenting/pause-game-seed";
import "./torrenting/resume-game-seed"; import "./torrenting/resume-game-seed";
import "./torrenting/check-debrid-availability";
import "./user-preferences/get-user-preferences"; import "./user-preferences/get-user-preferences";
import "./user-preferences/update-user-preferences"; import "./user-preferences/update-user-preferences";
import "./user-preferences/auto-launch"; import "./user-preferences/auto-launch";
import "./autoupdater/check-for-updates"; import "./autoupdater/check-for-updates";
import "./autoupdater/restart-and-install-update"; import "./autoupdater/restart-and-install-update";
import "./user-preferences/authenticate-real-debrid"; import "./user-preferences/authenticate-real-debrid";
import "./user-preferences/authenticate-torbox";
import "./download-sources/put-download-source"; import "./download-sources/put-download-source";
import "./auth/sign-out"; import "./auth/sign-out";
import "./auth/open-auth-window"; import "./auth/open-auth-window";
@@ -64,7 +55,6 @@ import "./user/get-blocked-users";
import "./user/block-user"; import "./user/block-user";
import "./user/unblock-user"; import "./user/unblock-user";
import "./user/get-user-friends"; import "./user/get-user-friends";
import "./user/get-auth";
import "./user/get-user-stats"; import "./user/get-user-stats";
import "./user/report-user"; import "./user/report-user";
import "./user/get-unlocked-achievements"; import "./user/get-unlocked-achievements";
@@ -84,19 +74,6 @@ import "./cloud-save/upload-save-game";
import "./cloud-save/delete-game-artifact"; import "./cloud-save/delete-game-artifact";
import "./cloud-save/select-game-backup-path"; import "./cloud-save/select-game-backup-path";
import "./notifications/publish-new-repacks-notification"; import "./notifications/publish-new-repacks-notification";
import "./themes/add-custom-theme";
import "./themes/delete-custom-theme";
import "./themes/get-all-custom-themes";
import "./themes/delete-all-custom-themes";
import "./themes/update-custom-theme";
import "./themes/open-editor-window";
import "./themes/get-custom-theme-by-id";
import "./themes/get-active-custom-theme";
import "./themes/close-editor-window";
import "./themes/toggle-custom-theme";
import "./download-sources/create-download-sources";
import "./download-sources/remove-download-source";
import "./download-sources/get-download-sources";
import { isPortableVersion } from "@main/helpers"; import { isPortableVersion } from "@main/helpers";
ipcMain.handle("ping", () => "pong"); ipcMain.handle("ping", () => "pong");

View File

@@ -1,25 +0,0 @@
import { registerEvent } from "../register-event";
import { gamesSublevel, levelKeys } from "@main/level";
import type { GameShop } from "@types";
const addGameToFavorites = async (
_event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string
) => {
const gameKey = levelKeys.game(shop, objectId);
const game = await gamesSublevel.get(gameKey);
if (!game) return;
try {
await gamesSublevel.put(gameKey, {
...game,
favorite: true,
});
} catch (error) {
throw new Error(`Failed to update game favorite status: ${error}`);
}
};
registerEvent("addGameToFavorites", addGameToFavorites);

View File

@@ -1,55 +1,57 @@
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import type { Game, GameShop } from "@types"; import type { GameShop } from "@types";
import { steamGamesWorker } from "@main/workers"; import { steamGamesWorker } from "@main/workers";
import { createGame } from "@main/services/library-sync"; import { createGame } from "@main/services/library-sync";
import { steamUrlBuilder } from "@shared"; import { steamUrlBuilder } from "@shared";
import { updateLocalUnlockedAchievements } from "@main/services/achievements/update-local-unlocked-achivements"; import { updateLocalUnlockedAchivements } from "@main/services/achievements/update-local-unlocked-achivements";
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
const addGameToLibrary = async ( const addGameToLibrary = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string, objectId: string,
title: string title: string,
shop: GameShop
) => { ) => {
const gameKey = levelKeys.game(shop, objectId); return gameRepository
const game = await gamesSublevel.get(gameKey); .update(
{
objectID: objectId,
},
{
shop,
status: null,
isDeleted: false,
}
)
.then(async ({ affected }) => {
if (!affected) {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
if (game) { const iconUrl = steamGame?.clientIcon
await downloadsSublevel.del(gameKey); ? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
: null;
await gamesSublevel.put(gameKey, { await gameRepository.insert({
...game, title,
isDeleted: false, iconUrl,
objectID: objectId,
shop,
});
}
const game = await gameRepository.findOne({
where: { objectID: objectId },
});
updateLocalUnlockedAchivements(game!);
createGame(game!).catch(() => {});
}); });
} else {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
: null;
const game: Game = {
title,
iconUrl,
objectId,
shop,
remoteId: null,
isDeleted: false,
playTimeInMilliseconds: 0,
lastTimePlayed: null,
};
await gamesSublevel.put(levelKeys.game(shop, objectId), game);
await createGame(game).catch(() => {});
updateLocalUnlockedAchievements(game);
}
}; };
registerEvent("addGameToLibrary", addGameToLibrary); registerEvent("addGameToLibrary", addGameToLibrary);

View File

@@ -1,11 +1,10 @@
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { logger } from "@main/services"; import { logger } from "@main/services";
import sudo from "sudo-prompt"; import sudo from "sudo-prompt";
import { app } from "electron"; import { app } from "electron";
import { PythonRPC } from "@main/services/python-rpc"; import { PythonRPC } from "@main/services/python-rpc";
import { ProcessPayload } from "@main/services/download/types"; import { ProcessPayload } from "@main/services/download/types";
import { gamesSublevel, levelKeys } from "@main/level";
import { GameShop } from "@types";
const getKillCommand = (pid: number) => { const getKillCommand = (pid: number) => {
if (process.platform == "win32") { if (process.platform == "win32") {
@@ -17,14 +16,15 @@ const getKillCommand = (pid: number) => {
const closeGame = async ( const closeGame = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop, gameId: number
objectId: string
) => { ) => {
const processes = const processes =
(await PythonRPC.rpc.get<ProcessPayload[] | null>("/process-list")).data || (await PythonRPC.rpc.get<ProcessPayload[] | null>("/process-list")).data ||
[]; [];
const game = await gamesSublevel.get(levelKeys.game(shop, objectId)); const game = await gameRepository.findOne({
where: { id: gameId, isDeleted: false },
});
if (!game) return; if (!game) return;

View File

@@ -1,18 +1,18 @@
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { IsNull, Not } from "typeorm";
import createDesktopShortcut from "create-desktop-shortcuts"; import createDesktopShortcut from "create-desktop-shortcuts";
import path from "node:path"; import path from "node:path";
import { app } from "electron"; import { app } from "electron";
import { removeSymbolsFromName } from "@shared"; import { removeSymbolsFromName } from "@shared";
import { GameShop } from "@types";
import { gamesSublevel, levelKeys } from "@main/level";
const createGameShortcut = async ( const createGameShortcut = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop, id: number
objectId: string
): Promise<boolean> => { ): Promise<boolean> => {
const gameKey = levelKeys.game(shop, objectId); const game = await gameRepository.findOne({
const game = await gamesSublevel.get(gameKey); where: { id, executablePath: Not(IsNull()) },
});
if (game) { if (game) {
const filePath = game.executablePath; const filePath = game.executablePath;

View File

@@ -1,55 +1,61 @@
import path from "node:path"; import path from "node:path";
import fs from "node:fs"; import fs from "node:fs";
import { gameRepository } from "@main/repository";
import { getDownloadsPath } from "../helpers/get-downloads-path"; import { getDownloadsPath } from "../helpers/get-downloads-path";
import { logger } from "@main/services"; import { logger } from "@main/services";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { GameShop } from "@types";
import { downloadsSublevel, levelKeys } from "@main/level";
const deleteGameFolder = async ( const deleteGameFolder = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop, gameId: number
objectId: string
): Promise<void> => { ): Promise<void> => {
const downloadKey = levelKeys.game(shop, objectId); const game = await gameRepository.findOne({
const download = await downloadsSublevel.get(downloadKey); where: [
{
id: gameId,
isDeleted: false,
status: "removed",
},
{
id: gameId,
progress: 1,
isDeleted: false,
},
],
});
if (!download?.folderName) return; if (!game) return;
const folderPath = path.join( if (game.folderName) {
download.downloadPath ?? (await getDownloadsPath()), const folderPath = path.join(
download.folderName game.downloadPath ?? (await getDownloadsPath()),
); game.folderName
);
const metaPath = `${folderPath}.meta`; if (fs.existsSync(folderPath)) {
const deleteFile = async (filePath: string, isDirectory = false) => {
if (fs.existsSync(filePath)) {
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
fs.rm( fs.rm(
filePath, folderPath,
{ { recursive: true, force: true, maxRetries: 5, retryDelay: 200 },
recursive: isDirectory,
force: true,
maxRetries: 5,
retryDelay: 200,
},
(error) => { (error) => {
if (error) { if (error) {
logger.error(error); logger.error(error);
reject(); reject();
} }
resolve(); resolve();
} }
); );
}); });
} }
}; }
await deleteFile(folderPath, true); await gameRepository.update(
await deleteFile(metaPath); { id: gameId },
await downloadsSublevel.del(downloadKey); { downloadPath: null, folderName: null, status: null, progress: 0 }
);
}; };
registerEvent("deleteGameFolder", deleteGameFolder); registerEvent("deleteGameFolder", deleteGameFolder);

View File

@@ -1,46 +0,0 @@
import { registerEvent } from "../register-event";
import { GameShop } from "@types";
import path from "node:path";
import { GameFilesManager } from "@main/services";
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
import { FILE_EXTENSIONS_TO_EXTRACT } from "@shared";
const extractGameDownload = async (
_event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string
): Promise<boolean> => {
const gameKey = levelKeys.game(shop, objectId);
const [download, game] = await Promise.all([
downloadsSublevel.get(gameKey),
gamesSublevel.get(gameKey),
]);
if (!download || !game) return false;
await downloadsSublevel.put(gameKey, {
...download,
extracting: true,
});
const gameFilesManager = new GameFilesManager(shop, objectId);
if (
FILE_EXTENSIONS_TO_EXTRACT.some((ext) => download.folderName?.endsWith(ext))
) {
gameFilesManager.extractDownloadedFile();
} else {
gameFilesManager
.extractFilesInDirectory(
path.join(download.downloadPath, download.folderName!)
)
.then(() => {
gameFilesManager.setExtractionComplete(false);
});
}
return true;
};
registerEvent("extractGameDownload", extractGameDownload);

View File

@@ -1,21 +1,16 @@
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { gamesSublevel, downloadsSublevel, levelKeys } from "@main/level";
import type { GameShop } from "@types";
const getGameByObjectId = async ( const getGameByObjectId = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string objectId: string
) => { ) =>
const gameKey = levelKeys.game(shop, objectId); gameRepository.findOne({
const [game, download] = await Promise.all([ where: {
gamesSublevel.get(gameKey), objectID: objectId,
downloadsSublevel.get(gameKey), isDeleted: false,
]); },
});
if (!game || game.isDeleted) return null;
return { id: gameKey, ...game, download };
};
registerEvent("getGameByObjectId", getGameByObjectId); registerEvent("getGameByObjectId", getGameByObjectId);

View File

@@ -1,26 +1,17 @@
import type { LibraryGame } from "@types"; import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { downloadsSublevel, gamesSublevel } from "@main/level";
const getLibrary = async (): Promise<LibraryGame[]> => { const getLibrary = async () =>
return gamesSublevel gameRepository.find({
.iterator() where: {
.all() isDeleted: false,
.then((results) => { },
return Promise.all( relations: {
results downloadQueue: true,
.filter(([_key, game]) => game.isDeleted === false) },
.map(async ([key, game]) => { order: {
const download = await downloadsSublevel.get(key); createdAt: "desc",
},
return { });
id: key,
...game,
download: download ?? null,
};
})
);
});
};
registerEvent("getLibrary", getLibrary); registerEvent("getLibrary", getLibrary);

View File

@@ -1,14 +1,14 @@
import { shell } from "electron"; import { shell } from "electron";
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { gamesSublevel, levelKeys } from "@main/level";
import { GameShop } from "@types";
const openGameExecutablePath = async ( const openGameExecutablePath = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
shop: GameShop, gameId: number
objectId: string
) => { ) => {
const game = await gamesSublevel.get(levelKeys.game(shop, objectId)); const game = await gameRepository.findOne({
where: { id: gameId, isDeleted: false },
});
if (!game || !game.executablePath) return; if (!game || !game.executablePath) return;

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