mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-19 01:03:57 +00:00
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b8a0af8e9 | ||
|
|
31319b0811 | ||
|
|
9976812bf7 | ||
|
|
6b4d422b65 | ||
|
|
ec9db3a48f | ||
|
|
72cde368f5 | ||
|
|
bd77aee3c2 | ||
|
|
dafdda1bc7 | ||
|
|
6c6a238c5f | ||
|
|
0df18022ed | ||
|
|
6626368613 | ||
|
|
cff728bbbe | ||
|
|
3961b53d0e | ||
|
|
3f08e87eda | ||
|
|
b84c34a69c | ||
|
|
c2e1a092c9 | ||
|
|
fe03894054 | ||
|
|
893695eda0 | ||
|
|
28727600fe | ||
|
|
d892ed4f34 | ||
|
|
ea618febe2 | ||
|
|
358eac061f | ||
|
|
33f9e5729b | ||
|
|
2a7feba5dc | ||
|
|
1390f99895 | ||
|
|
daf8ee6ba2 | ||
|
|
652fe69141 | ||
|
|
61b2710219 | ||
|
|
0e67e21223 | ||
|
|
f12784b6e7 | ||
|
|
8795359c0b | ||
|
|
bcc2127a6e | ||
|
|
766c5760d7 | ||
|
|
3c8db34456 | ||
|
|
433ebf55ac | ||
|
|
b68bed97dd | ||
|
|
2a8600a841 | ||
|
|
3fd48eb784 | ||
|
|
aa19241b2b | ||
|
|
a16a75ff39 | ||
|
|
e4d97e9775 | ||
|
|
5688aaf0e8 | ||
|
|
ad5eb22553 | ||
|
|
1c3a5f4800 | ||
|
|
d8a7275512 | ||
|
|
489dd710fa | ||
|
|
0db93c7dad | ||
|
|
d9fe3c709e | ||
|
|
03a3d734f1 | ||
|
|
106d66f1a2 | ||
|
|
3ef6472825 | ||
|
|
df77d754ae | ||
|
|
6a8da74bcf | ||
|
|
1aa3bb1ad2 | ||
|
|
fdcf244f4e | ||
|
|
d8a4eaaa66 | ||
|
|
1c71c20b48 | ||
|
|
aefc5eca49 | ||
|
|
5f1b83a0d9 | ||
|
|
85efc23c25 | ||
|
|
b344a1850a | ||
|
|
497f5e7742 | ||
|
|
199b0d5b19 | ||
|
|
2554dc4c69 | ||
|
|
c4c401e054 | ||
|
|
864ff0070f | ||
|
|
da8c40d5dc | ||
|
|
a32fdf3385 | ||
|
|
c9c1750afb | ||
|
|
857063d2c7 | ||
|
|
ed699a8dee | ||
|
|
a9ec1a62bf | ||
|
|
778e921594 | ||
|
|
206886c091 | ||
|
|
af2896efc3 | ||
|
|
c7735362e0 | ||
|
|
886e176b08 | ||
|
|
f522a7c9ef | ||
|
|
69f4ce821f | ||
|
|
8bfd6e5547 | ||
|
|
1cbde684e7 | ||
|
|
e5f7e9addc | ||
|
|
6832c11d06 | ||
|
|
740cc2ec7c | ||
|
|
e12c6daa16 | ||
|
|
2ee8a64945 | ||
|
|
a54edd8b4a | ||
|
|
f7b6a91621 | ||
|
|
96dbfe8593 | ||
|
|
8513f83169 | ||
|
|
b116e29dc0 | ||
|
|
52b291fb24 | ||
|
|
260a11ba6a | ||
|
|
adf3bf38a8 | ||
|
|
b6193636dd | ||
|
|
923601bdef | ||
|
|
58855a93a8 | ||
|
|
d879f2e3df | ||
|
|
73ab3872a7 | ||
|
|
726a39a430 | ||
|
|
741364e922 | ||
|
|
92ac5b0d1a | ||
|
|
4641b1967e | ||
|
|
0bf70ffebd | ||
|
|
21cec50e2e | ||
|
|
0724a40cb7 | ||
|
|
e066ea3503 | ||
|
|
cf202c8f01 | ||
|
|
75a44bed3f | ||
|
|
c5206c68ee | ||
|
|
0b4c3a6cd2 | ||
|
|
86847ec50e | ||
|
|
e086369b13 | ||
|
|
e695d599de | ||
|
|
ad55165078 | ||
|
|
385e29262c | ||
|
|
dddc5bfd96 | ||
|
|
282bb45869 | ||
|
|
c982ebe33a | ||
|
|
08935ceabd | ||
|
|
0e7a0d1afa | ||
|
|
38dc8bdea5 | ||
|
|
c33aa3c874 | ||
|
|
afa34c74d7 | ||
|
|
cda9b8fb42 | ||
|
|
026abd7909 | ||
|
|
bc31a28eee | ||
|
|
f1624441b4 | ||
|
|
358abde963 | ||
|
|
c65fc15d99 | ||
|
|
b32226273e | ||
|
|
2e7a2a05a5 | ||
|
|
ab70ff10be | ||
|
|
6a429f9f39 | ||
|
|
b03c69b185 | ||
|
|
3fe77f4961 | ||
|
|
79be151de0 | ||
|
|
e804b92f30 | ||
|
|
cf31b36961 | ||
|
|
3156baf78e |
@@ -1,2 +1,4 @@
|
|||||||
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=
|
||||||
|
|||||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -1,5 +1,9 @@
|
|||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -43,12 +47,13 @@ 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'
|
||||||
@@ -57,14 +62,15 @@ 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: Test Upload build
|
- name: 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 }}
|
||||||
|
|||||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -1,5 +1,9 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -1,5 +1,9 @@
|
|||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: main
|
branches: main
|
||||||
@@ -45,13 +49,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'
|
||||||
@@ -60,13 +64,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
|
||||||
|
|||||||
13
.github/workflows/trigger-lp.yml
vendored
Normal file
13
.github/workflows/trigger-lp.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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 }}'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydralauncher",
|
"name": "hydralauncher",
|
||||||
"version": "3.2.0",
|
"version": "3.3.1",
|
||||||
"description": "Hydra",
|
"description": "Hydra",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "Los Broxas",
|
"author": "Los Broxas",
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"@sentry/vite-plugin": "^2.22.7",
|
"@sentry/vite-plugin": "^2.22.7",
|
||||||
"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",
|
"better-sqlite3": "^11.7.0",
|
||||||
"classic-level": "^2.0.0",
|
"classic-level": "^2.0.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
@@ -71,9 +72,11 @@
|
|||||||
"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",
|
||||||
"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",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -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,12 +26,13 @@
|
|||||||
"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": "الإعدادات",
|
||||||
@@ -40,16 +41,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}} نتيجة",
|
||||||
@@ -68,34 +69,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": "تعذر الحصول على تفاصيل المتجر.",
|
||||||
@@ -110,12 +111,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": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"",
|
||||||
@@ -123,35 +124,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": "تم حذف النسخة الاحتياطية",
|
||||||
@@ -164,61 +165,67 @@
|
|||||||
"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": "إدارة"
|
||||||
@@ -228,8 +235,8 @@
|
|||||||
"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",
|
"real_debrid_api_token_label": "رمز Real-Debrid API",
|
||||||
"quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق",
|
"quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق",
|
||||||
"launch_with_system": "تشغيل Hydra مع بدء النظام",
|
"launch_with_system": "تشغيل Hydra مع بدء النظام",
|
||||||
"general": "عام",
|
"general": "عام",
|
||||||
@@ -238,21 +245,27 @@
|
|||||||
"language": "اللغة",
|
"language": "اللغة",
|
||||||
"api_token": "رمز API",
|
"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 غير صالح",
|
"debrid_invalid_token": "رمز API غير صالح",
|
||||||
"debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا</0>",
|
"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}}\"",
|
"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": "عنوان URL لمصدر التنزيل",
|
"download_source_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": "خطأ",
|
||||||
@@ -272,13 +285,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": "الحساب",
|
||||||
@@ -296,18 +309,47 @@
|
|||||||
"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",
|
||||||
@@ -319,7 +361,7 @@
|
|||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
"title": "البرامج غير مثبتة",
|
"title": "البرامج غير مثبتة",
|
||||||
"description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك",
|
"description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك",
|
||||||
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة لينكس الخاصة بك حتى تعمل اللعبة بشكل طبيعي"
|
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة Linux الخاصة بك حتى تعمل اللعبة بشكل طبيعي"
|
||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "زر الإغلاق"
|
"close": "زر الإغلاق"
|
||||||
@@ -328,16 +370,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": "تم الحفظ بنجاح",
|
||||||
@@ -346,13 +388,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": "قائمة الأصدقاء",
|
||||||
@@ -371,19 +413,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": "الإبلاغ",
|
||||||
@@ -393,32 +435,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": "كيفية كسب نقاط الإنجازات؟"
|
||||||
@@ -428,10 +470,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": "معرفة المزيد"
|
||||||
|
|||||||
@@ -107,6 +107,12 @@
|
|||||||
"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": "Запускаць праграму пры запуску сыстэмы"
|
||||||
|
|||||||
@@ -253,6 +253,12 @@
|
|||||||
"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 линк",
|
||||||
|
|||||||
@@ -175,6 +175,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -237,6 +237,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -201,6 +201,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -185,6 +185,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"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.",
|
||||||
@@ -265,6 +267,12 @@
|
|||||||
"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",
|
||||||
@@ -327,12 +335,16 @@
|
|||||||
"enable_torbox": "Enable Torbox",
|
"enable_torbox": "Enable Torbox",
|
||||||
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
||||||
"torbox_account_linked": "TorBox account linked",
|
"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",
|
"real_debrid_account_linked": "Real-Debrid account linked",
|
||||||
"name_min_length": "Theme name must be at least 3 characters long",
|
"name_min_length": "Theme name must be at least 3 characters long",
|
||||||
"import_theme": "Import theme",
|
"import_theme": "Import theme",
|
||||||
"import_theme_description": "You will import {{theme}} from the theme store",
|
"import_theme_description": "You will import {{theme}} from the theme store",
|
||||||
"error_importing_theme": "Error importing theme",
|
"error_importing_theme": "Error importing theme",
|
||||||
"theme_imported": "Theme imported successfully"
|
"theme_imported": "Theme imported successfully",
|
||||||
|
"enable_friend_request_notifications": "When a friend request is received",
|
||||||
|
"enable_auto_install": "Download updates automatically"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
@@ -343,13 +355,17 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
"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": {
|
||||||
|
|||||||
@@ -174,6 +174,8 @@
|
|||||||
"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",
|
||||||
@@ -185,7 +187,13 @@
|
|||||||
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
|
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
|
||||||
"reset_achievements_title": "¿Estás seguro?",
|
"reset_achievements_title": "¿Estás seguro?",
|
||||||
"reset_achievements_success": "Logros reiniciados exitosamente",
|
"reset_achievements_success": "Logros reiniciados exitosamente",
|
||||||
"reset_achievements_error": "Se produjo un error al reiniciar los logros"
|
"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",
|
||||||
@@ -259,6 +267,12 @@
|
|||||||
"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",
|
||||||
@@ -290,14 +304,45 @@
|
|||||||
"no_email_account": "No has configurado un correo aún",
|
"no_email_account": "No has configurado un correo aún",
|
||||||
"no_subscription": "Disfruta Hydra de la mejor manera",
|
"no_subscription": "Disfruta Hydra de la mejor manera",
|
||||||
"no_users_blocked": "No tienes usuarios bloqueados",
|
"no_users_blocked": "No tienes usuarios bloqueados",
|
||||||
"notifications": "Notificaciones",
|
|
||||||
"renew_subscription": "Renovar Hydra Cloud",
|
"renew_subscription": "Renovar Hydra Cloud",
|
||||||
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
|
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
|
||||||
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
|
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
|
||||||
"subscription_renew_cancelled": "Está desactivada la renovación automática",
|
"subscription_renew_cancelled": "Está desactivada la renovación automática",
|
||||||
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
|
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
|
||||||
"update_email": "Actualizar correo",
|
"update_email": "Actualizar correo",
|
||||||
"update_password": "Actualizar contraseña"
|
"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",
|
||||||
@@ -308,13 +353,17 @@
|
|||||||
"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": {
|
||||||
|
|||||||
@@ -236,6 +236,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -111,6 +111,12 @@
|
|||||||
"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> بگیرید.",
|
"debrid_api_token_hint": "کلید API خود را از <ب0>اینجا</0> بگیرید.",
|
||||||
"save_changes": "ذخیره تغییرات"
|
"save_changes": "ذخیره تغییرات"
|
||||||
|
|||||||
@@ -99,7 +99,13 @@
|
|||||||
"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é",
|
||||||
|
|||||||
@@ -106,6 +106,12 @@
|
|||||||
"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": {
|
||||||
|
|||||||
@@ -175,6 +175,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -120,6 +120,12 @@
|
|||||||
"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",
|
||||||
|
"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>",
|
"debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here</0>",
|
||||||
"save_changes": "Salva modifiche"
|
"save_changes": "Salva modifiche"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -173,6 +173,12 @@
|
|||||||
"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}} жүктеу тізімде",
|
||||||
|
|||||||
@@ -111,6 +111,12 @@
|
|||||||
"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>에서 얻으세요.",
|
"debrid_api_token_hint": "API 키를 <0>이곳</0>에서 얻으세요.",
|
||||||
"save_changes": "변경 사항 저장"
|
"save_changes": "변경 사항 저장"
|
||||||
|
|||||||
@@ -201,6 +201,12 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -112,6 +112,12 @@
|
|||||||
"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.",
|
"debrid_api_token_hint": "U kunt uw API-sleutel <0>hier</0> verkrijgen.",
|
||||||
"save_changes": "Wijzigingen opslaan"
|
"save_changes": "Wijzigingen opslaan"
|
||||||
|
|||||||
@@ -120,6 +120,11 @@
|
|||||||
"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>",
|
"debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj</0>",
|
||||||
"save_changes": "Zapisz zmiany"
|
"save_changes": "Zapisz zmiany"
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Buscar jogos",
|
"search": "Buscar jogos",
|
||||||
|
|
||||||
"catalogue": "Catálogo",
|
"catalogue": "Catálogo",
|
||||||
"downloads": "Downloads",
|
"downloads": "Downloads",
|
||||||
"search_results": "Resultados da busca",
|
"search_results": "Resultados da busca",
|
||||||
@@ -165,6 +164,8 @@
|
|||||||
"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",
|
||||||
@@ -255,6 +256,12 @@
|
|||||||
"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",
|
||||||
@@ -315,12 +322,16 @@
|
|||||||
"enable_torbox": "Habilitar Torbox",
|
"enable_torbox": "Habilitar Torbox",
|
||||||
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
|
"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",
|
"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",
|
"real_debrid_account_linked": "Conta Real-Debrid associada",
|
||||||
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
|
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
|
||||||
"import_theme": "Importar tema",
|
"import_theme": "Importar tema",
|
||||||
"import_theme_description": "Você irá importar {{theme}} da loja de temas",
|
"import_theme_description": "Você irá importar {{theme}} da loja de temas",
|
||||||
"error_importing_theme": "Erro ao importar tema",
|
"error_importing_theme": "Erro ao importar tema",
|
||||||
"theme_imported": "Tema importado com sucesso"
|
"theme_imported": "Tema importado com sucesso",
|
||||||
|
"enable_friend_request_notifications": "Quando um pedido de amizade é recebido",
|
||||||
|
"enable_auto_install": "Baixar atualizações automaticamente"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download concluído",
|
"download_complete": "Download concluído",
|
||||||
@@ -329,13 +340,17 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
"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": {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Procurar jogos",
|
"search": "Procurar jogos",
|
||||||
|
|
||||||
"catalogue": "Catálogo",
|
"catalogue": "Catálogo",
|
||||||
"downloads": "Transferências",
|
"downloads": "Transferências",
|
||||||
"search_results": "Resultados da pesquisa",
|
"search_results": "Resultados da pesquisa",
|
||||||
@@ -103,7 +102,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": "Gerir",
|
"options": "Opções",
|
||||||
"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",
|
||||||
@@ -159,7 +158,27 @@
|
|||||||
"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",
|
||||||
@@ -192,7 +211,11 @@
|
|||||||
"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",
|
||||||
@@ -232,6 +255,12 @@
|
|||||||
"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",
|
||||||
@@ -250,7 +279,57 @@
|
|||||||
"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",
|
||||||
@@ -259,13 +338,17 @@
|
|||||||
"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": {
|
||||||
@@ -275,7 +358,16 @@
|
|||||||
},
|
},
|
||||||
"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"
|
||||||
@@ -352,7 +444,17 @@
|
|||||||
"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",
|
||||||
@@ -360,15 +462,25 @@
|
|||||||
"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": "Apoio Premium",
|
"premium_support": "Suporte 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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,12 @@
|
|||||||
"behavior": "Comportament",
|
"behavior": "Comportament",
|
||||||
"language": "Limbă",
|
"language": "Limbă",
|
||||||
"api_token": "Token API",
|
"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",
|
"debrid_invalid_token": "Token API invalid",
|
||||||
|
|||||||
@@ -178,12 +178,20 @@
|
|||||||
"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_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": "Игра удалена из избранного"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Активировать Hydra",
|
"title": "Активировать Hydra",
|
||||||
@@ -260,6 +268,12 @@
|
|||||||
"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": "Вставьте действительный URL JSON-файла",
|
"insert_valid_json_url": "Вставьте действительный URL JSON-файла",
|
||||||
@@ -295,7 +309,38 @@
|
|||||||
"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 привязан",
|
||||||
|
"name_min_length": "Название темы должно содержать не менее 3 символов",
|
||||||
|
"import_theme": "Импортировать тему",
|
||||||
|
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
|
||||||
|
"error_importing_theme": "Ошибка при импорте темы",
|
||||||
|
"theme_imported": "Тема успешно импортирована",
|
||||||
|
"enable_friend_request_notifications": "При получении запроса на добавление в друзья",
|
||||||
|
"enable_auto_install": "Загружать обновления автоматически"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Загрузка завершена",
|
"download_complete": "Загрузка завершена",
|
||||||
@@ -306,13 +351,17 @@
|
|||||||
"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": "Вы получили новый запрос на добавление в друзья"
|
||||||
},
|
},
|
||||||
"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": {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"catalogue": "Katalog",
|
"catalogue": "Katalog",
|
||||||
"downloads": "İndirilenler",
|
"downloads": "İndirilenler",
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
"my_library": "Kütüphanem",
|
"my_library": "Kütüphane",
|
||||||
"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…)",
|
||||||
@@ -36,13 +36,13 @@
|
|||||||
"downloads": "İndirilenler",
|
"downloads": "İndirilenler",
|
||||||
"search_results": "Arama sonuçları",
|
"search_results": "Arama sonuçları",
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
"version_available_install": "Sürüm {{version}} mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.",
|
"version_available_install": "{{version}} sürümü mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.",
|
||||||
"version_available_download": "Sürüm {{version}} mevcut. İndirmek için buraya tıklayın."
|
"version_available_download": "{{version}} sürümü 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ı) - Tamamlama: {{eta}} - Hız: {{speed}}",
|
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlanma: {{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ı)"
|
||||||
},
|
},
|
||||||
@@ -116,14 +116,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 {{game}} oyununu kütüphanenizden kaldıracaktır",
|
"remove_from_library_description": "Bu işlem sonrasında {{game}} oyunu 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\" tıklandığında çalıştırılacak dosyanın yolu",
|
"executable_section_description": "\"Oyna\" butonuna 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 veya Hydra tarafından indirilen dosyaları kaldırın",
|
"danger_zone_section_description": "Bu oyunu kütüphanenizden kaldırın veya Hydra tarafından indirilen dosyaları silin.",
|
||||||
"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",
|
||||||
@@ -141,8 +141,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ılar",
|
"achievements": "Başarımlar",
|
||||||
"achievements_count": "Başarılar {{unlockedCount}}/{{achievementsCount}}",
|
"achievements_count": "Başarımlar {{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",
|
||||||
@@ -150,15 +150,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 kayıtlı oyun bulunamadı",
|
"no_backup_preview": "Bu oyun için bir kayıt dosyası 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ıları gör",
|
"see_all_achievements": "Tüm başarımları gör",
|
||||||
"sign_in_to_see_achievements": "Başarıları görmek için giriş yapın",
|
"sign_in_to_see_achievements": "Başarımları görmek için oturum açı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",
|
||||||
@@ -169,29 +169,37 @@
|
|||||||
"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 değişiklikler girebilir (deneysel özellik)",
|
"launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine parametreler 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ılarınızı senkronize etmeyi öğrenin",
|
"achievements_not_sync": "Başarımları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ıları sıfırla",
|
"reset_achievements": "Başarımları sıfırla",
|
||||||
"reset_achievements_description": "Bu işlem {{game}} için tüm başarıları sıfırlar",
|
"reset_achievements_description": "Bu işlem {{game}} için tüm başarımları sıfırlar",
|
||||||
"reset_achievements_title": "Emin misiniz?",
|
"reset_achievements_title": "Emin misiniz?",
|
||||||
"reset_achievements_success": "Başarılar başarıyla sıfırlandı",
|
"reset_achievements_success": "Başarımlar başarıyla sıfırlandı",
|
||||||
"reset_achievements_error": "Başarılar sıfırlanamadı"
|
"reset_achievements_error": "Başarımlar 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"
|
||||||
},
|
},
|
||||||
"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 nereden soracağınızı bilmiyorsanız, bu sizin için olmamalı.",
|
"message": "Bunu nasıl edineceğini bilmiyorsan, buna sahip olmamalısın.",
|
||||||
"activate": "Aktive Et",
|
"activate": "Aktive Et",
|
||||||
"loading": "Yükleniyor…"
|
"loading": "Yükleniyor…"
|
||||||
},
|
},
|
||||||
@@ -206,7 +214,7 @@
|
|||||||
"cancel": "İptal Et",
|
"cancel": "İptal Et",
|
||||||
"filter": "İndirilen oyunları filtrele",
|
"filter": "İndirilen oyunları filtrele",
|
||||||
"remove": "Kaldır",
|
"remove": "Kaldır",
|
||||||
"downloading_metadata": "Metadata indiriliyor…",
|
"downloading_metadata": "Meta verileri 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?",
|
||||||
@@ -231,7 +239,7 @@
|
|||||||
"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ırken gizlemeyin",
|
"quit_app_instead_hiding": "Hydra'yı kapatınca sistem tepsisine gitmesin",
|
||||||
"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ış",
|
||||||
@@ -250,6 +258,12 @@
|
|||||||
"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",
|
||||||
@@ -277,11 +291,56 @@
|
|||||||
"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ı kilidi açıldığında",
|
"enable_achievement_notifications": "Bir başarım 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ı açıklamalarını kilitlenmeden önce göster"
|
"show_hidden_achievement_description": "Gizli başarım açı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ğlando",
|
||||||
|
"real_debrid_account_linked": "Real-Debrid hesabı bağlando",
|
||||||
|
"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ı"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "İndirme tamamlandı",
|
"download_complete": "İndirme tamamlandı",
|
||||||
@@ -289,17 +348,19 @@
|
|||||||
"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": "Sürüm {{version}} mevcut",
|
"new_update_available": "{{version}} sürümü 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ı kilidi açıldı",
|
"notification_achievement_unlocked_title": "{{game}} için başarım kilidi açıldı",
|
||||||
"notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarılar açıldı"
|
"notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarım açı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",
|
||||||
@@ -332,7 +393,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ı. Çıkış yaptığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Çıkışa devam etmek istiyor musunuz?",
|
"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?",
|
||||||
"add_friends": "Arkadaş Ekle",
|
"add_friends": "Arkadaş Ekle",
|
||||||
"add": "Ekle",
|
"add": "Ekle",
|
||||||
"friend_code": "Arkadaş kodu",
|
"friend_code": "Arkadaş kodu",
|
||||||
@@ -383,39 +444,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ılar",
|
"achievements": "Başarımlar",
|
||||||
"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ılar açıldı",
|
"achievements_unlocked": "Başarımlar açıldı",
|
||||||
"earned_points": "Kazanılan puanlar",
|
"earned_points": "Kazanılan puanlar",
|
||||||
"show_achievements_on_profile": "Başarılarınızı profilinizde gösterin",
|
"show_achievements_on_profile": "Başarımları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ı açıldı",
|
"achievement_unlocked": "Başarım açıldı",
|
||||||
"user_achievements": "{{displayName}}'in Başarıları",
|
"user_achievements": "{{displayName}} oyununun Başarımları",
|
||||||
"your_achievements": "Başarılarınız",
|
"your_achievements": "Başarımları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ı açıldı",
|
"new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarım açıldı",
|
||||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarı",
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarım",
|
||||||
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarı açıldı",
|
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarım açıldı",
|
||||||
"hidden_achievement_tooltip": "Bu gizli bir başarıdır",
|
"hidden_achievement_tooltip": "Bu gizli bir başarımdır",
|
||||||
"achievement_earn_points": "Bu başarı ile {{points}} puan kazanın",
|
"achievement_earn_points": "Bu başarım 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ı puanları nasıl kazanılır?"
|
"how_to_earn_achievements_points": "Başarım 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ılarınızı buluta kaydedin",
|
"cloud_achievements": "Başarımları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ılarınızı diğer kullanıcılarla karşılaştırın ve gösterin",
|
"show_and_compare_achievements": "Başarımları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!",
|
||||||
|
|||||||
@@ -176,6 +176,12 @@
|
|||||||
"import": "Імпортувати",
|
"import": "Імпортувати",
|
||||||
"insert_valid_json_url": "Вставте дійсний URL JSON-файлу",
|
"insert_valid_json_url": "Вставте дійсний URL JSON-файлу",
|
||||||
"language": "Мова",
|
"language": "Мова",
|
||||||
|
"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": "Видаліть усі джерела завантаження",
|
||||||
"api_token": "API-токен",
|
"api_token": "API-токен",
|
||||||
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
|
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
|
||||||
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
||||||
|
|||||||
@@ -226,6 +226,12 @@
|
|||||||
"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}} 个下载",
|
||||||
|
|||||||
@@ -31,3 +31,5 @@ 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;
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import updater from "electron-updater";
|
|||||||
|
|
||||||
const { autoUpdater } = updater;
|
const { autoUpdater } = updater;
|
||||||
|
|
||||||
const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
|
export const restartAndInstallUpdate = () => {
|
||||||
autoUpdater.removeAllListeners();
|
autoUpdater.removeAllListeners();
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
autoUpdater.quitAndInstall(false);
|
autoUpdater.quitAndInstall(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("restartAndInstallUpdate", restartAndInstallUpdate);
|
const restartAndInstallUpdateEvent = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent
|
||||||
|
) => restartAndInstallUpdate();
|
||||||
|
|
||||||
|
registerEvent("restartAndInstallUpdate", restartAndInstallUpdateEvent);
|
||||||
|
|||||||
@@ -1,44 +1,8 @@
|
|||||||
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
|
import { CloudSync } 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 axios from "axios";
|
import { t } from "i18next";
|
||||||
import os from "node:os";
|
import { format } from "date-fns";
|
||||||
import { backupsPath } from "@main/constants";
|
|
||||||
import { app } from "electron";
|
|
||||||
import { normalizePath } from "@main/helpers";
|
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
|
||||||
|
|
||||||
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,
|
||||||
@@ -46,61 +10,15 @@ const uploadSaveGame = async (
|
|||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
downloadOptionTitle: string | null
|
downloadOptionTitle: string | null
|
||||||
) => {
|
) => {
|
||||||
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
return CloudSync.uploadSaveGame(
|
||||||
|
|
||||||
const bundleLocation = await bundleBackup(
|
|
||||||
shop,
|
|
||||||
objectId,
|
objectId,
|
||||||
game?.winePrefixPath ?? null
|
shop,
|
||||||
|
downloadOptionTitle,
|
||||||
|
t("backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ 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 "./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";
|
||||||
@@ -58,6 +60,7 @@ 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";
|
||||||
|
|||||||
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { levelKeys, gamesSublevel } from "@main/level";
|
||||||
|
import type { GameShop } from "@types";
|
||||||
|
|
||||||
|
const toggleAutomaticCloudSync = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) => {
|
||||||
|
const gameKey = levelKeys.game(shop, objectId);
|
||||||
|
|
||||||
|
const game = await gamesSublevel.get(gameKey);
|
||||||
|
|
||||||
|
if (!game) return;
|
||||||
|
|
||||||
|
await gamesSublevel.put(gameKey, {
|
||||||
|
...game,
|
||||||
|
automaticCloudSync,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("toggleAutomaticCloudSync", toggleAutomaticCloudSync);
|
||||||
22
src/main/events/misc/get-badges.ts
Normal file
22
src/main/events/misc/get-badges.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Badge } from "@types";
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { HydraApi } from "@main/services";
|
||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
|
|
||||||
|
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||||
|
const language = await db
|
||||||
|
.get<string, string>(levelKeys.language, {
|
||||||
|
valueEncoding: "utf-8",
|
||||||
|
})
|
||||||
|
.then((language) => language || "en");
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
locale: language,
|
||||||
|
});
|
||||||
|
|
||||||
|
return HydraApi.get<Badge[]>(`/badges?${params.toString()}`, null, {
|
||||||
|
needsAuth: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("getBadges", getBadges);
|
||||||
@@ -1,17 +1,59 @@
|
|||||||
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { HydraApi } from "@main/services";
|
import { HydraApi, WindowManager } from "@main/services";
|
||||||
|
import { publishNewFriendRequestNotification } from "@main/services/notifications";
|
||||||
import { UserNotLoggedInError } from "@shared";
|
import { UserNotLoggedInError } from "@shared";
|
||||||
import type { FriendRequestSync } from "@types";
|
import type { FriendRequestSync } from "@types";
|
||||||
|
|
||||||
const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => {
|
interface SyncState {
|
||||||
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`).catch(
|
friendRequestCount: number | null;
|
||||||
(err) => {
|
tick: number;
|
||||||
if (err instanceof UserNotLoggedInError) {
|
}
|
||||||
return { friendRequests: [] };
|
|
||||||
}
|
const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes
|
||||||
throw err;
|
|
||||||
}
|
const syncState: SyncState = {
|
||||||
);
|
friendRequestCount: null,
|
||||||
|
tick: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("syncFriendRequests", syncFriendRequests);
|
const syncFriendRequests = async () => {
|
||||||
|
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`)
|
||||||
|
.then((res) => {
|
||||||
|
if (
|
||||||
|
syncState.friendRequestCount != null &&
|
||||||
|
syncState.friendRequestCount < res.friendRequestCount
|
||||||
|
) {
|
||||||
|
publishNewFriendRequestNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncState.friendRequestCount = res.friendRequestCount;
|
||||||
|
|
||||||
|
WindowManager.mainWindow?.webContents.send(
|
||||||
|
"on-sync-friend-requests",
|
||||||
|
res
|
||||||
|
);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err instanceof UserNotLoggedInError) {
|
||||||
|
return { friendRequestCount: 0 } as FriendRequestSync;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncFriendRequestsEvent = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||||
|
return syncFriendRequests();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const watchFriendRequests = async () => {
|
||||||
|
if (syncState.tick % ticksToUpdate === 0) {
|
||||||
|
await syncFriendRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncState.tick++;
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("syncFriendRequests", syncFriendRequestsEvent);
|
||||||
|
|||||||
11
src/main/events/user/get-auth.ts
Normal file
11
src/main/events/user/get-auth.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
|
import type { Auth } from "@types";
|
||||||
|
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
|
||||||
|
const getAuth = async (_event: Electron.IpcMainInvokeEvent) =>
|
||||||
|
db.get<string, Auth>(levelKeys.auth, {
|
||||||
|
valueEncoding: "json",
|
||||||
|
});
|
||||||
|
|
||||||
|
registerEvent("getAuth", getAuth);
|
||||||
@@ -14,4 +14,5 @@ export const levelKeys = {
|
|||||||
userPreferences: "userPreferences",
|
userPreferences: "userPreferences",
|
||||||
language: "language",
|
language: "language",
|
||||||
sqliteMigrationDone: "sqliteMigrationDone",
|
sqliteMigrationDone: "sqliteMigrationDone",
|
||||||
|
screenState: "screenState",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ export const loadState = async () => {
|
|||||||
game.uri !== null
|
game.uri !== null
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("downloadsToSeed", downloadsToSeed);
|
|
||||||
|
|
||||||
await DownloadManager.startRPC(nextItemOnQueue, downloadsToSeed);
|
await DownloadManager.startRPC(nextItemOnQueue, downloadsToSeed);
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
@@ -132,7 +130,9 @@ const migrateFromSqlite = async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (rest.language) {
|
if (rest.language) {
|
||||||
await db.put(levelKeys.language, rest.language);
|
await db.put<string, string>(levelKeys.language, rest.language, {
|
||||||
|
valueEncoding: "utf-8",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
111
src/main/services/cloud-sync.ts
Normal file
111
src/main/services/cloud-sync.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { levelKeys, gamesSublevel, db } from "@main/level";
|
||||||
|
import { app } from "electron";
|
||||||
|
import path from "node:path";
|
||||||
|
import * as tar from "tar";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import type { GameShop, User } from "@types";
|
||||||
|
import { backupsPath } from "@main/constants";
|
||||||
|
import { HydraApi } from "./hydra-api";
|
||||||
|
import { normalizePath } from "@main/helpers";
|
||||||
|
import { logger } from "./logger";
|
||||||
|
import { WindowManager } from "./window-manager";
|
||||||
|
import axios from "axios";
|
||||||
|
import { Ludusavi } from "./ludusavi";
|
||||||
|
import { SubscriptionRequiredError } from "@shared";
|
||||||
|
|
||||||
|
export class CloudSync {
|
||||||
|
private static async bundleBackup(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async uploadSaveGame(
|
||||||
|
objectId: string,
|
||||||
|
shop: GameShop,
|
||||||
|
downloadOptionTitle: string | null,
|
||||||
|
label?: string
|
||||||
|
) {
|
||||||
|
const hasActiveSubscription = await db
|
||||||
|
.get<string, User>(levelKeys.user, { valueEncoding: "json" })
|
||||||
|
.then((user) => {
|
||||||
|
const expiresAt = new Date(user?.subscription?.expiresAt ?? 0);
|
||||||
|
return expiresAt > new Date();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasActiveSubscription) {
|
||||||
|
throw new SubscriptionRequiredError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
||||||
|
|
||||||
|
const bundleLocation = await this.bundleBackup(
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
game?.winePrefixPath ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
const stat = await fs.promises.stat(bundleLocation);
|
||||||
|
|
||||||
|
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(),
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileBuffer = await fs.promises.readFile(bundleLocation);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,13 @@ import { Downloader, DownloadError } from "@shared";
|
|||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import { publishDownloadCompleteNotification } from "../notifications";
|
import { publishDownloadCompleteNotification } from "../notifications";
|
||||||
import type { Download, DownloadProgress, UserPreferences } from "@types";
|
import type { Download, DownloadProgress, UserPreferences } from "@types";
|
||||||
import { GofileApi, QiwiApi, DatanodesApi, MediafireApi } from "../hosters";
|
import {
|
||||||
|
GofileApi,
|
||||||
|
QiwiApi,
|
||||||
|
DatanodesApi,
|
||||||
|
MediafireApi,
|
||||||
|
PixelDrainApi,
|
||||||
|
} from "../hosters";
|
||||||
import { PythonRPC } from "../python-rpc";
|
import { PythonRPC } from "../python-rpc";
|
||||||
import {
|
import {
|
||||||
LibtorrentPayload,
|
LibtorrentPayload,
|
||||||
@@ -283,11 +289,12 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
case Downloader.PixelDrain: {
|
case Downloader.PixelDrain: {
|
||||||
const id = download.uri.split("/").pop();
|
const id = download.uri.split("/").pop();
|
||||||
|
const downloadUrl = await PixelDrainApi.getDownloadUrl(id!);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: "start",
|
action: "start",
|
||||||
game_id: downloadId,
|
game_id: downloadId,
|
||||||
url: `https://cdn.pd5-gamedriveorg.workers.dev/api/file/${id}`,
|
url: downloadUrl,
|
||||||
save_path: download.downloadPath,
|
save_path: download.downloadPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {
|
|||||||
TorBoxAddTorrentRequest,
|
TorBoxAddTorrentRequest,
|
||||||
TorBoxRequestLinkRequest,
|
TorBoxRequestLinkRequest,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
|
import { appVersion } from "@main/constants";
|
||||||
|
|
||||||
export class TorBoxClient {
|
export class TorBoxClient {
|
||||||
private static instance: AxiosInstance;
|
private static instance: AxiosInstance;
|
||||||
@@ -18,6 +19,7 @@ export class TorBoxClient {
|
|||||||
baseURL: this.baseURL,
|
baseURL: this.baseURL,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${apiToken}`,
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
"User-Agent": `Hydra/${appVersion}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,71 @@
|
|||||||
import axios, { AxiosResponse } from "axios";
|
import axios, { AxiosResponse } from "axios";
|
||||||
|
import { wrapper } from "axios-cookiejar-support";
|
||||||
|
import { CookieJar } from "tough-cookie";
|
||||||
|
|
||||||
export class DatanodesApi {
|
export class DatanodesApi {
|
||||||
private static readonly session = axios.create({});
|
private static readonly jar = new CookieJar();
|
||||||
|
|
||||||
|
private static readonly session = wrapper(
|
||||||
|
axios.create({
|
||||||
|
jar: DatanodesApi.jar,
|
||||||
|
withCredentials: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
|
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
|
||||||
const parsedUrl = new URL(downloadUrl);
|
try {
|
||||||
const pathSegments = parsedUrl.pathname.split("/");
|
const parsedUrl = new URL(downloadUrl);
|
||||||
|
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
|
||||||
|
const fileCode = pathSegments[0];
|
||||||
|
|
||||||
const fileCode = decodeURIComponent(pathSegments[1]);
|
await this.jar.setCookie("lang=english;", "https://datanodes.to");
|
||||||
const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]);
|
|
||||||
|
|
||||||
const payload = new URLSearchParams({
|
const payload = new URLSearchParams({
|
||||||
op: "download2",
|
op: "download2",
|
||||||
id: fileCode,
|
id: fileCode,
|
||||||
rand: "",
|
method_free: "Free Download >>",
|
||||||
referer: "https://datanodes.to/download",
|
dl: "1",
|
||||||
method_free: "Free Download >>",
|
});
|
||||||
method_premium: "",
|
|
||||||
adblock_detected: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const response: AxiosResponse = await this.session.post(
|
const response: AxiosResponse = await this.session.post(
|
||||||
"https://datanodes.to/download",
|
"https://datanodes.to/download",
|
||||||
payload,
|
payload,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"User-Agent":
|
||||||
Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`,
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
|
||||||
Host: "datanodes.to",
|
Referer: "https://datanodes.to/download",
|
||||||
Origin: "https://datanodes.to",
|
Origin: "https://datanodes.to",
|
||||||
Referer: "https://datanodes.to/download",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"User-Agent":
|
},
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
|
maxRedirects: 0,
|
||||||
},
|
validateStatus: (status: number) => status === 302 || status < 400,
|
||||||
maxRedirects: 0,
|
}
|
||||||
validateStatus: (status: number) => status === 302 || status < 400,
|
);
|
||||||
|
|
||||||
|
if (response.status === 302) {
|
||||||
|
return response.headers["location"];
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status === 302) {
|
if (typeof response.data === "object" && response.data.url) {
|
||||||
return response.headers["location"];
|
return decodeURIComponent(response.data.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlContent = String(response.data);
|
||||||
|
if (!htmlContent) {
|
||||||
|
throw new Error("Empty response received");
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadLinkRegex = /href=["'](https:\/\/[^"']+)["']/;
|
||||||
|
const downloadLinkMatch = downloadLinkRegex.exec(htmlContent);
|
||||||
|
if (downloadLinkMatch) {
|
||||||
|
return downloadLinkMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Failed to get the download link");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching download URL:", error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ export * from "./gofile";
|
|||||||
export * from "./qiwi";
|
export * from "./qiwi";
|
||||||
export * from "./datanodes";
|
export * from "./datanodes";
|
||||||
export * from "./mediafire";
|
export * from "./mediafire";
|
||||||
|
export * from "./pixeldrain";
|
||||||
|
|||||||
42
src/main/services/hosters/pixeldrain.ts
Normal file
42
src/main/services/hosters/pixeldrain.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export class PixelDrainApi {
|
||||||
|
private static readonly browserHeaders = {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
|
||||||
|
Accept:
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
|
"Accept-Language": "en-US,en;q=0.9",
|
||||||
|
"Accept-Encoding": "gzip, deflate, br",
|
||||||
|
DNT: "1",
|
||||||
|
Connection: "keep-alive",
|
||||||
|
"Sec-Fetch-Dest": "document",
|
||||||
|
"Sec-Fetch-Mode": "navigate",
|
||||||
|
"Sec-Fetch-Site": "none",
|
||||||
|
"Sec-Fetch-User": "?1",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static async getDownloadUrl(fileId: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://pd.cybar.xyz/${fileId}`, {
|
||||||
|
headers: this.browserHeaders,
|
||||||
|
maxRedirects: 0,
|
||||||
|
validateStatus: (status) =>
|
||||||
|
status === 301 || status === 302 || status === 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
response.headers.location ||
|
||||||
|
response.status === 301 ||
|
||||||
|
response.status === 302
|
||||||
|
) {
|
||||||
|
return response.headers.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No redirect URL found (status: ${response.status})`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching PixelDrain URL:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import { UserNotLoggedInError, SubscriptionRequiredError } from "@shared";
|
|||||||
import { omit } from "lodash-es";
|
import { omit } from "lodash-es";
|
||||||
import { appVersion } from "@main/constants";
|
import { appVersion } from "@main/constants";
|
||||||
import { getUserData } from "./user/get-user-data";
|
import { getUserData } from "./user/get-user-data";
|
||||||
import { isFuture, isToday } from "date-fns";
|
|
||||||
import { db } from "@main/level";
|
import { db } from "@main/level";
|
||||||
import { levelKeys } from "@main/level/sublevels";
|
import { levelKeys } from "@main/level/sublevels";
|
||||||
import type { Auth, User } from "@types";
|
import type { Auth, User } from "@types";
|
||||||
@@ -47,8 +46,8 @@ export class HydraApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static hasActiveSubscription() {
|
private static hasActiveSubscription() {
|
||||||
const expiresAt = this.userAuth.subscription?.expiresAt;
|
const expiresAt = new Date(this.userAuth.subscription?.expiresAt ?? 0);
|
||||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
return expiresAt > new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleExternalAuth(uri: string) {
|
static async handleExternalAuth(uri: string) {
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export * from "./process-watcher";
|
|||||||
export * from "./main-loop";
|
export * from "./main-loop";
|
||||||
export * from "./hydra-api";
|
export * from "./hydra-api";
|
||||||
export * from "./ludusavi";
|
export * from "./ludusavi";
|
||||||
|
export * from "./cloud-sync";
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ import { DownloadManager } from "./download";
|
|||||||
import { watchProcesses } from "./process-watcher";
|
import { watchProcesses } from "./process-watcher";
|
||||||
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
|
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
|
||||||
import { UpdateManager } from "./update-manager";
|
import { UpdateManager } from "./update-manager";
|
||||||
|
import { watchFriendRequests } from "@main/events/profile/sync-friend-requests";
|
||||||
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
|
|
||||||
export const startMainLoop = async () => {
|
export const startMainLoop = async () => {
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
watchProcesses(),
|
watchProcesses(),
|
||||||
|
watchFriendRequests(),
|
||||||
DownloadManager.watchDownloads(),
|
DownloadManager.watchDownloads(),
|
||||||
AchievementWatcherManager.watchAchievements(),
|
AchievementWatcherManager.watchAchievements(),
|
||||||
DownloadManager.getSeedStatus(),
|
DownloadManager.getSeedStatus(),
|
||||||
UpdateManager.checkForUpdatePeriodically(),
|
UpdateManager.checkForUpdatePeriodically(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await sleep(1500);
|
await sleep(MAIN_LOOP_INTERVAL);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { logger } from "../logger";
|
|||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import type { Game, UserPreferences } from "@types";
|
import type { Game, UserPreferences } from "@types";
|
||||||
import { db, levelKeys } from "@main/level";
|
import { db, levelKeys } from "@main/level";
|
||||||
|
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
||||||
|
|
||||||
async function downloadImage(url: string | null) {
|
async function downloadImage(url: string | null) {
|
||||||
if (!url) return undefined;
|
if (!url) return undefined;
|
||||||
@@ -72,10 +73,33 @@ export const publishNotificationUpdateReadyToInstall = async (
|
|||||||
ns: "notifications",
|
ns: "notifications",
|
||||||
}),
|
}),
|
||||||
icon: trayIcon,
|
icon: trayIcon,
|
||||||
}).show();
|
})
|
||||||
|
.on("click", () => {
|
||||||
|
restartAndInstallUpdate();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const publishNewFriendRequestNotification = async () => {};
|
export const publishNewFriendRequestNotification = async () => {
|
||||||
|
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||||
|
levelKeys.userPreferences,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userPreferences?.friendRequestNotificationsEnabled) return;
|
||||||
|
|
||||||
|
new Notification({
|
||||||
|
title: t("new_friend_request_title", {
|
||||||
|
ns: "notifications",
|
||||||
|
}),
|
||||||
|
body: t("new_friend_request_description", {
|
||||||
|
ns: "notifications",
|
||||||
|
}),
|
||||||
|
icon: trayIcon,
|
||||||
|
}).show();
|
||||||
|
};
|
||||||
|
|
||||||
export const publishCombinedNewAchievementNotification = async (
|
export const publishCombinedNewAchievementNotification = async (
|
||||||
achievementCount,
|
achievementCount,
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import axios from "axios";
|
|||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
import { ProcessPayload } from "./download/types";
|
import { ProcessPayload } from "./download/types";
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
import { gamesSublevel, levelKeys } from "@main/level";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import { CloudSync } from "./cloud-sync";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
const commands = {
|
const commands = {
|
||||||
findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`,
|
findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`,
|
||||||
@@ -225,6 +228,18 @@ function onOpenGame(game: Game) {
|
|||||||
|
|
||||||
if (game.remoteId) {
|
if (game.remoteId) {
|
||||||
updateGamePlaytime(game, 0, new Date()).catch(() => {});
|
updateGamePlaytime(game, 0, new Date()).catch(() => {});
|
||||||
|
|
||||||
|
if (game.automaticCloudSync) {
|
||||||
|
CloudSync.uploadSaveGame(
|
||||||
|
game.objectId,
|
||||||
|
game.shop,
|
||||||
|
null,
|
||||||
|
t("automatic_backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
||||||
}
|
}
|
||||||
@@ -287,6 +302,18 @@ const onCloseGame = (game: Game) => {
|
|||||||
performance.now() - gamePlaytime.lastSyncTick,
|
performance.now() - gamePlaytime.lastSyncTick,
|
||||||
game.lastTimePlayed!
|
game.lastTimePlayed!
|
||||||
).catch(() => {});
|
).catch(() => {});
|
||||||
|
|
||||||
|
if (game.automaticCloudSync) {
|
||||||
|
CloudSync.uploadSaveGame(
|
||||||
|
game.objectId,
|
||||||
|
game.shop,
|
||||||
|
null,
|
||||||
|
t("automatic_backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createGame(game).catch(() => {});
|
createGame(game).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import updater, { UpdateInfo } from "electron-updater";
|
import updater, { UpdateInfo } from "electron-updater";
|
||||||
import { logger, WindowManager } from "@main/services";
|
import { logger, WindowManager } from "@main/services";
|
||||||
import { AppUpdaterEvent } from "@types";
|
import { AppUpdaterEvent, UserPreferences } from "@types";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
|
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
|
||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
const isAutoInstallAvailable =
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
process.platform !== "darwin" && process.env.PORTABLE_EXECUTABLE_FILE == null;
|
|
||||||
|
|
||||||
const { autoUpdater } = updater;
|
const { autoUpdater } = updater;
|
||||||
const sendEventsForDebug = false;
|
const sendEventsForDebug = false;
|
||||||
|
const ticksToUpdate = (50 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 50 minutes
|
||||||
|
|
||||||
export class UpdateManager {
|
export class UpdateManager {
|
||||||
private static hasNotified = false;
|
private static hasNotified = false;
|
||||||
@@ -16,7 +16,7 @@ export class UpdateManager {
|
|||||||
private static checkTick = 0;
|
private static checkTick = 0;
|
||||||
|
|
||||||
private static mockValuesForDebug() {
|
private static mockValuesForDebug() {
|
||||||
this.sendEvent({ type: "update-available", info: { version: "1.3.0" } });
|
this.sendEvent({ type: "update-available", info: { version: "3.3.1" } });
|
||||||
this.sendEvent({ type: "update-downloaded" });
|
this.sendEvent({ type: "update-downloaded" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,27 @@ export class UpdateManager {
|
|||||||
WindowManager.mainWindow?.webContents.send("autoUpdaterEvent", event);
|
WindowManager.mainWindow?.webContents.send("autoUpdaterEvent", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static checkForUpdates() {
|
private static async isAutoInstallEnabled() {
|
||||||
|
if (process.platform === "darwin") return false;
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
return process.env.PORTABLE_EXECUTABLE_FILE == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||||
|
levelKeys.userPreferences,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return userPreferences?.enableAutoInstall === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async checkForUpdates() {
|
||||||
autoUpdater
|
autoUpdater
|
||||||
.once("update-available", (info: UpdateInfo) => {
|
.once("update-available", (info: UpdateInfo) => {
|
||||||
this.sendEvent({ type: "update-available", info });
|
this.sendEvent({ type: "update-available", info });
|
||||||
@@ -39,6 +59,8 @@ export class UpdateManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isAutoInstallAvailable = await this.isAutoInstallEnabled();
|
||||||
|
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
autoUpdater.autoDownload = isAutoInstallAvailable;
|
autoUpdater.autoDownload = isAutoInstallAvailable;
|
||||||
autoUpdater.checkForUpdates().then((result) => {
|
autoUpdater.checkForUpdates().then((result) => {
|
||||||
@@ -52,7 +74,7 @@ export class UpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static checkForUpdatePeriodically() {
|
public static checkForUpdatePeriodically() {
|
||||||
if (this.checkTick % 2000 == 0) {
|
if (this.checkTick % ticksToUpdate == 0) {
|
||||||
this.checkForUpdates();
|
this.checkForUpdates();
|
||||||
}
|
}
|
||||||
this.checkTick++;
|
this.checkTick++;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { HydraApi } from "./hydra-api";
|
|||||||
import UserAgent from "user-agents";
|
import UserAgent from "user-agents";
|
||||||
import { db, gamesSublevel, levelKeys } from "@main/level";
|
import { db, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import { slice, sortBy } from "lodash-es";
|
import { slice, sortBy } from "lodash-es";
|
||||||
import type { UserPreferences } from "@types";
|
import type { ScreenState, UserPreferences } from "@types";
|
||||||
import { AuthPage } from "@shared";
|
import { AuthPage } from "@shared";
|
||||||
import { isStaging } from "@main/constants";
|
import { isStaging } from "@main/constants";
|
||||||
|
|
||||||
@@ -26,6 +26,28 @@ export class WindowManager {
|
|||||||
|
|
||||||
private static readonly editorWindows: Map<string, BrowserWindow> = new Map();
|
private static readonly editorWindows: Map<string, BrowserWindow> = new Map();
|
||||||
|
|
||||||
|
private static initialConfigInitializationMainWindow: Electron.BrowserWindowConstructorOptions =
|
||||||
|
{
|
||||||
|
width: 1200,
|
||||||
|
height: 720,
|
||||||
|
minWidth: 1024,
|
||||||
|
minHeight: 540,
|
||||||
|
backgroundColor: "#1c1c1c",
|
||||||
|
titleBarStyle: process.platform === "linux" ? "default" : "hidden",
|
||||||
|
icon,
|
||||||
|
trafficLightPosition: { x: 16, y: 16 },
|
||||||
|
titleBarOverlay: {
|
||||||
|
symbolColor: "#DADBE1",
|
||||||
|
color: "#00000000",
|
||||||
|
height: 34,
|
||||||
|
},
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, "../preload/index.mjs"),
|
||||||
|
sandbox: false,
|
||||||
|
},
|
||||||
|
show: false,
|
||||||
|
};
|
||||||
|
|
||||||
private static loadMainWindowURL(hash = "") {
|
private static loadMainWindowURL(hash = "") {
|
||||||
// HMR for renderer base on electron-vite cli.
|
// HMR for renderer base on electron-vite cli.
|
||||||
// Load the remote URL for development or the local html file for production.
|
// Load the remote URL for development or the local html file for production.
|
||||||
@@ -43,29 +65,46 @@ export class WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createMainWindow() {
|
private static async saveScreenConfig(configScreenWhenClosed: ScreenState) {
|
||||||
|
await db.put(levelKeys.screenState, configScreenWhenClosed, {
|
||||||
|
valueEncoding: "json",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async loadScreenConfig() {
|
||||||
|
const data = await db.get<string, ScreenState | undefined>(
|
||||||
|
levelKeys.screenState,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data ?? { isMaximized: false, height: 720, width: 1200 };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static updateInitialConfig(
|
||||||
|
newConfig: Partial<Electron.BrowserWindowConstructorOptions>
|
||||||
|
) {
|
||||||
|
this.initialConfigInitializationMainWindow = {
|
||||||
|
...this.initialConfigInitializationMainWindow,
|
||||||
|
...newConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async createMainWindow() {
|
||||||
if (this.mainWindow) return;
|
if (this.mainWindow) return;
|
||||||
|
|
||||||
this.mainWindow = new BrowserWindow({
|
const { isMaximized = false, ...configWithoutMaximized } =
|
||||||
width: 1200,
|
await this.loadScreenConfig();
|
||||||
height: 720,
|
|
||||||
minWidth: 1024,
|
this.updateInitialConfig(configWithoutMaximized);
|
||||||
minHeight: 540,
|
|
||||||
backgroundColor: "#1c1c1c",
|
this.mainWindow = new BrowserWindow(
|
||||||
titleBarStyle: process.platform === "linux" ? "default" : "hidden",
|
this.initialConfigInitializationMainWindow
|
||||||
icon,
|
);
|
||||||
trafficLightPosition: { x: 16, y: 16 },
|
|
||||||
titleBarOverlay: {
|
if (isMaximized) {
|
||||||
symbolColor: "#DADBE1",
|
this.mainWindow.maximize();
|
||||||
color: "#151515",
|
}
|
||||||
height: 34,
|
|
||||||
},
|
|
||||||
webPreferences: {
|
|
||||||
preload: path.join(__dirname, "../preload/index.mjs"),
|
|
||||||
sandbox: false,
|
|
||||||
},
|
|
||||||
show: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
this.mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
||||||
(details, callback) => {
|
(details, callback) => {
|
||||||
@@ -143,9 +182,26 @@ export class WindowManager {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.mainWindow) {
|
||||||
|
const lastBounds = this.mainWindow.getBounds();
|
||||||
|
const isMaximized = this.mainWindow.isMaximized() ?? false;
|
||||||
|
const screenConfig = isMaximized
|
||||||
|
? {
|
||||||
|
x: undefined,
|
||||||
|
y: undefined,
|
||||||
|
height: this.initialConfigInitializationMainWindow.height ?? 720,
|
||||||
|
width: this.initialConfigInitializationMainWindow.width ?? 1200,
|
||||||
|
isMaximized: true,
|
||||||
|
}
|
||||||
|
: { ...lastBounds, isMaximized };
|
||||||
|
|
||||||
|
await this.saveScreenConfig(screenConfig);
|
||||||
|
}
|
||||||
|
|
||||||
if (userPreferences?.preferQuitInsteadOfHiding) {
|
if (userPreferences?.preferQuitInsteadOfHiding) {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowManager.mainWindow?.setProgressBar(-1);
|
WindowManager.mainWindow?.setProgressBar(-1);
|
||||||
WindowManager.mainWindow = null;
|
WindowManager.mainWindow = null;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type {
|
|||||||
SeedingStatus,
|
SeedingStatus,
|
||||||
GameAchievement,
|
GameAchievement,
|
||||||
Theme,
|
Theme,
|
||||||
|
FriendRequestSync,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AuthPage, CatalogueCategory } from "@shared";
|
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
@@ -101,6 +102,17 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("putDownloadSource", objectIds),
|
ipcRenderer.invoke("putDownloadSource", objectIds),
|
||||||
|
|
||||||
/* Library */
|
/* Library */
|
||||||
|
toggleAutomaticCloudSync: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) =>
|
||||||
|
ipcRenderer.invoke(
|
||||||
|
"toggleAutomaticCloudSync",
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
automaticCloudSync
|
||||||
|
),
|
||||||
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
|
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
|
||||||
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
|
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
|
||||||
createGameShortcut: (shop: GameShop, objectId: string) =>
|
createGameShortcut: (shop: GameShop, objectId: string) =>
|
||||||
@@ -266,6 +278,7 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
showItemInFolder: (path: string) =>
|
showItemInFolder: (path: string) =>
|
||||||
ipcRenderer.invoke("showItemInFolder", path),
|
ipcRenderer.invoke("showItemInFolder", path),
|
||||||
getFeatures: () => ipcRenderer.invoke("getFeatures"),
|
getFeatures: () => ipcRenderer.invoke("getFeatures"),
|
||||||
|
getBadges: () => ipcRenderer.invoke("getBadges"),
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
|
|
||||||
/* Auto update */
|
/* Auto update */
|
||||||
@@ -294,6 +307,15 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("processProfileImage", imagePath),
|
ipcRenderer.invoke("processProfileImage", imagePath),
|
||||||
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
|
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
|
||||||
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"),
|
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"),
|
||||||
|
onSyncFriendRequests: (cb: (friendRequests: FriendRequestSync) => void) => {
|
||||||
|
const listener = (
|
||||||
|
_event: Electron.IpcRendererEvent,
|
||||||
|
friendRequests: FriendRequestSync
|
||||||
|
) => cb(friendRequests);
|
||||||
|
ipcRenderer.on("on-sync-friend-requests", listener);
|
||||||
|
return () =>
|
||||||
|
ipcRenderer.removeListener("on-sync-friend-requests", listener);
|
||||||
|
},
|
||||||
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
|
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
|
||||||
ipcRenderer.invoke("updateFriendRequest", userId, action),
|
ipcRenderer.invoke("updateFriendRequest", userId, action),
|
||||||
sendFriendRequest: (userId: string) =>
|
sendFriendRequest: (userId: string) =>
|
||||||
@@ -325,6 +347,7 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("getUnlockedAchievements", objectId, shop),
|
ipcRenderer.invoke("getUnlockedAchievements", objectId, shop),
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
|
getAuth: () => ipcRenderer.invoke("getAuth"),
|
||||||
signOut: () => ipcRenderer.invoke("signOut"),
|
signOut: () => ipcRenderer.invoke("signOut"),
|
||||||
openAuthWindow: (page: AuthPage) =>
|
openAuthWindow: (page: AuthPage) =>
|
||||||
ipcRenderer.invoke("openAuthWindow", page),
|
ipcRenderer.invoke("openAuthWindow", page),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
setUserDetails,
|
setUserDetails,
|
||||||
setProfileBackground,
|
setProfileBackground,
|
||||||
setGameRunning,
|
setGameRunning,
|
||||||
|
setFriendRequestCount,
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
||||||
@@ -51,7 +52,6 @@ export function App() {
|
|||||||
isFriendsModalVisible,
|
isFriendsModalVisible,
|
||||||
friendRequetsModalTab,
|
friendRequetsModalTab,
|
||||||
friendModalUserId,
|
friendModalUserId,
|
||||||
syncFriendRequests,
|
|
||||||
hideFriendsModal,
|
hideFriendsModal,
|
||||||
fetchUserDetails,
|
fetchUserDetails,
|
||||||
updateUserDetails,
|
updateUserDetails,
|
||||||
@@ -123,7 +123,7 @@ export function App() {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
updateUserDetails(response);
|
updateUserDetails(response);
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -134,23 +134,27 @@ export function App() {
|
|||||||
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`;
|
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`;
|
||||||
document.head.appendChild($script);
|
document.head.appendChild($script);
|
||||||
});
|
});
|
||||||
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]);
|
}, [fetchUserDetails, updateUserDetails, dispatch]);
|
||||||
|
|
||||||
const onSignIn = useCallback(() => {
|
const onSignIn = useCallback(() => {
|
||||||
fetchUserDetails().then((response) => {
|
fetchUserDetails().then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
updateUserDetails(response);
|
updateUserDetails(response);
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
showSuccessToast(t("successfully_signed_in"));
|
showSuccessToast(t("successfully_signed_in"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [
|
}, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
|
||||||
fetchUserDetails,
|
|
||||||
syncFriendRequests,
|
useEffect(() => {
|
||||||
t,
|
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
|
||||||
showSuccessToast,
|
dispatch(setFriendRequestCount(result.friendRequestCount));
|
||||||
updateUserDetails,
|
});
|
||||||
]);
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
|
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
|
||||||
@@ -263,9 +267,7 @@ export function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = window.electron.onCssInjected((cssString) => {
|
const unsubscribe = window.electron.onCssInjected((cssString) => {
|
||||||
if (cssString) {
|
injectCustomCss(cssString);
|
||||||
injectCustomCss(cssString);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => unsubscribe();
|
return () => unsubscribe();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: globals.$muted-color;
|
color: globals.$muted-color;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { PersonIcon } from "@primer/octicons-react";
|
import { PersonIcon } from "@primer/octicons-react";
|
||||||
|
import cn from "classnames";
|
||||||
|
|
||||||
import "./avatar.scss";
|
import "./avatar.scss";
|
||||||
|
|
||||||
@@ -14,11 +15,18 @@ export interface AvatarProps
|
|||||||
src?: string | null;
|
src?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Avatar({ size, alt, src, ...props }: AvatarProps) {
|
export function Avatar({ size, alt, src, className, ...props }: AvatarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="profile-avatar" style={{ width: size, height: size }}>
|
<div className="profile-avatar" style={{ width: size, height: size }}>
|
||||||
{src ? (
|
{src ? (
|
||||||
<img className="profile-avatar__image" alt={alt} src={src} {...props} />
|
<img
|
||||||
|
className={cn("profile-avatar__image", className)}
|
||||||
|
alt={alt}
|
||||||
|
src={src}
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PersonIcon size={size * 0.7} />
|
<PersonIcon size={size * 0.7} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ export interface BackdropProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Backdrop({ isClosing = false, children }: BackdropProps) {
|
export function Backdrop({
|
||||||
|
isClosing = false,
|
||||||
|
children,
|
||||||
|
}: Readonly<BackdropProps>) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("backdrop", {
|
className={cn("backdrop", {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function Button({
|
|||||||
theme = "primary",
|
theme = "primary",
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: ButtonProps) {
|
}: Readonly<ButtonProps>) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface CheckboxFieldProps
|
|||||||
React.InputHTMLAttributes<HTMLInputElement>,
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
HTMLInputElement
|
HTMLInputElement
|
||||||
> {
|
> {
|
||||||
label: string;
|
label: string | React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CheckboxField({ label, ...props }: CheckboxFieldProps) {
|
export function CheckboxField({ label, ...props }: CheckboxFieldProps) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export interface ConfirmationModalProps extends Omit<ModalProps, "children"> {
|
|||||||
|
|
||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
|
|
||||||
|
buttonsIsDisabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConfirmationModal({
|
export function ConfirmationModal({
|
||||||
@@ -18,6 +20,7 @@ export function ConfirmationModal({
|
|||||||
descriptionText,
|
descriptionText,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
buttonsIsDisabled = false,
|
||||||
...props
|
...props
|
||||||
}: ConfirmationModalProps) {
|
}: ConfirmationModalProps) {
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
@@ -38,7 +41,11 @@ export function ConfirmationModal({
|
|||||||
<Button theme="outline" onClick={handleCancelClick}>
|
<Button theme="outline" onClick={handleCancelClick}>
|
||||||
{cancelButtonLabel}
|
{cancelButtonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
<Button theme="danger" onClick={onConfirm}>
|
<Button
|
||||||
|
theme="danger"
|
||||||
|
disabled={buttonsIsDisabled}
|
||||||
|
onClick={onConfirm}
|
||||||
|
>
|
||||||
{confirmButtonLabel}
|
{confirmButtonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import "./game-card.scss";
|
|||||||
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Badge } from "../badge/badge";
|
import { Badge } from "../badge/badge";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState, useMemo } from "react";
|
||||||
import { useFormat, useRepacks } from "@renderer/hooks";
|
import { useFormat, useRepacks } from "@renderer/hooks";
|
||||||
import { steamUrlBuilder } from "@shared";
|
import { steamUrlBuilder } from "@shared";
|
||||||
|
|
||||||
@@ -45,6 +45,15 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
|
|
||||||
const { numberFormatter } = useFormat();
|
const { numberFormatter } = useFormat();
|
||||||
|
|
||||||
|
const firstThreeRepackers = useMemo(
|
||||||
|
() => uniqueRepackers.slice(0, 3),
|
||||||
|
[uniqueRepackers]
|
||||||
|
);
|
||||||
|
const remainingCount = useMemo(
|
||||||
|
() => uniqueRepackers.length - 3,
|
||||||
|
[uniqueRepackers]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
{...props}
|
{...props}
|
||||||
@@ -68,15 +77,24 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
|
|
||||||
{uniqueRepackers.length > 0 ? (
|
{uniqueRepackers.length > 0 ? (
|
||||||
<ul className="game-card__download-options">
|
<ul className="game-card__download-options">
|
||||||
{uniqueRepackers.map((repacker) => (
|
{firstThreeRepackers.map((repacker) => (
|
||||||
<li key={repacker}>
|
<li key={repacker}>
|
||||||
<Badge>{repacker}</Badge>
|
<Badge>{repacker}</Badge>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
{remainingCount > 0 && (
|
||||||
|
<li>
|
||||||
|
<Badge>
|
||||||
|
+{remainingCount}{" "}
|
||||||
|
{t("game_card:available", { count: remainingCount })}
|
||||||
|
</Badge>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p className="game-card__no-download-label">{t("no_downloads")}</p>
|
<p className="game-card__no-download-label">{t("no_downloads")}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="game-card__specifics">
|
<div className="game-card__specifics">
|
||||||
<div className="game-card__specifics-item">
|
<div className="game-card__specifics-item">
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
@@ -84,11 +102,10 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
{stats ? numberFormatter.format(stats.downloadCount) : "…"}
|
{stats ? numberFormatter.format(stats.downloadCount) : "…"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="game-card__specifics-item">
|
<div className="game-card__specifics-item">
|
||||||
<PeopleIcon />
|
<PeopleIcon />
|
||||||
<span>
|
<span>
|
||||||
{stats ? numberFormatter.format(stats?.playerCount) : "…"}
|
{stats ? numberFormatter.format(stats.playerCount) : "…"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { PeopleIcon } from "@primer/octicons-react";
|
import { PeopleIcon } from "@primer/octicons-react";
|
||||||
import { useAppSelector, useUserDetails } from "@renderer/hooks";
|
import { useAppSelector, useUserDetails } from "@renderer/hooks";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||||
@@ -9,19 +9,13 @@ import { Avatar } from "../avatar/avatar";
|
|||||||
import { AuthPage } from "@shared";
|
import { AuthPage } from "@shared";
|
||||||
import "./sidebar-profile.scss";
|
import "./sidebar-profile.scss";
|
||||||
|
|
||||||
const LONG_POLLING_INTERVAL = 120_000;
|
|
||||||
|
|
||||||
export function SidebarProfile() {
|
export function SidebarProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { t } = useTranslation("sidebar");
|
const { t } = useTranslation("sidebar");
|
||||||
|
|
||||||
const {
|
const { userDetails, friendRequestCount, showFriendsModal } =
|
||||||
userDetails,
|
useUserDetails();
|
||||||
friendRequestCount,
|
|
||||||
showFriendsModal,
|
|
||||||
syncFriendRequests,
|
|
||||||
} = useUserDetails();
|
|
||||||
|
|
||||||
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
||||||
|
|
||||||
@@ -34,16 +28,6 @@ export function SidebarProfile() {
|
|||||||
navigate(`/profile/${userDetails.id}`);
|
navigate(`/profile/${userDetails.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const pollingInterval = setInterval(() => {
|
|
||||||
syncFriendRequests();
|
|
||||||
}, LONG_POLLING_INTERVAL);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(pollingInterval);
|
|
||||||
};
|
|
||||||
}, [syncFriendRequests]);
|
|
||||||
|
|
||||||
const friendsButton = useMemo(() => {
|
const friendsButton = useMemo(() => {
|
||||||
if (!userDetails) return null;
|
if (!userDetails) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { darkenColor } from "@renderer/helpers";
|
import { darkenColor } from "@renderer/helpers";
|
||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
import type { UserProfile, UserStats } from "@types";
|
import type { Badge, UserProfile, UserStats } from "@types";
|
||||||
import { average } from "color.js";
|
import { average } from "color.js";
|
||||||
|
|
||||||
import { createContext, useCallback, useEffect, useState } from "react";
|
import { createContext, useCallback, useEffect, useState } from "react";
|
||||||
@@ -16,6 +16,7 @@ export interface UserProfileContext {
|
|||||||
getUserProfile: () => Promise<void>;
|
getUserProfile: () => Promise<void>;
|
||||||
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
|
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
|
||||||
backgroundImage: string;
|
backgroundImage: string;
|
||||||
|
badges: Badge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
|
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
|
||||||
@@ -28,6 +29,7 @@ export const userProfileContext = createContext<UserProfileContext>({
|
|||||||
getUserProfile: async () => {},
|
getUserProfile: async () => {},
|
||||||
setSelectedBackgroundImage: () => {},
|
setSelectedBackgroundImage: () => {},
|
||||||
backgroundImage: "",
|
backgroundImage: "",
|
||||||
|
badges: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { Provider } = userProfileContext;
|
const { Provider } = userProfileContext;
|
||||||
@@ -41,12 +43,13 @@ export interface UserProfileContextProviderProps {
|
|||||||
export function UserProfileContextProvider({
|
export function UserProfileContextProvider({
|
||||||
children,
|
children,
|
||||||
userId,
|
userId,
|
||||||
}: UserProfileContextProviderProps) {
|
}: Readonly<UserProfileContextProviderProps>) {
|
||||||
const { userDetails } = useAppSelector((state) => state.userDetails);
|
const { userDetails } = useAppSelector((state) => state.userDetails);
|
||||||
|
|
||||||
const [userStats, setUserStats] = useState<UserStats | null>(null);
|
const [userStats, setUserStats] = useState<UserStats | null>(null);
|
||||||
|
|
||||||
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
|
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
|
||||||
|
const [badges, setBadges] = useState<Badge[]>([]);
|
||||||
const [heroBackground, setHeroBackground] = useState(
|
const [heroBackground, setHeroBackground] = useState(
|
||||||
DEFAULT_USER_PROFILE_BACKGROUND
|
DEFAULT_USER_PROFILE_BACKGROUND
|
||||||
);
|
);
|
||||||
@@ -101,12 +104,18 @@ export function UserProfileContextProvider({
|
|||||||
});
|
});
|
||||||
}, [navigate, getUserStats, showErrorToast, userId, t]);
|
}, [navigate, getUserStats, showErrorToast, userId, t]);
|
||||||
|
|
||||||
|
const getBadges = useCallback(async () => {
|
||||||
|
const badges = await window.electron.getBadges();
|
||||||
|
setBadges(badges);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUserProfile(null);
|
setUserProfile(null);
|
||||||
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
|
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
|
||||||
|
|
||||||
getUserProfile();
|
getUserProfile();
|
||||||
}, [getUserProfile]);
|
getBadges();
|
||||||
|
}, [getUserProfile, getBadges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider
|
<Provider
|
||||||
@@ -118,6 +127,7 @@ export function UserProfileContextProvider({
|
|||||||
setSelectedBackgroundImage,
|
setSelectedBackgroundImage,
|
||||||
backgroundImage: getBackgroundImageUrl(),
|
backgroundImage: getBackgroundImageUrl(),
|
||||||
userStats,
|
userStats,
|
||||||
|
badges,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
14
src/renderer/src/declaration.d.ts
vendored
14
src/renderer/src/declaration.d.ts
vendored
@@ -30,6 +30,8 @@ import type {
|
|||||||
GameRunning,
|
GameRunning,
|
||||||
TorBoxUser,
|
TorBoxUser,
|
||||||
Theme,
|
Theme,
|
||||||
|
Badge,
|
||||||
|
Auth,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type disk from "diskusage";
|
import type disk from "diskusage";
|
||||||
@@ -86,6 +88,11 @@ declare global {
|
|||||||
getDevelopers: () => Promise<string[]>;
|
getDevelopers: () => Promise<string[]>;
|
||||||
|
|
||||||
/* Library */
|
/* Library */
|
||||||
|
toggleAutomaticCloudSync: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) => Promise<void>;
|
||||||
addGameToLibrary: (
|
addGameToLibrary: (
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
objectId: string,
|
objectId: string,
|
||||||
@@ -217,6 +224,7 @@ declare global {
|
|||||||
) => Promise<Electron.OpenDialogReturnValue>;
|
) => Promise<Electron.OpenDialogReturnValue>;
|
||||||
showItemInFolder: (path: string) => Promise<void>;
|
showItemInFolder: (path: string) => Promise<void>;
|
||||||
getFeatures: () => Promise<string[]>;
|
getFeatures: () => Promise<string[]>;
|
||||||
|
getBadges: () => Promise<Badge[]>;
|
||||||
platform: NodeJS.Platform;
|
platform: NodeJS.Platform;
|
||||||
|
|
||||||
/* Auto update */
|
/* Auto update */
|
||||||
@@ -227,6 +235,7 @@ declare global {
|
|||||||
restartAndInstallUpdate: () => Promise<void>;
|
restartAndInstallUpdate: () => Promise<void>;
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
|
getAuth: () => Promise<Auth | null>;
|
||||||
signOut: () => Promise<void>;
|
signOut: () => Promise<void>;
|
||||||
openAuthWindow: (page: AuthPage) => Promise<void>;
|
openAuthWindow: (page: AuthPage) => Promise<void>;
|
||||||
getSessionHash: () => Promise<string | null>;
|
getSessionHash: () => Promise<string | null>;
|
||||||
@@ -271,7 +280,10 @@ declare global {
|
|||||||
path: string
|
path: string
|
||||||
) => Promise<{ imagePath: string; mimeType: string }>;
|
) => Promise<{ imagePath: string; mimeType: string }>;
|
||||||
getFriendRequests: () => Promise<FriendRequest[]>;
|
getFriendRequests: () => Promise<FriendRequest[]>;
|
||||||
syncFriendRequests: () => Promise<FriendRequestSync>;
|
syncFriendRequests: () => Promise<void>;
|
||||||
|
onSyncFriendRequests: (
|
||||||
|
cb: (friendRequests: FriendRequestSync) => void
|
||||||
|
) => () => Electron.IpcRenderer;
|
||||||
updateFriendRequest: (
|
updateFriendRequest: (
|
||||||
userId: string,
|
userId: string,
|
||||||
action: FriendRequestAction
|
action: FriendRequestAction
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
setFriendRequests,
|
setFriendRequests,
|
||||||
setFriendsModalVisible,
|
setFriendsModalVisible,
|
||||||
setFriendsModalHidden,
|
setFriendsModalHidden,
|
||||||
setFriendRequestCount,
|
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
import type {
|
import type {
|
||||||
FriendRequestAction,
|
FriendRequestAction,
|
||||||
@@ -15,7 +14,6 @@ import type {
|
|||||||
} from "@types";
|
} from "@types";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||||
import { isFuture, isToday } from "date-fns";
|
|
||||||
|
|
||||||
export function useUserDetails() {
|
export function useUserDetails() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -89,24 +87,15 @@ export function useUserDetails() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncFriendRequests = useCallback(async () => {
|
|
||||||
return window.electron
|
|
||||||
.syncFriendRequests()
|
|
||||||
.then((sync) => {
|
|
||||||
dispatch(setFriendRequestCount(sync.friendRequestCount));
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const fetchFriendRequests = useCallback(async () => {
|
const fetchFriendRequests = useCallback(async () => {
|
||||||
return window.electron
|
return window.electron
|
||||||
.getFriendRequests()
|
.getFriendRequests()
|
||||||
.then((friendRequests) => {
|
.then((friendRequests) => {
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
dispatch(setFriendRequests(friendRequests));
|
dispatch(setFriendRequests(friendRequests));
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, [dispatch, syncFriendRequests]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const showFriendsModal = useCallback(
|
const showFriendsModal = useCallback(
|
||||||
(initialTab: UserFriendModalTab, userId: string) => {
|
(initialTab: UserFriendModalTab, userId: string) => {
|
||||||
@@ -146,8 +135,8 @@ export function useUserDetails() {
|
|||||||
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
||||||
|
|
||||||
const hasActiveSubscription = useMemo(() => {
|
const hasActiveSubscription = useMemo(() => {
|
||||||
const expiresAt = userDetails?.subscription?.expiresAt;
|
const expiresAt = new Date(userDetails?.subscription?.expiresAt ?? 0);
|
||||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
return expiresAt > new Date();
|
||||||
}, [userDetails]);
|
}, [userDetails]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -168,7 +157,6 @@ export function useUserDetails() {
|
|||||||
patchUser,
|
patchUser,
|
||||||
sendFriendRequest,
|
sendFriendRequest,
|
||||||
fetchFriendRequests,
|
fetchFriendRequests,
|
||||||
syncFriendRequests,
|
|
||||||
updateFriendRequestState,
|
updateFriendRequestState,
|
||||||
blockUser,
|
blockUser,
|
||||||
unblockUser,
|
unblockUser,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "@fontsource/noto-sans/500.css";
|
|||||||
import "@fontsource/noto-sans/700.css";
|
import "@fontsource/noto-sans/700.css";
|
||||||
|
|
||||||
import "react-loading-skeleton/dist/skeleton.css";
|
import "react-loading-skeleton/dist/skeleton.css";
|
||||||
|
import "react-tooltip/dist/react-tooltip.css";
|
||||||
|
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
|
|
||||||
@@ -18,26 +19,17 @@ import { store } from "./store";
|
|||||||
|
|
||||||
import resources from "@locales";
|
import resources from "@locales";
|
||||||
|
|
||||||
import { SuspenseWrapper } from "./components";
|
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { addCookieInterceptor } from "./cookies";
|
import { addCookieInterceptor } from "./cookies";
|
||||||
|
|
||||||
const Home = React.lazy(() => import("./pages/home/home"));
|
|
||||||
const GameDetails = React.lazy(
|
|
||||||
() => import("./pages/game-details/game-details")
|
|
||||||
);
|
|
||||||
const Downloads = React.lazy(() => import("./pages/downloads/downloads"));
|
|
||||||
const Settings = React.lazy(() => import("./pages/settings/settings"));
|
|
||||||
const Catalogue = React.lazy(() => import("./pages/catalogue/catalogue"));
|
|
||||||
const Profile = React.lazy(() => import("./pages/profile/profile"));
|
|
||||||
const Achievements = React.lazy(
|
|
||||||
() => import("./pages/achievements/achievements")
|
|
||||||
);
|
|
||||||
const ThemeEditor = React.lazy(
|
|
||||||
() => import("./pages/theme-editor/theme-editor")
|
|
||||||
);
|
|
||||||
|
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
|
import Catalogue from "./pages/catalogue/catalogue";
|
||||||
|
import Home from "./pages/home/home";
|
||||||
|
import Downloads from "./pages/downloads/downloads";
|
||||||
|
import GameDetails from "./pages/game-details/game-details";
|
||||||
|
import Settings from "./pages/settings/settings";
|
||||||
|
import Profile from "./pages/profile/profile";
|
||||||
|
import Achievements from "./pages/achievements/achievements";
|
||||||
|
import ThemeEditor from "./pages/theme-editor/theme-editor";
|
||||||
|
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: import.meta.env.RENDERER_VITE_SENTRY_DSN,
|
dsn: import.meta.env.RENDERER_VITE_SENTRY_DSN,
|
||||||
@@ -82,37 +74,16 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
|||||||
<HashRouter>
|
<HashRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<App />}>
|
<Route element={<App />}>
|
||||||
<Route path="/" element={<SuspenseWrapper Component={Home} />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route
|
<Route path="/catalogue" element={<Catalogue />} />
|
||||||
path="/catalogue"
|
<Route path="/downloads" element={<Downloads />} />
|
||||||
element={<SuspenseWrapper Component={Catalogue} />}
|
<Route path="/game/:shop/:objectId" element={<GameDetails />} />
|
||||||
/>
|
<Route path="/settings" element={<Settings />} />
|
||||||
<Route
|
<Route path="/profile/:userId" element={<Profile />} />
|
||||||
path="/downloads"
|
<Route path="/achievements" element={<Achievements />} />
|
||||||
element={<SuspenseWrapper Component={Downloads} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/game/:shop/:objectId"
|
|
||||||
element={<SuspenseWrapper Component={GameDetails} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/settings"
|
|
||||||
element={<SuspenseWrapper Component={Settings} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/profile/:userId"
|
|
||||||
element={<SuspenseWrapper Component={Profile} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/achievements"
|
|
||||||
element={<SuspenseWrapper Component={Achievements} />}
|
|
||||||
/>
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route
|
<Route path="/theme-editor" element={<ThemeEditor />} />
|
||||||
path="/theme-editor"
|
|
||||||
element={<SuspenseWrapper Component={ThemeEditor} />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ $logo-max-width: 200px;
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: globals.$spacing-unit / 2;
|
gap: calc(globals.$spacing-unit / 2);
|
||||||
color: globals.$body-color;
|
color: globals.$body-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -203,9 +203,10 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
|
|||||||
<div className="cloud-sync-modal__artifact-info">
|
<div className="cloud-sync-modal__artifact-info">
|
||||||
<div className="cloud-sync-modal__artifact-header">
|
<div className="cloud-sync-modal__artifact-header">
|
||||||
<h3>
|
<h3>
|
||||||
{t("backup_from", {
|
{artifact.label ??
|
||||||
date: format(artifact.createdAt, "dd/MM/yyyy"),
|
t("backup_from", {
|
||||||
})}
|
date: format(artifact.createdAt, "dd/MM/yyyy"),
|
||||||
|
})}
|
||||||
</h3>
|
</h3>
|
||||||
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,13 +16,8 @@ import { useUserDetails } from "@renderer/hooks";
|
|||||||
import { useSubscription } from "@renderer/hooks/use-subscription";
|
import { useSubscription } from "@renderer/hooks/use-subscription";
|
||||||
import "./game-details.scss";
|
import "./game-details.scss";
|
||||||
|
|
||||||
const HERO_HEIGHT = 300;
|
|
||||||
const HERO_ANIMATION_THRESHOLD = 25;
|
|
||||||
|
|
||||||
export function GameDetailsContent() {
|
export function GameDetailsContent() {
|
||||||
const heroRef = useRef<HTMLDivElement | null>(null);
|
const heroRef = useRef<HTMLDivElement | null>(null);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
const [isHeaderStuck, setIsHeaderStuck] = useState(false);
|
|
||||||
|
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
@@ -61,7 +56,7 @@ export function GameDetailsContent() {
|
|||||||
return t("no_shop_details");
|
return t("no_shop_details");
|
||||||
}, [shopDetails, t]);
|
}, [shopDetails, t]);
|
||||||
|
|
||||||
const [backdropOpactiy, setBackdropOpacity] = useState(1);
|
const [backdropOpacity, setBackdropOpacity] = useState(1);
|
||||||
|
|
||||||
const handleHeroLoad = async () => {
|
const handleHeroLoad = async () => {
|
||||||
const output = await average(steamUrlBuilder.libraryHero(objectId!), {
|
const output = await average(steamUrlBuilder.libraryHero(objectId!), {
|
||||||
@@ -80,26 +75,6 @@ export function GameDetailsContent() {
|
|||||||
setBackdropOpacity(1);
|
setBackdropOpacity(1);
|
||||||
}, [objectId]);
|
}, [objectId]);
|
||||||
|
|
||||||
const onScroll: React.UIEventHandler<HTMLElement> = (event) => {
|
|
||||||
const heroHeight = heroRef.current?.clientHeight ?? HERO_HEIGHT;
|
|
||||||
|
|
||||||
const scrollY = (event.target as HTMLDivElement).scrollTop;
|
|
||||||
const opacity = Math.max(
|
|
||||||
0,
|
|
||||||
1 - scrollY / (heroHeight - HERO_ANIMATION_THRESHOLD)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (scrollY >= heroHeight && !isHeaderStuck) {
|
|
||||||
setIsHeaderStuck(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollY <= heroHeight && isHeaderStuck) {
|
|
||||||
setIsHeaderStuck(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setBackdropOpacity(opacity);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloudSaveButtonClick = () => {
|
const handleCloudSaveButtonClick = () => {
|
||||||
if (!userDetails) {
|
if (!userDetails) {
|
||||||
window.electron.openAuthWindow(AuthPage.SignIn);
|
window.electron.openAuthWindow(AuthPage.SignIn);
|
||||||
@@ -122,31 +97,25 @@ export function GameDetailsContent() {
|
|||||||
<div
|
<div
|
||||||
className={`game-details__wrapper ${hasNSFWContentBlocked ? "game-details__wrapper--blurred" : ""}`}
|
className={`game-details__wrapper ${hasNSFWContentBlocked ? "game-details__wrapper--blurred" : ""}`}
|
||||||
>
|
>
|
||||||
<img
|
<section className="game-details__container">
|
||||||
src={steamUrlBuilder.libraryHero(objectId!)}
|
|
||||||
className="game-details__hero-image"
|
|
||||||
alt={game?.title}
|
|
||||||
onLoad={handleHeroLoad}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<section
|
|
||||||
ref={containerRef}
|
|
||||||
onScroll={onScroll}
|
|
||||||
className="game-details__container"
|
|
||||||
>
|
|
||||||
<div ref={heroRef} className="game-details__hero">
|
<div ref={heroRef} className="game-details__hero">
|
||||||
|
<img
|
||||||
|
src={steamUrlBuilder.libraryHero(objectId!)}
|
||||||
|
className="game-details__hero-image"
|
||||||
|
alt={game?.title}
|
||||||
|
onLoad={handleHeroLoad}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className="game-details__hero-backdrop"
|
className="game-details__hero-backdrop"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: gameColor,
|
backgroundColor: gameColor,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
opacity: Math.min(1, 1 - backdropOpactiy),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="game-details__hero-logo-backdrop"
|
className="game-details__hero-logo-backdrop"
|
||||||
style={{ opacity: backdropOpactiy }}
|
style={{ opacity: backdropOpacity }}
|
||||||
>
|
>
|
||||||
<div className="game-details__hero-content">
|
<div className="game-details__hero-content">
|
||||||
<img
|
<img
|
||||||
@@ -173,7 +142,7 @@ export function GameDetailsContent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HeroPanel isHeaderStuck={isHeaderStuck} />
|
<HeroPanel />
|
||||||
|
|
||||||
<div className="game-details__description-container">
|
<div className="game-details__description-container">
|
||||||
<div className="game-details__description-content">
|
<div className="game-details__description-content">
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ import { HeroPanelPlaytime } from "./hero-panel-playtime";
|
|||||||
import { gameDetailsContext } from "@renderer/context";
|
import { gameDetailsContext } from "@renderer/context";
|
||||||
import "./hero-panel.scss";
|
import "./hero-panel.scss";
|
||||||
|
|
||||||
export interface HeroPanelProps {
|
export function HeroPanel() {
|
||||||
isHeaderStuck: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HeroPanel({ isHeaderStuck }: HeroPanelProps) {
|
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const { formatDate } = useDate();
|
const { formatDate } = useDate();
|
||||||
@@ -54,10 +50,7 @@ export function HeroPanel({ isHeaderStuck }: HeroPanelProps) {
|
|||||||
game?.download?.status === "paused";
|
game?.download?.status === "paused";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div style={{ backgroundColor: gameColor }} className="hero-panel">
|
||||||
style={{ backgroundColor: gameColor }}
|
|
||||||
className={`hero-panel ${isHeaderStuck ? "hero-panel--stuck" : ""}`}
|
|
||||||
>
|
|
||||||
<div className="hero-panel__content">{getInfo()}</div>
|
<div className="hero-panel__content">{getInfo()}</div>
|
||||||
<div className="hero-panel__actions">
|
<div className="hero-panel__actions">
|
||||||
<HeroPanelActions />
|
<HeroPanelActions />
|
||||||
|
|||||||
@@ -23,6 +23,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__cloud-sync-label {
|
||||||
|
display: flex;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cloud-sync-hydra-cloud {
|
||||||
|
background: linear-gradient(270deg, #16b195 50%, #3e62c0 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 0 globals.$spacing-unit;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: globals.$small-font-size;
|
||||||
|
}
|
||||||
|
|
||||||
&__row {
|
&__row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button, Modal, TextField } from "@renderer/components";
|
import { Button, CheckboxField, Modal, TextField } from "@renderer/components";
|
||||||
import type { LibraryGame } from "@types";
|
import type { LibraryGame } from "@types";
|
||||||
import { gameDetailsContext } from "@renderer/context";
|
import { gameDetailsContext } from "@renderer/context";
|
||||||
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
||||||
@@ -34,12 +34,17 @@ export function GameOptionsModal({
|
|||||||
achievements,
|
achievements,
|
||||||
} = useContext(gameDetailsContext);
|
} = useContext(gameDetailsContext);
|
||||||
|
|
||||||
|
const { hasActiveSubscription } = useUserDetails();
|
||||||
|
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
||||||
const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
|
const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
|
||||||
const [showResetAchievementsModal, setShowResetAchievementsModal] =
|
const [showResetAchievementsModal, setShowResetAchievementsModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isDeletingAchievements, setIsDeletingAchievements] = useState(false);
|
const [isDeletingAchievements, setIsDeletingAchievements] = useState(false);
|
||||||
|
const [automaticCloudSync, setAutomaticCloudSync] = useState(
|
||||||
|
game.automaticCloudSync ?? false
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
removeGameInstaller,
|
removeGameInstaller,
|
||||||
@@ -183,6 +188,20 @@ export function GameOptionsModal({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleAutomaticCloudSync = async (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
setAutomaticCloudSync(event.target.checked);
|
||||||
|
|
||||||
|
await window.electron.toggleAutomaticCloudSync(
|
||||||
|
game.shop,
|
||||||
|
game.objectId,
|
||||||
|
event.target.checked
|
||||||
|
);
|
||||||
|
|
||||||
|
updateGame();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteGameModal
|
<DeleteGameModal
|
||||||
@@ -266,6 +285,20 @@ export function GameOptionsModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={
|
||||||
|
<div className="game-options-modal__cloud-sync-label">
|
||||||
|
{t("enable_automatic_cloud_sync")}
|
||||||
|
<span className="game-options-modal__cloud-sync-hydra-cloud">
|
||||||
|
Hydra Cloud
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
checked={automaticCloudSync}
|
||||||
|
disabled={!hasActiveSubscription || !game.executablePath}
|
||||||
|
onChange={handleToggleAutomaticCloudSync}
|
||||||
|
/>
|
||||||
|
|
||||||
{shouldShowWinePrefixConfiguration && (
|
{shouldShowWinePrefixConfiguration && (
|
||||||
<div className="game-options-modal__wine-prefix">
|
<div className="game-options-modal__wine-prefix">
|
||||||
<div className="game-options-modal__header">
|
<div className="game-options-modal__header">
|
||||||
|
|||||||
@@ -27,6 +27,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__badges {
|
||||||
|
display: flex;
|
||||||
|
gap: calc(globals.$spacing-unit / 2);
|
||||||
|
}
|
||||||
|
|
||||||
&__user-information {
|
&__user-information {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: calc(globals.$spacing-unit * 7) calc(globals.$spacing-unit * 3);
|
padding: calc(globals.$spacing-unit * 7) calc(globals.$spacing-unit * 3);
|
||||||
@@ -65,6 +70,12 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__display-name-container {
|
||||||
|
display: flex;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
&__display-name {
|
&__display-name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import type { FriendRequestAction } from "@types";
|
|||||||
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
|
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
|
||||||
import Skeleton from "react-loading-skeleton";
|
import Skeleton from "react-loading-skeleton";
|
||||||
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
|
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
import "./profile-hero.scss";
|
import "./profile-hero.scss";
|
||||||
|
|
||||||
type FriendAction =
|
type FriendAction =
|
||||||
@@ -34,8 +35,14 @@ export function ProfileHero() {
|
|||||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
||||||
const [isPerformingAction, setIsPerformingAction] = useState(false);
|
const [isPerformingAction, setIsPerformingAction] = useState(false);
|
||||||
|
|
||||||
const { isMe, getUserProfile, userProfile, heroBackground, backgroundImage } =
|
const {
|
||||||
useContext(userProfileContext);
|
isMe,
|
||||||
|
badges,
|
||||||
|
getUserProfile,
|
||||||
|
userProfile,
|
||||||
|
heroBackground,
|
||||||
|
backgroundImage,
|
||||||
|
} = useContext(userProfileContext);
|
||||||
const {
|
const {
|
||||||
signOut,
|
signOut,
|
||||||
updateFriendRequestState,
|
updateFriendRequestState,
|
||||||
@@ -260,14 +267,6 @@ export function ProfileHero() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <ConfirmationModal
|
|
||||||
visible
|
|
||||||
title={t("sign_out_modal_title")}
|
|
||||||
descriptionText={t("sign_out_modal_text")}
|
|
||||||
confirmButtonLabel={t("sign_out")}
|
|
||||||
cancelButtonLabel={t("cancel")}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<EditProfileModal
|
<EditProfileModal
|
||||||
visible={showEditProfileModal}
|
visible={showEditProfileModal}
|
||||||
onClose={() => setShowEditProfileModal(false)}
|
onClose={() => setShowEditProfileModal(false)}
|
||||||
@@ -307,9 +306,34 @@ export function ProfileHero() {
|
|||||||
|
|
||||||
<div className="profile-hero__information">
|
<div className="profile-hero__information">
|
||||||
{userProfile ? (
|
{userProfile ? (
|
||||||
<h2 className="profile-hero__display-name">
|
<div className="profile-hero__display-name-container">
|
||||||
{userProfile?.displayName}
|
<h2 className="profile-hero__display-name">
|
||||||
</h2>
|
{userProfile?.displayName}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="profile-hero__badges">
|
||||||
|
{userProfile.badges.map((badgeName) => {
|
||||||
|
const badge = badges.find((b) => b.name === badgeName);
|
||||||
|
|
||||||
|
if (!badge) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
key={badge.name}
|
||||||
|
src={badge.badge.url}
|
||||||
|
alt={badge.name}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
data-tooltip-place="top"
|
||||||
|
data-tooltip-content={badge.description}
|
||||||
|
data-tooltip-id="badge-name"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<Tooltip id="badge-name" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Skeleton width={150} height={28} />
|
<Skeleton width={150} height={28} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ThemeActions, ThemeCard, ThemePlaceholder } from "./index";
|
|||||||
import type { Theme } from "@types";
|
import type { Theme } from "@types";
|
||||||
import { ImportThemeModal } from "./modals/import-theme-modal";
|
import { ImportThemeModal } from "./modals/import-theme-modal";
|
||||||
import { settingsContext } from "@renderer/context";
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
interface SettingsAppearanceProps {
|
interface SettingsAppearanceProps {
|
||||||
appearance: {
|
appearance: {
|
||||||
@@ -24,8 +25,10 @@ export function SettingsAppearance({
|
|||||||
authorId: string;
|
authorId: string;
|
||||||
authorName: string;
|
authorName: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const [hasShownModal, setHasShownModal] = useState(false);
|
||||||
|
|
||||||
const { clearTheme } = useContext(settingsContext);
|
const { clearTheme } = useContext(settingsContext);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const loadThemes = useCallback(async () => {
|
const loadThemes = useCallback(async () => {
|
||||||
const themesList = await window.electron.getAllCustomThemes();
|
const themesList = await window.electron.getAllCustomThemes();
|
||||||
@@ -45,20 +48,37 @@ export function SettingsAppearance({
|
|||||||
}, [loadThemes]);
|
}, [loadThemes]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (appearance.theme && appearance.authorId && appearance.authorName) {
|
if (
|
||||||
|
appearance.theme &&
|
||||||
|
appearance.authorId &&
|
||||||
|
appearance.authorName &&
|
||||||
|
!hasShownModal
|
||||||
|
) {
|
||||||
setIsImportThemeModalVisible(true);
|
setIsImportThemeModalVisible(true);
|
||||||
setImportTheme({
|
setImportTheme({
|
||||||
theme: appearance.theme,
|
theme: appearance.theme,
|
||||||
authorId: appearance.authorId,
|
authorId: appearance.authorId,
|
||||||
authorName: appearance.authorName,
|
authorName: appearance.authorName,
|
||||||
});
|
});
|
||||||
|
setHasShownModal(true);
|
||||||
|
|
||||||
|
navigate("/settings", { replace: true });
|
||||||
|
clearTheme();
|
||||||
}
|
}
|
||||||
}, [appearance.theme, appearance.authorId, appearance.authorName]);
|
}, [
|
||||||
|
appearance.theme,
|
||||||
|
appearance.authorId,
|
||||||
|
appearance.authorName,
|
||||||
|
navigate,
|
||||||
|
hasShownModal,
|
||||||
|
clearTheme,
|
||||||
|
]);
|
||||||
|
|
||||||
const onThemeImported = useCallback(() => {
|
const onThemeImported = useCallback(() => {
|
||||||
setIsImportThemeModalVisible(false);
|
setIsImportThemeModalVisible(false);
|
||||||
|
setImportTheme(null);
|
||||||
loadThemes();
|
loadThemes();
|
||||||
}, [clearTheme, loadThemes]);
|
}, [loadThemes]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-appearance">
|
<div className="settings-appearance">
|
||||||
@@ -90,6 +110,7 @@ export function SettingsAppearance({
|
|||||||
onClose={() => {
|
onClose={() => {
|
||||||
setIsImportThemeModalVisible(false);
|
setIsImportThemeModalVisible(false);
|
||||||
clearTheme();
|
clearTheme();
|
||||||
|
setHasShownModal(false);
|
||||||
}}
|
}}
|
||||||
onThemeImported={onThemeImported}
|
onThemeImported={onThemeImported}
|
||||||
themeName={importTheme.theme}
|
themeName={importTheme.theme}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function SettingsBehavior() {
|
|||||||
runAtStartup: false,
|
runAtStartup: false,
|
||||||
startMinimized: false,
|
startMinimized: false,
|
||||||
disableNsfwAlert: false,
|
disableNsfwAlert: false,
|
||||||
|
enableAutoInstall: false,
|
||||||
seedAfterDownloadComplete: false,
|
seedAfterDownloadComplete: false,
|
||||||
showHiddenAchievementsDescription: false,
|
showHiddenAchievementsDescription: false,
|
||||||
});
|
});
|
||||||
@@ -34,6 +35,7 @@ export function SettingsBehavior() {
|
|||||||
runAtStartup: userPreferences.runAtStartup ?? false,
|
runAtStartup: userPreferences.runAtStartup ?? false,
|
||||||
startMinimized: userPreferences.startMinimized ?? false,
|
startMinimized: userPreferences.startMinimized ?? false,
|
||||||
disableNsfwAlert: userPreferences.disableNsfwAlert ?? false,
|
disableNsfwAlert: userPreferences.disableNsfwAlert ?? false,
|
||||||
|
enableAutoInstall: userPreferences.enableAutoInstall ?? false,
|
||||||
seedAfterDownloadComplete:
|
seedAfterDownloadComplete:
|
||||||
userPreferences.seedAfterDownloadComplete ?? false,
|
userPreferences.seedAfterDownloadComplete ?? false,
|
||||||
showHiddenAchievementsDescription:
|
showHiddenAchievementsDescription:
|
||||||
@@ -99,6 +101,16 @@ export function SettingsBehavior() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{window.electron.platform === "linux" && (
|
||||||
|
<CheckboxField
|
||||||
|
label={t("enable_auto_install")}
|
||||||
|
checked={form.enableAutoInstall}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({ enableAutoInstall: !form.enableAutoInstall })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("disable_nsfw_alert")}
|
label={t("disable_nsfw_alert")}
|
||||||
checked={form.disableNsfwAlert}
|
checked={form.disableNsfwAlert}
|
||||||
|
|||||||
@@ -53,4 +53,9 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__buttons-container {
|
||||||
|
display: flex;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { TextField, Button, Badge } from "@renderer/components";
|
import {
|
||||||
|
TextField,
|
||||||
|
Button,
|
||||||
|
Badge,
|
||||||
|
ConfirmationModal,
|
||||||
|
} from "@renderer/components";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { DownloadSource } from "@types";
|
import type { DownloadSource } from "@types";
|
||||||
import { NoEntryIcon, PlusCircleIcon, SyncIcon } from "@primer/octicons-react";
|
import {
|
||||||
|
NoEntryIcon,
|
||||||
|
PlusCircleIcon,
|
||||||
|
SyncIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from "@primer/octicons-react";
|
||||||
import { AddDownloadSourceModal } from "./add-download-source-modal";
|
import { AddDownloadSourceModal } from "./add-download-source-modal";
|
||||||
import { useAppDispatch, useRepacks, useToast } from "@renderer/hooks";
|
import { useAppDispatch, useRepacks, useToast } from "@renderer/hooks";
|
||||||
import { DownloadSourceStatus } from "@shared";
|
import { DownloadSourceStatus } from "@shared";
|
||||||
@@ -16,6 +26,10 @@ import { setFilters, clearFilters } from "@renderer/features";
|
|||||||
import "./settings-download-sources.scss";
|
import "./settings-download-sources.scss";
|
||||||
|
|
||||||
export function SettingsDownloadSources() {
|
export function SettingsDownloadSources() {
|
||||||
|
const [
|
||||||
|
showConfirmationDeleteAllSourcesModal,
|
||||||
|
setShowConfirmationDeleteAllSourcesModal,
|
||||||
|
] = useState(false);
|
||||||
const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] =
|
const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
||||||
@@ -23,6 +37,7 @@ export function SettingsDownloadSources() {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [isRemovingDownloadSource, setIsRemovingDownloadSource] =
|
const [isRemovingDownloadSource, setIsRemovingDownloadSource] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const [isFetchingSources, setIsFetchingSources] = useState(true);
|
||||||
|
|
||||||
const { sourceUrl, clearSourceUrl } = useContext(settingsContext);
|
const { sourceUrl, clearSourceUrl } = useContext(settingsContext);
|
||||||
|
|
||||||
@@ -41,6 +56,9 @@ export function SettingsDownloadSources() {
|
|||||||
.sortBy("createdAt")
|
.sortBy("createdAt")
|
||||||
.then((sources) => {
|
.then((sources) => {
|
||||||
setDownloadSources(sources.reverse());
|
setDownloadSources(sources.reverse());
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsFetchingSources(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +86,25 @@ export function SettingsDownloadSources() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemoveAllDownloadSources = () => {
|
||||||
|
setIsRemovingDownloadSource(true);
|
||||||
|
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
const channel = new BroadcastChannel(`download_sources:delete_all:${id}`);
|
||||||
|
|
||||||
|
downloadSourcesWorker.postMessage(["DELETE_ALL_DOWNLOAD_SOURCES", id]);
|
||||||
|
|
||||||
|
channel.onmessage = () => {
|
||||||
|
showSuccessToast(t("removed_download_sources"));
|
||||||
|
|
||||||
|
getDownloadSources();
|
||||||
|
setIsRemovingDownloadSource(false);
|
||||||
|
setShowConfirmationDeleteAllSourcesModal(false);
|
||||||
|
channel.close();
|
||||||
|
updateRepacks();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddDownloadSource = async () => {
|
const handleAddDownloadSource = async () => {
|
||||||
await getDownloadSources();
|
await getDownloadSources();
|
||||||
showSuccessToast(t("added_download_source"));
|
showSuccessToast(t("added_download_source"));
|
||||||
@@ -115,6 +152,17 @@ export function SettingsDownloadSources() {
|
|||||||
onClose={handleModalClose}
|
onClose={handleModalClose}
|
||||||
onAddDownloadSource={handleAddDownloadSource}
|
onAddDownloadSource={handleAddDownloadSource}
|
||||||
/>
|
/>
|
||||||
|
<ConfirmationModal
|
||||||
|
cancelButtonLabel={t("cancel_button_confirmation_delete_all_sources")}
|
||||||
|
confirmButtonLabel={t("confirm_button_confirmation_delete_all_sources")}
|
||||||
|
descriptionText={t("description_confirmation_delete_all_sources")}
|
||||||
|
clickOutsideToClose={false}
|
||||||
|
onConfirm={handleRemoveAllDownloadSources}
|
||||||
|
visible={showConfirmationDeleteAllSourcesModal}
|
||||||
|
title={t("title_confirmation_delete_all_sources")}
|
||||||
|
onClose={() => setShowConfirmationDeleteAllSourcesModal(false)}
|
||||||
|
buttonsIsDisabled={isRemovingDownloadSource}
|
||||||
|
/>
|
||||||
|
|
||||||
<p>{t("download_sources_description")}</p>
|
<p>{t("download_sources_description")}</p>
|
||||||
|
|
||||||
@@ -125,7 +173,8 @@ export function SettingsDownloadSources() {
|
|||||||
disabled={
|
disabled={
|
||||||
!downloadSources.length ||
|
!downloadSources.length ||
|
||||||
isSyncingDownloadSources ||
|
isSyncingDownloadSources ||
|
||||||
isRemovingDownloadSource
|
isRemovingDownloadSource ||
|
||||||
|
isFetchingSources
|
||||||
}
|
}
|
||||||
onClick={syncDownloadSources}
|
onClick={syncDownloadSources}
|
||||||
>
|
>
|
||||||
@@ -133,15 +182,36 @@ export function SettingsDownloadSources() {
|
|||||||
{t("sync_download_sources")}
|
{t("sync_download_sources")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<div className="settings-download-sources__buttons-container">
|
||||||
type="button"
|
<Button
|
||||||
theme="outline"
|
type="button"
|
||||||
onClick={() => setShowAddDownloadSourceModal(true)}
|
theme="danger"
|
||||||
disabled={isSyncingDownloadSources}
|
onClick={() => setShowConfirmationDeleteAllSourcesModal(true)}
|
||||||
>
|
disabled={
|
||||||
<PlusCircleIcon />
|
isRemovingDownloadSource ||
|
||||||
{t("add_download_source")}
|
isSyncingDownloadSources ||
|
||||||
</Button>
|
!downloadSources.length ||
|
||||||
|
isFetchingSources
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TrashIcon />
|
||||||
|
{t("button_delete_all_sources")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => setShowAddDownloadSourceModal(true)}
|
||||||
|
disabled={
|
||||||
|
isSyncingDownloadSources ||
|
||||||
|
isFetchingSources ||
|
||||||
|
isRemovingDownloadSource
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PlusCircleIcon />
|
||||||
|
{t("add_download_source")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="settings-download-sources__list">
|
<ul className="settings-download-sources__list">
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function SettingsGeneral() {
|
|||||||
downloadNotificationsEnabled: false,
|
downloadNotificationsEnabled: false,
|
||||||
repackUpdatesNotificationsEnabled: false,
|
repackUpdatesNotificationsEnabled: false,
|
||||||
achievementNotificationsEnabled: false,
|
achievementNotificationsEnabled: false,
|
||||||
|
friendRequestNotificationsEnabled: false,
|
||||||
language: "",
|
language: "",
|
||||||
|
|
||||||
customStyles: window.localStorage.getItem("customStyles") || "",
|
customStyles: window.localStorage.getItem("customStyles") || "",
|
||||||
@@ -82,6 +83,8 @@ export function SettingsGeneral() {
|
|||||||
userPreferences.repackUpdatesNotificationsEnabled ?? false,
|
userPreferences.repackUpdatesNotificationsEnabled ?? false,
|
||||||
achievementNotificationsEnabled:
|
achievementNotificationsEnabled:
|
||||||
userPreferences.achievementNotificationsEnabled ?? false,
|
userPreferences.achievementNotificationsEnabled ?? false,
|
||||||
|
friendRequestNotificationsEnabled:
|
||||||
|
userPreferences.friendRequestNotificationsEnabled ?? false,
|
||||||
language: language ?? "en",
|
language: language ?? "en",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -171,6 +174,17 @@ export function SettingsGeneral() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={t("enable_friend_request_notifications")}
|
||||||
|
checked={form.friendRequestNotificationsEnabled}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({
|
||||||
|
friendRequestNotificationsEnabled:
|
||||||
|
!form.friendRequestNotificationsEnabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,22 @@
|
|||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
|
||||||
margin-bottom: calc(globals.$spacing-unit * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__submit-button {
|
&__submit-button {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__description-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
margin-bottom: calc(globals.$spacing-unit * 2);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__create-account {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,14 @@ import "./settings-real-debrid.scss";
|
|||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
|
|
||||||
import { settingsContext } from "@renderer/context";
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { LinkExternalIcon } from "@primer/octicons-react";
|
||||||
|
|
||||||
|
const realDebridReferralId = import.meta.env
|
||||||
|
.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID;
|
||||||
|
|
||||||
|
const REAL_DEBRID_URL = realDebridReferralId
|
||||||
|
? `https://real-debrid.com/?id=${realDebridReferralId}`
|
||||||
|
: "https://real-debrid.com";
|
||||||
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
||||||
|
|
||||||
export function SettingsRealDebrid() {
|
export function SettingsRealDebrid() {
|
||||||
@@ -74,24 +81,43 @@ export function SettingsRealDebrid() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleRealDebrid = () => {
|
||||||
|
const updatedValue = !form.useRealDebrid;
|
||||||
|
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
useRealDebrid: updatedValue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!updatedValue) {
|
||||||
|
updateUserPreferences({
|
||||||
|
realDebridApiToken: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
(form.useRealDebrid && !form.realDebridApiToken) || isLoading;
|
(form.useRealDebrid && !form.realDebridApiToken) || isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="settings-real-debrid__form" onSubmit={handleFormSubmit}>
|
<form className="settings-real-debrid__form" onSubmit={handleFormSubmit}>
|
||||||
<p className="settings-real-debrid__description">
|
<div className="settings-real-debrid__description-container">
|
||||||
{t("real_debrid_description")}
|
<p className="settings-real-debrid__description">
|
||||||
</p>
|
{t("real_debrid_description")}
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to={REAL_DEBRID_URL}
|
||||||
|
className="settings-real-debrid__create-account"
|
||||||
|
>
|
||||||
|
<LinkExternalIcon />
|
||||||
|
{t("create_real_debrid_account")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("enable_real_debrid")}
|
label={t("enable_real_debrid")}
|
||||||
checked={form.useRealDebrid}
|
checked={form.useRealDebrid}
|
||||||
onChange={() => {
|
onChange={toggleRealDebrid}
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
useRealDebrid: !form.useRealDebrid,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{form.useRealDebrid && (
|
{form.useRealDebrid && (
|
||||||
|
|||||||
@@ -7,12 +7,22 @@
|
|||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
|
||||||
margin-bottom: calc(globals.$spacing-unit * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__submit-button {
|
&__submit-button {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__description-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
margin-bottom: calc(globals.$spacing-unit * 2);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__create-account {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ import "./settings-torbox.scss";
|
|||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
|
|
||||||
import { settingsContext } from "@renderer/context";
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { LinkExternalIcon } from "@primer/octicons-react";
|
||||||
|
|
||||||
|
const torBoxReferralCode = import.meta.env.RENDERER_VITE_TORBOX_REFERRAL_CODE;
|
||||||
|
|
||||||
|
const TORBOX_URL = torBoxReferralCode
|
||||||
|
? `https://torbox.app/subscription?referral=${torBoxReferralCode}`
|
||||||
|
: "https://torbox.app";
|
||||||
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
|
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
|
||||||
|
|
||||||
export function SettingsTorbox() {
|
export function SettingsTorbox() {
|
||||||
@@ -69,19 +75,37 @@ export function SettingsTorbox() {
|
|||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
(form.useTorBox && !form.torBoxApiToken) || isLoading;
|
(form.useTorBox && !form.torBoxApiToken) || isLoading;
|
||||||
|
|
||||||
|
const toggleTorBox = () => {
|
||||||
|
const updatedValue = !form.useTorBox;
|
||||||
|
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
useTorBox: updatedValue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!updatedValue) {
|
||||||
|
updateUserPreferences({
|
||||||
|
torBoxApiToken: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="settings-torbox__form" onSubmit={handleFormSubmit}>
|
<form className="settings-torbox__form" onSubmit={handleFormSubmit}>
|
||||||
<p className="settings-torbox__description">{t("torbox_description")}</p>
|
<div className="settings-torbox__description-container">
|
||||||
|
<p className="settings-torbox__description">
|
||||||
|
{t("torbox_description")}
|
||||||
|
</p>
|
||||||
|
<Link to={TORBOX_URL} className="settings-torbox__create-account">
|
||||||
|
<LinkExternalIcon />
|
||||||
|
{t("create_torbox_account")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("enable_torbox")}
|
label={t("enable_torbox")}
|
||||||
checked={form.useTorBox}
|
checked={form.useTorBox}
|
||||||
onChange={() =>
|
onChange={toggleTorBox}
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
useTorBox: !form.useTorBox,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{form.useTorBox && (
|
{form.useTorBox && (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@use "../../scss/globals.scss";
|
@use "../../scss/globals.scss";
|
||||||
|
@use "sass:color";
|
||||||
|
|
||||||
.theme-editor {
|
.theme-editor {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -58,7 +59,10 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
background-color: darken(globals.$dark-background-color, 2%);
|
background-color: color.adjust(
|
||||||
|
globals.$dark-background-color,
|
||||||
|
$lightness: -2%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/renderer/src/vite-env.d.ts
vendored
2
src/renderer/src/vite-env.d.ts
vendored
@@ -4,6 +4,8 @@
|
|||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly RENDERER_VITE_EXTERNAL_RESOURCES_URL: string;
|
readonly RENDERER_VITE_EXTERNAL_RESOURCES_URL: string;
|
||||||
readonly RENDERER_VITE_SENTRY_DSN: string;
|
readonly RENDERER_VITE_SENTRY_DSN: string;
|
||||||
|
readonly RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: string;
|
||||||
|
readonly RENDERER_VITE_TORBOX_REFERRAL_CODE: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ type Payload =
|
|||||||
| ["IMPORT_DOWNLOAD_SOURCE", string]
|
| ["IMPORT_DOWNLOAD_SOURCE", string]
|
||||||
| ["DELETE_DOWNLOAD_SOURCE", number]
|
| ["DELETE_DOWNLOAD_SOURCE", number]
|
||||||
| ["VALIDATE_DOWNLOAD_SOURCE", string]
|
| ["VALIDATE_DOWNLOAD_SOURCE", string]
|
||||||
| ["SYNC_DOWNLOAD_SOURCES", string];
|
| ["SYNC_DOWNLOAD_SOURCES", string]
|
||||||
|
| ["DELETE_ALL_DOWNLOAD_SOURCES", string];
|
||||||
|
|
||||||
export type SteamGamesByLetter = Record<string, { id: string; name: string }[]>;
|
export type SteamGamesByLetter = Record<string, { id: string; name: string }[]>;
|
||||||
|
|
||||||
@@ -114,6 +115,13 @@ const deleteDownloadSource = async (id: number) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteAllDowloadSources = async () => {
|
||||||
|
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
||||||
|
await repacksTable.clear();
|
||||||
|
await downloadSourcesTable.clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self.onmessage = async (event: MessageEvent<Payload>) => {
|
self.onmessage = async (event: MessageEvent<Payload>) => {
|
||||||
const [type, data] = event.data;
|
const [type, data] = event.data;
|
||||||
|
|
||||||
@@ -132,6 +140,14 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === "DELETE_ALL_DOWNLOAD_SOURCES") {
|
||||||
|
await deleteAllDowloadSources();
|
||||||
|
|
||||||
|
const channel = new BroadcastChannel(`download_sources:delete_all:${data}`);
|
||||||
|
|
||||||
|
channel.postMessage(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
||||||
await deleteDownloadSource(data);
|
await deleteDownloadSource(data);
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,14 @@ export interface UserProfileCurrentGame extends Omit<GameRunning, "objectId"> {
|
|||||||
|
|
||||||
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||||
|
|
||||||
|
export interface Badge {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
badge: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface UserDetails {
|
export interface UserDetails {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -164,6 +172,7 @@ export interface UserProfile {
|
|||||||
quirks: {
|
quirks: {
|
||||||
backupsPerGameLimit: number;
|
backupsPerGameLimit: number;
|
||||||
};
|
};
|
||||||
|
badges: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProfileRequest {
|
export interface UpdateProfileRequest {
|
||||||
@@ -244,6 +253,7 @@ export interface GameArtifact {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
downloadCount: number;
|
downloadCount: number;
|
||||||
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComparedAchievements {
|
export interface ComparedAchievements {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface Game {
|
|||||||
executablePath?: string | null;
|
executablePath?: string | null;
|
||||||
launchOptions?: string | null;
|
launchOptions?: string | null;
|
||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
|
automaticCloudSync?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Download {
|
export interface Download {
|
||||||
@@ -75,9 +76,19 @@ export interface UserPreferences {
|
|||||||
runAtStartup?: boolean;
|
runAtStartup?: boolean;
|
||||||
startMinimized?: boolean;
|
startMinimized?: boolean;
|
||||||
disableNsfwAlert?: boolean;
|
disableNsfwAlert?: boolean;
|
||||||
|
enableAutoInstall?: boolean;
|
||||||
seedAfterDownloadComplete?: boolean;
|
seedAfterDownloadComplete?: boolean;
|
||||||
showHiddenAchievementsDescription?: boolean;
|
showHiddenAchievementsDescription?: boolean;
|
||||||
downloadNotificationsEnabled?: boolean;
|
downloadNotificationsEnabled?: boolean;
|
||||||
repackUpdatesNotificationsEnabled?: boolean;
|
repackUpdatesNotificationsEnabled?: boolean;
|
||||||
achievementNotificationsEnabled?: boolean;
|
achievementNotificationsEnabled?: boolean;
|
||||||
|
friendRequestNotificationsEnabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScreenState {
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
isMaximized: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
61
yarn.lock
61
yarn.lock
@@ -1663,6 +1663,14 @@
|
|||||||
"@floating-ui/core" "^1.6.0"
|
"@floating-ui/core" "^1.6.0"
|
||||||
"@floating-ui/utils" "^0.2.8"
|
"@floating-ui/utils" "^0.2.8"
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.6.1":
|
||||||
|
version "1.6.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.13.tgz#a8a938532aea27a95121ec16e667a7cbe8c59e34"
|
||||||
|
integrity sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.6.0"
|
||||||
|
"@floating-ui/utils" "^0.2.9"
|
||||||
|
|
||||||
"@floating-ui/react-dom@^2.0.0":
|
"@floating-ui/react-dom@^2.0.0":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31"
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31"
|
||||||
@@ -1675,6 +1683,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
|
||||||
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
||||||
|
|
||||||
|
"@floating-ui/utils@^0.2.9":
|
||||||
|
version "0.2.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429"
|
||||||
|
integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==
|
||||||
|
|
||||||
"@fontsource/noto-sans@^5.1.0":
|
"@fontsource/noto-sans@^5.1.0":
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.1.0.tgz#54a5edd1b2b8c8e17bec6a85d4ee3a53b4b89c1f"
|
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.1.0.tgz#54a5edd1b2b8c8e17bec6a85d4ee3a53b4b89c1f"
|
||||||
@@ -3559,6 +3572,11 @@ agent-base@^7.0.2, agent-base@^7.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
agent-base@^7.1.3:
|
||||||
|
version "7.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
|
||||||
|
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
|
||||||
|
|
||||||
agentkeepalive@^4.2.1:
|
agentkeepalive@^4.2.1:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
|
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
|
||||||
@@ -3895,6 +3913,13 @@ axe-core@^4.10.0:
|
|||||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
|
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
|
||||||
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
|
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
|
||||||
|
|
||||||
|
axios-cookiejar-support@^5.0.5:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios-cookiejar-support/-/axios-cookiejar-support-5.0.5.tgz#6030e17b438a64e3967a5ca190ee4202481c0ecf"
|
||||||
|
integrity sha512-jJG+p7JnOYxkVrYkCDKBrLqUmcpwHZTNQrEcIEKr5qe7YVTyPAD9nCsi1cO5LDmQpQApfS430czO+oceI3g/3g==
|
||||||
|
dependencies:
|
||||||
|
http-cookie-agent "^6.0.8"
|
||||||
|
|
||||||
axios@^1.7.9:
|
axios@^1.7.9:
|
||||||
version "1.7.9"
|
version "1.7.9"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
||||||
@@ -4279,7 +4304,7 @@ classic-level@^2.0.0:
|
|||||||
napi-macros "^2.2.2"
|
napi-macros "^2.2.2"
|
||||||
node-gyp-build "^4.3.0"
|
node-gyp-build "^4.3.0"
|
||||||
|
|
||||||
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.5.1:
|
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
|
||||||
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
||||||
@@ -6134,6 +6159,13 @@ http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||||
|
|
||||||
|
http-cookie-agent@^6.0.8:
|
||||||
|
version "6.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-cookie-agent/-/http-cookie-agent-6.0.8.tgz#f2635638f4172c7de0c482396ea7313e9731a62b"
|
||||||
|
integrity sha512-qnYh3yLSr2jBsTYkw11elq+T361uKAJaZ2dR4cfYZChw1dt9uL5t3zSUwehoqqVb4oldk1BpkXKm2oat8zV+oA==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^7.1.3"
|
||||||
|
|
||||||
http-proxy-agent@^5.0.0:
|
http-proxy-agent@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||||
@@ -8093,6 +8125,14 @@ react-style-singleton@^2.2.1:
|
|||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
react-tooltip@^5.28.0:
|
||||||
|
version "5.28.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.28.0.tgz#c7b5343ab2d740a428494a3d8315515af1f26f46"
|
||||||
|
integrity sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.6.1"
|
||||||
|
classnames "^2.3.0"
|
||||||
|
|
||||||
react@^18.2.0:
|
react@^18.2.0:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
||||||
@@ -9127,6 +9167,18 @@ tinyexec@^0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
|
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
|
||||||
integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
|
integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
|
||||||
|
|
||||||
|
tldts-core@^6.1.78:
|
||||||
|
version "6.1.78"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.78.tgz#47b477d9742870daa01dbd5ff9a598a48379728c"
|
||||||
|
integrity sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==
|
||||||
|
|
||||||
|
tldts@^6.1.32:
|
||||||
|
version "6.1.78"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.78.tgz#ee94576653a60d421ff94162c4e9060f2e62467b"
|
||||||
|
integrity sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ==
|
||||||
|
dependencies:
|
||||||
|
tldts-core "^6.1.78"
|
||||||
|
|
||||||
tmp-promise@^3.0.2:
|
tmp-promise@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
|
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
|
||||||
@@ -9174,6 +9226,13 @@ tough-cookie@^4.1.4:
|
|||||||
universalify "^0.2.0"
|
universalify "^0.2.0"
|
||||||
url-parse "^1.5.3"
|
url-parse "^1.5.3"
|
||||||
|
|
||||||
|
tough-cookie@^5.1.1:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.1.tgz#4641c1fdbf024927e29c5532edb7b6e5377ea1f2"
|
||||||
|
integrity sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==
|
||||||
|
dependencies:
|
||||||
|
tldts "^6.1.32"
|
||||||
|
|
||||||
tr46@^5.0.0:
|
tr46@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
|
||||||
|
|||||||
Reference in New Issue
Block a user