Merge branch 'main' into ci/testing-build-webhook

This commit is contained in:
Chubby Granny Chaser 2024-12-18 18:10:26 +00:00 committed by GitHub
commit 94f0df096d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 1202 additions and 630 deletions

View File

@ -47,6 +47,7 @@ jobs:
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }} RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.MAIN_VITE_EXTERNAL_RESOURCES_URL }}
- name: Build Windows - name: Build Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
@ -59,6 +60,7 @@ jobs:
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }} RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.MAIN_VITE_EXTERNAL_RESOURCES_URL }}
- name: Test Upload build - name: Test Upload build
env: env:

View File

@ -49,6 +49,7 @@ jobs:
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }} RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.MAIN_VITE_EXTERNAL_RESOURCES_URL }}
- name: Build Windows - name: Build Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: yarn build:win run: yarn build:win
@ -60,6 +61,7 @@ jobs:
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }} RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }} RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.RENDERER_VITE_EXTERNAL_RESOURCES_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.MAIN_VITE_EXTERNAL_RESOURCES_URL }}
- name: Create artifact - name: Create artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.9.20

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -1,6 +1,6 @@
<div align="center"> <div align="center">
[<img src="../resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="../resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -2,7 +2,7 @@
<div align="center"> <div align="center">
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site) [<img src="./resources/icon.png" width="144"/>](https://help.hydralauncher.gg)
<h1 align="center">Hydra Launcher</h1> <h1 align="center">Hydra Launcher</h1>

View File

@ -1,6 +1,6 @@
{ {
"name": "hydralauncher", "name": "hydralauncher",
"version": "3.0.7", "version": "3.0.8",
"description": "Hydra", "description": "Hydra",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "Los Broxas", "author": "Los Broxas",
@ -34,24 +34,24 @@
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.0.22", "@fontsource/noto-sans": "^5.1.0",
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.1",
"@primer/octicons-react": "^19.9.0", "@primer/octicons-react": "^19.9.0",
"@reduxjs/toolkit": "^2.2.3", "@reduxjs/toolkit": "^2.2.3",
"@vanilla-extract/css": "^1.14.2", "@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/dynamic": "^2.1.1", "@vanilla-extract/dynamic": "^2.1.2",
"@vanilla-extract/recipes": "^0.5.2", "@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6", "auto-launch": "^5.0.6",
"axios": "^1.7.7", "axios": "^1.7.9",
"better-sqlite3": "^11.3.0", "better-sqlite3": "^11.7.0",
"check-disk-space": "^3.4.0", "check-disk-space": "^3.4.0",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"color": "^4.2.3", "color": "^4.2.3",
"color.js": "^1.2.0", "color.js": "^1.2.0",
"create-desktop-shortcuts": "^1.11.0", "create-desktop-shortcuts": "^1.11.0",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"dexie": "^4.0.9", "dexie": "^4.0.10",
"electron-log": "^5.2.0", "electron-log": "^5.2.4",
"electron-updater": "^6.3.9", "electron-updater": "^6.3.9",
"file-type": "^19.6.0", "file-type": "^19.6.0",
"flexsearch": "^0.7.43", "flexsearch": "^0.7.43",
@ -72,15 +72,15 @@
"sudo-prompt": "^9.2.1", "sudo-prompt": "^9.2.1",
"tar": "^7.4.3", "tar": "^7.4.3",
"typeorm": "^0.3.20", "typeorm": "^0.3.20",
"user-agents": "^1.1.193", "user-agents": "^1.1.387",
"yaml": "^2.4.1", "yaml": "^2.6.1",
"yup": "^1.4.0", "yup": "^1.5.0",
"zod": "^3.23.8" "zod": "^3.24.1"
}, },
"devDependencies": { "devDependencies": {
"@aws-sdk/client-s3": "^3.705.0", "@aws-sdk/client-s3": "^3.705.0",
"@commitlint/cli": "^19.5.0", "@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.5.0", "@commitlint/config-conventional": "^19.6.0",
"@electron-toolkit/eslint-config-prettier": "^2.0.0", "@electron-toolkit/eslint-config-prettier": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^2.0.0", "@electron-toolkit/eslint-config-ts": "^2.0.0",
"@electron-toolkit/tsconfig": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1",
@ -88,8 +88,8 @@
"@types/auto-launch": "^5.0.5", "@types/auto-launch": "^5.0.5",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/folder-hash": "^4.0.4", "@types/folder-hash": "^4.0.4",
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.7",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.7",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7", "@types/node": "^20.12.7",
"@types/parse-torrent": "^5.8.7", "@types/parse-torrent": "^5.8.7",
@ -99,15 +99,15 @@
"@types/user-agents": "^1.0.4", "@types/user-agents": "^1.0.4",
"@vanilla-extract/vite-plugin": "^4.0.7", "@vanilla-extract/vite-plugin": "^4.0.7",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"electron": "^30.3.0", "electron": "^31.7.6",
"electron-builder": "^25.1.8", "electron-builder": "^25.1.8",
"electron-vite": "^2.0.0", "electron-vite": "^2.0.0",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"husky": "^9.0.11", "husky": "^9.1.7",
"prettier": "^3.2.4", "prettier": "^3.4.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"sass-embedded": "^1.80.6", "sass-embedded": "^1.80.6",

View File

@ -1,33 +1,49 @@
{ {
"language_name": "اَلْعَرَبِيَّةُ", "language_name": "اَلْعَرَبِيَّةُ",
"app": {
"successfully_signed_in": "تم تسجيل الدخول بنجاح"
},
"home": { "home": {
"featured": "مميّز", "featured": "مميّز",
"surprise_me": "فاجئني", "surprise_me": "فاجئني",
"no_results": "لم يتم العثور على نتائج" "no_results": "لم يتم العثور على نتائج",
"start_typing": "بدء الكتابة للبحث...",
"hot": "الأكثر رواجا الآن",
"weekly": "📅 أفضل ألعاب الأسبوع",
"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": "الرئيسية",
"queued": "{{title}} (في قائمة الانتظار)",
"game_has_no_executable": "لم يتم تحديد اللعبة القابلة للتنفيذ",
"sign_in": "تسجيل الدخول",
"friends": "أصدقاء",
"need_help": "بحاجة الى مساعدة؟"
}, },
"header": { "header": {
"search": "ابحث عن الألعاب", "search": "ابحث عن الألعاب",
"home": "الرئيسية", "home": "الرئيسية",
"catalogue": "قائمة الألعاب", "catalogue": "قائمة الألعاب",
"downloads": "التحميلات", "downloads": "التنزيلات",
"search_results": "نتائج البحث", "search_results": "نتائج البحث",
"settings": "إعدادات" "settings": "إعدادات",
"version_available_install": "إصدار {{version}} متاح. ",
"version_available_download": "إصدار {{version}} متاح. "
}, },
"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}} مكتمل) - حساب الوقت المتبقي...",
"checking_files": "التحقق {{title}} ملفات…({{percentage}} مكتمل)"
}, },
"catalogue": { "catalogue": {
"next_page": "الصفحة التالية", "next_page": "الصفحة التالية",
@ -35,101 +51,242 @@
}, },
"game_details": { "game_details": {
"open_download_options": "افتح خيارات التنزيل", "open_download_options": "افتح خيارات التنزيل",
"download_options_zero": "لا يوجد خيار تنزيل", "download_options_zero": "{{count}} خيارات التنزيل",
"download_options_one": "{{count}} خيار تنزيل",
"download_options_other": "{{count}} خيار تنزيل",
"updated_at": "تم التحديث {{updated_at}}", "updated_at": "تم التحديث {{updated_at}}",
"install": "تثبيت", "install": "ثَبَّتَ",
"resume": "استئناف", "resume": "استئناف",
"pause": "إيقاف", "pause": "إيقاف",
"cancel": "إلغاء", "cancel": "إلغاء",
"remove": "إزالة", "remove": "إزالة",
"space_left_on_disk": "{{space}} متبقية على القرص", "space_left_on_disk": "{{space}} متبقية على القرص",
"eta": "الوقت المتبقي {{eta}}", "eta": "الوقت المتبقي {{eta}}",
"downloading_metadata": "جاري تنزيل البيانات الوصفية...", "calculating_eta": "جارٍ حساب الوقت المتبقي…",
"filter": "تصفية حزم إعادة التجميع", "downloading_metadata": "جارٍ تنزيل البيانات الوصفية…",
"filter": "إعادة حزم التصفية",
"requirements": "متطلبات النظام", "requirements": "متطلبات النظام",
"minimum": "الحد الأدنى", "minimum": "الحد الأدنى",
"recommended": "موصى به", "recommended": "مُستَحسَن",
"release_date": "تم الإصدار في {{date}}", "paused": "متوقف مؤقتًا",
"publisher": "نشر بواسطة {{publisher}}", "release_date": "صدر بتاريخ {{date}}",
"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": "لتغيير المجلد الافتراضي، انتقل إلى الإعدادات", "select_folder_hint": "لتغيير المجلد الافتراضي، انتقل إلى <0>إعدادات</0>",
"download_now": "تنزيل الآن", "download_now": "قم بالتنزيل الآن",
"no_shop_details": م يتم استرداد تفاصيل المتجر.", "no_shop_details": ا يمكن استرداد تفاصيل المتجر.",
"download_options": "خيارات التنزيل", "download_options": "خيارات التنزيل",
"download_path": "مسار التنزيل", "download_path": "مسار التحميل",
"previous_screenshot": "لقطة الشاشة السابقة", "previous_screenshot": "لقطة الشاشة السابقة",
"next_screenshot": "لقطة الشاشة التالية", "next_screenshot": "لقطة الشاشة التالية",
"screenshot": "لقطة شاشة {{number}}", "screenshot": "لقطة الشاشة {{number}}",
"open_screenshot": "افتح لقطة الشاشة {{number}}" "open_screenshot": "فتح لقطة الشاشة {{number}}",
"download_settings": "تحميل الإعدادات",
"downloader": "أداة التنزيل",
"select_executable": "يختار",
"no_executable_selected": "لم يتم تحديد أي ملف قابل للتنفيذ",
"open_folder": "افتح المجلد",
"open_download_location": "انظر الملفات التي تم تنزيلها",
"create_shortcut": "إنشاء اختصار سطح المكتب",
"clear": "واضح",
"remove_files": "إزالة الملفات",
"remove_from_library_title": "هل أنت متأكد؟",
"remove_from_library_description": "سيتم إزالة هذا {{game}} من مكتبتك",
"options": "خيارات",
"executable_section_title": "قابل للتنفيذ",
"executable_section_description": "مسار الملف الذي سيتم تنفيذه عند النقر فوق \"تشغيل\".",
"downloads_secion_title": "التنزيلات",
"downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى من هذه اللعبة",
"danger_zone_section_title": "منطقة الخطر",
"danger_zone_section_description": "قم بإزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra",
"download_in_progress": "التنزيل قيد التقدم",
"download_paused": "تم إيقاف التنزيل مؤقتًا",
"last_downloaded_option": "آخر خيار تم تنزيله",
"create_shortcut_success": "تم إنشاء الاختصار بنجاح",
"create_shortcut_error": "حدث خطأ أثناء إنشاء الاختصار",
"nsfw_content_title": "تحتوي هذه اللعبة على محتوى غير مناسب",
"nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يكون مناسبًا لجميع الأعمار. ",
"allow_nsfw_content": "اسمح",
"refuse_nsfw_content": "عُد",
"stats": "احصائيات",
"download_count": "التنزيلات",
"player_count": "اللاعبين النشطين",
"download_error": "خيار التنزيل هذا غير متوفر",
"download": "تحميل",
"executable_path_in_use": "قابل للتنفيذ قيد الاستخدام بالفعل بواسطة \"{{game}}\"",
"warning": "تحذير:",
"hydra_needs_to_remain_open": "لإجراء هذا التنزيل، يجب أن يظل Hydra مفتوحًا حتى اكتماله. ",
"achievements": "الإنجازات",
"achievements_count": "الإنجازات {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "حفظ السحابة",
"cloud_save_description": "احفظ تقدمك في السحابة واستمر في اللعب على أي جهاز",
"backups": "النسخ الاحتياطية",
"install_backup": "ثَبَّتَ",
"delete_backup": "يمسح",
"create_backup": "نسخة احتياطية جديدة",
"last_backup_date": "آخر نسخة احتياطية قيد التشغيل {{date}}",
"no_backup_preview": "لم يتم العثور على ألعاب محفوظة لهذا العنوان",
"restoring_backup": "استعادة النسخة الاحتياطية ({{progress}} مكتمل)…",
"uploading_backup": "جارٍ تحميل النسخة الاحتياطية…",
"no_backups": "لم تقم بإنشاء أي نسخ احتياطية لهذه اللعبة حتى الآن",
"backup_uploaded": "تم تحميل النسخة الاحتياطية",
"backup_deleted": "تم حذف النسخة الاحتياطية",
"backup_restored": "تمت استعادة النسخة الاحتياطية",
"see_all_achievements": "شاهد جميع الإنجازات",
"sign_in_to_see_achievements": "قم بتسجيل الدخول لرؤية الإنجازات",
"mapping_method_automatic": "تلقائي",
"mapping_method_manual": "يدوي",
"mapping_method_label": "طريقة رسم الخرائط",
"files_automatically_mapped": "تم تعيين الملفات تلقائيًا",
"no_backups_created": "لم يتم إنشاء نسخ احتياطية لهذه اللعبة",
"manage_files": "إدارة الملفات",
"loading_save_preview": "جارٍ البحث عن حفظ الألعاب...",
"wine_prefix": "بادئة النبيذ",
"wine_prefix_description": "بادئة Wine المستخدمة لتشغيل هذه اللعبة",
"no_download_option_info": "لا توجد معلومات متاحة",
"backup_deletion_failed": "فشل في حذف النسخة الاحتياطية",
"max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى لعدد النسخ الاحتياطية لهذه اللعبة",
"achievements_not_sync": "لا تتم مزامنة إنجازاتك",
"manage_files_description": "إدارة الملفات التي سيتم نسخها احتياطيًا واستعادتها",
"select_folder": "حدد المجلد",
"backup_from": "نسخة احتياطية من {{date}}",
"custom_backup_location_set": "تعيين موقع النسخ الاحتياطي المخصص",
"no_directory_selected": "لم يتم تحديد أي دليل",
"download_options_one": "{{count}} خيار التنزيل",
"download_options_two": "{{count}} خيارات التنزيل",
"download_options_few": "{{count}} خيارات التنزيل",
"download_options_many": "{{count}} خيارات التنزيل",
"download_options_other": "{{count}} خيارات التنزيل"
}, },
"activation": { "activation": {
"title": "تفعيل هايدرا", "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": "لم يتم تحميلها",
"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": "في تَقَدم",
"queued_downloads": "التنزيلات في قائمة الانتظار",
"downloads_completed": "مكتمل",
"queued": "في قائمة الانتظار",
"no_downloads_title": "هذا فارغ",
"no_downloads_description": "لم تقم بتنزيل أي شيء باستخدام Hydra بعد، ولكن لم يفت الأوان بعد للبدء.",
"checking_files": "جارٍ فحص الملفات…"
}, },
"settings": { "settings": {
"downloads_path": "مسار التنزيلات", "downloads_path": "مسار التنزيلات",
"change": "تحديث", "change": "تحديث",
"notifications": "الإشعارات", "notifications": "إشعارات",
"enable_download_notifications": "عند اكتمال التنزيل", "enable_download_notifications": "عند اكتمال التنزيل",
"enable_repack_list_notifications": "عند إضافة حزمة جديدة", "enable_repack_list_notifications": "عند إضافة حزمة جديدة",
"real_debrid_api_token_label": "رمز واجهة برمجة التطبيقات (API) لـReal-Debrid ", "real_debrid_api_token_label": "رمز Real-Debrid API",
"quit_app_instead_hiding": "إنهاء هايدرا بدلاً من التصغير الى شريط الحالة", "quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق",
"launch_with_system": "تشغيل هايدرا عند بدء تشغيل النظام", "launch_with_system": "قم بتشغيل Hydra عند بدء تشغيل النظام",
"general": "عام", "general": "عام",
"behavior": "السلوك", "behavior": "سلوك",
"enable_real_debrid": "تفعيل Real-Debrid ", "download_sources": "تحميل المصادر",
"real_debrid_api_token_hint": "يمكنك الحصول على مفتاح API الخاص بك هنا", "language": "لغة",
"save_changes": "حفظ التغييرات" "real_debrid_api_token": "رمز API",
"enable_real_debrid": "تمكين ريال ديبريد",
"real_debrid_description": "Real-Debrid هو برنامج تنزيل غير مقيد يسمح لك بتنزيل الملفات بسرعة، ولا يقتصر ذلك إلا على سرعة الإنترنت لديك.",
"real_debrid_invalid_token": "رمز API غير صالح",
"real_debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا</0>",
"real_debrid_free_account_error": "الحساب \"{{username}}\" هو حساب مجاني. يرجى الاشتراك في Real-Debrid",
"real_debrid_linked_message": "حساب \"{{username}}\"مرتبط",
"save_changes": "حفظ التغييرات",
"changes_saved": "تم حفظ التغييرات بنجاح",
"download_sources_description": "ستقوم Hydra بجلب روابط التنزيل من هذه المصادر. ",
"validate_download_source": "التحقق من صحة",
"remove_download_source": "إزالة",
"add_download_source": "أضف المصدر",
"download_count_zero": "{{countFormatted}} خيارات التنزيل",
"download_source_url": "تنزيل عنوان URL المصدر",
"add_download_source_description": "أدخل عنوان URL لملف .json",
"download_source_up_to_date": "محدث",
"download_source_errored": "خطأ",
"sync_download_sources": "مصادر المزامنة",
"removed_download_source": "تمت إزالة مصدر التنزيل",
"added_download_source": "تمت إضافة مصدر التنزيل",
"download_sources_synced": "تتم مزامنة جميع مصادر التنزيل",
"insert_valid_json_url": "أدخل عنوان URL صالحًا لـ JSON",
"found_download_option_zero": "وجد {{countFormatted}} خيارات التنزيل",
"import": "يستورد",
"public": "عام",
"private": "خاص",
"friends_only": "الأصدقاء فقط",
"privacy": "خصوصية",
"profile_visibility": "رؤية الملف الشخصي",
"profile_visibility_description": "اختر من يمكنه رؤية ملفك الشخصي ومكتبتك",
"required_field": "هذه الخانة مطلوبه",
"source_already_exists": "تمت إضافة هذا المصدر بالفعل",
"must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالحًا",
"blocked_users": "المستخدمين المحظورين",
"user_unblocked": "تم إلغاء حظر المستخدم",
"enable_achievement_notifications": "عندما يتم فتح الإنجاز",
"launch_minimized": "تم تصغير إطلاق Hydra",
"disable_nsfw_alert": "تعطيل تنبيه NSFW",
"show_hidden_achievement_description": "إظهار وصف الإنجازات المخفية قبل فتحها",
"download_count_one": "{{countFormatted}} خيار التنزيل",
"download_count_two": "{{countFormatted}} خيارات التنزيل",
"download_count_few": "{{countFormatted}} خيارات التنزيل",
"download_count_many": "{{countFormatted}} خيارات التنزيل",
"download_count_other": "{{countFormatted}} خيارات التنزيل",
"found_download_option_one": "وجد {{countFormatted}} خيار التنزيل",
"found_download_option_two": "وجد {{countFormatted}} خيارات التنزيل",
"found_download_option_few": "وجد {{countFormatted}} خيارات التنزيل",
"found_download_option_many": "وجد {{countFormatted}} خيارات التنزيل",
"found_download_option_other": "وجد {{countFormatted}} خيارات التنزيل"
}, },
"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}} حزمة مضافة", "new_update_available": "إصدار {{version}} متاح",
"repack_count_other": "{{count}} حزم مُضافة" "restart_to_install_update": "أعد تشغيل Hydra لتثبيت التحديث",
"notification_achievement_unlocked_title": "تم فتح الإنجاز لـ {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} وغيرها {{count}} تم فتحها",
"repack_count_zero": "{{count}} تمت إضافة العبوات",
"repack_count_one": "{{count}} تمت إضافة أعد حزم",
"repack_count_two": "{{count}} تمت إضافة العبوات",
"repack_count_few": "{{count}} تمت إضافة العبوات",
"repack_count_many": "{{count}} تمت إضافة العبوات",
"repack_count_other": "{{count}} تمت إضافة العبوات"
}, },
"system_tray": { "system_tray": {
"open": "فتح هايدرا", "open": "افتح Hydra",
"quit": "خروج" "quit": "خروج"
}, },
"game_card": { "game_card": {
@ -137,10 +294,109 @@
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
"title": "البرامج غير مثبتة", "title": "البرامج غير مثبتة",
"description": "لم يتم العثور على ملفات Wine أو Lutris التنفيذية على نظامك", "description": "لم يتم العثور على الملفات التنفيذية الخاصة بـ Wine أو Lutris على نظامك",
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة Linux الخاصة بك حتى تعمل اللعبة بشكل طبيعي" "instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة Linux لديك حتى تعمل اللعبة بشكل طبيعي"
}, },
"modal": { "modal": {
"close": "زر إغلاق" "close": "زر الإغلاق"
},
"forms": {
"toggle_password_visibility": "تبديل رؤية كلمة المرور"
},
"user_profile": {
"amount_hours": "{{amount}} ساعات",
"amount_minutes": "{{amount}} دقائق",
"last_time_played": "لعبت آخر مرة {{period}}",
"activity": "النشاط الأخير",
"library": "مكتبة",
"total_play_time": "إجمالي وقت اللعب: {{amount}}",
"no_recent_activity_title": "هممم... لا شيء هنا",
"no_recent_activity_description": "لم تلعب أي مباراة مؤخرًا. ",
"display_name": "اسم العرض",
"saving": "توفير",
"save": "يحفظ",
"edit_profile": "تحرير الملف الشخصي",
"saved_successfully": "تم الحفظ بنجاح",
"try_again": "من فضلك، حاول مرة أخرى",
"sign_out_modal_title": "هل أنت متأكد؟",
"cancel": "إلغاء",
"successfully_signed_out": "تم تسجيل الخروج بنجاح",
"sign_out": "تسجيل الخروج",
"playing_for": "اللعب من أجل {{amount}}",
"sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. ",
"add_friends": "أضف أصدقاء",
"add": "يضيف",
"friend_code": "رمز الصديق",
"see_profile": "انظر الملف الشخصي",
"sending": "إرسال",
"friend_request_sent": "تم إرسال طلب الصداقة",
"friends": "أصدقاء",
"friends_list": "قائمة الأصدقاء",
"user_not_found": "لم يتم العثور على المستخدم",
"block_user": "حظر المستخدم",
"add_friend": "إضافة صديق",
"request_sent": "تم إرسال الطلب",
"request_received": "تم استلام الطلب",
"accept_request": "قبول الطلب",
"ignore_request": "تجاهل الطلب",
"cancel_request": "إلغاء الطلب",
"undo_friendship": "التراجع عن الصداقة",
"request_accepted": "تم قبول الطلب",
"user_blocked_successfully": "تم حظر المستخدم بنجاح",
"user_block_modal_text": "هذا سوف يمنع {{displayName}}",
"blocked_users": "المستخدمين المحظورين",
"unblock": "إلغاء الحظر",
"no_friends_added": "ليس لديك أي أصدقاء مضافين",
"pending": "قيد الانتظار",
"no_pending_invites": "ليس لديك أي دعوات معلقة",
"no_blocked_users": "ليس لديك أي مستخدمين محظورين",
"friend_code_copied": "تم نسخ رمز الصديق",
"undo_friendship_modal_text": "سيؤدي هذا إلى التراجع عن صداقتك معه {{displayName}}",
"privacy_hint": "لضبط من يمكنه رؤية هذا، انتقل إلى <0>إعدادات</0>",
"locked_profile": "هذا الملف الشخصي خاص",
"image_process_failure": "فشل أثناء معالجة الصورة",
"required_field": "هذه الخانة مطلوبه",
"displayname_min_length": "يجب أن يتكون اسم العرض من 3 أحرف على الأقل",
"displayname_max_length": "يجب ألا يزيد طول اسم العرض عن 50 حرفًا",
"report_profile": "الإبلاغ عن هذا الملف الشخصي",
"report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف الشخصي؟",
"report_description": "معلومات إضافية",
"report_description_placeholder": "معلومات إضافية",
"report": "تقرير",
"report_reason_hate": "خطاب الكراهية",
"report_reason_sexual_content": "المحتوى الجنسي",
"report_reason_violence": "عنف",
"report_reason_spam": "رسائل إلكترونية مزعجة",
"profile_reported": "تم الإبلاغ عن الملف الشخصي",
"your_friend_code": "رمز صديقك:",
"upload_banner": "تحميل لافتة",
"uploading_banner": "جارٍ تحميل البانر…",
"background_image_updated": "تم تحديث صورة الخلفية",
"report_reason_zero": "آخر",
"report_reason_one": "لماذا تقوم بالإبلاغ عن هذا الملف الشخصي؟",
"report_reason_two": "آخر",
"report_reason_few": "آخر",
"report_reason_many": "آخر",
"report_reason_other": "آخر"
},
"achievement": {
"achievement_unlocked": "تم فتح الإنجاز",
"user_achievements": "{{displayName}}إنجازات",
"your_achievements": "إنجازاتك",
"unlocked_at": "مقفلة في:",
"subscription_needed": "مطلوب اشتراك Hydra Cloud لرؤية هذا المحتوى",
"new_achievements_unlocked": "مفتوح {{achievementCount}} انجازات جديدة من {{gameCount}} ألعاب",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} الإنجازات",
"achievements_unlocked_for_game": "مفتوح {{achievementCount}} انجازات جديدة ل {{gameTitle}}"
},
"tour": {
"subscription_tour_title": "اشتراك Hydra كلاود",
"subscribe_now": "اشترك الآن",
"cloud_saving": "الحفظ السحابي",
"cloud_achievements": "احفظ إنجازاتك على السحابة",
"animated_profile_picture": "صور شخصية متحركة",
"premium_support": "دعم متميز",
"show_and_compare_achievements": "عرض ومقارنة إنجازاتك مع المستخدمين الآخرين",
"animated_profile_banner": "لافتة الملف الشخصي المتحركة"
} }
} }

View File

@ -258,7 +258,8 @@
"user_unblocked": "User has been unblocked", "user_unblocked": "User has been unblocked",
"enable_achievement_notifications": "When an achievement is unlocked", "enable_achievement_notifications": "When an achievement is unlocked",
"launch_minimized": "Launch Hydra minimized", "launch_minimized": "Launch Hydra minimized",
"disable_nsfw_alert": "Disable NSFW alert" "disable_nsfw_alert": "Disable NSFW alert",
"show_hidden_achievement_description": "Show hidden achievements description before unlocking them"
}, },
"notifications": { "notifications": {
"download_complete": "Download complete", "download_complete": "Download complete",

View File

@ -254,7 +254,8 @@
"user_unblocked": "Usuário desbloqueado", "user_unblocked": "Usuário desbloqueado",
"enable_achievement_notifications": "Quando uma conquista é desbloqueada", "enable_achievement_notifications": "Quando uma conquista é desbloqueada",
"launch_minimized": "Iniciar o Hydra minimizado", "launch_minimized": "Iniciar o Hydra minimizado",
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado" "disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado",
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las"
}, },
"notifications": { "notifications": {
"download_complete": "Download concluído", "download_complete": "Download concluído",

View File

@ -4,12 +4,13 @@
"successfully_signed_in": "Успешный вход" "successfully_signed_in": "Успешный вход"
}, },
"home": { "home": {
"featured": "Рекомендованное", "featured": "Рекомендации",
"surprise_me": "Удиви меня", "surprise_me": "Удиви меня",
"no_results": "Ничего не найдено", "no_results": "Ничего не найдено",
"hot": "Сейчас в топе", "hot": "Сейчас в топе",
"start_typing": "Начинаю вводить текст для поиска...", "start_typing": "Начинаю вводить текст для поиска...",
"weekly": "📅 Лучшие игры недели" "weekly": "📅 Лучшие игры недели",
"achievements": "🏆 Игры, в которых нужно победить"
}, },
"sidebar": { "sidebar": {
"catalogue": "Каталог", "catalogue": "Каталог",
@ -19,7 +20,7 @@
"downloading_metadata": "{{title}} (Загрузка метаданных…)", "downloading_metadata": "{{title}} (Загрузка метаданных…)",
"paused": "{{title}} (Приостановлено)", "paused": "{{title}} (Приостановлено)",
"downloading": "{{title}} ({{percentage}} - Загрузка…)", "downloading": "{{title}} ({{percentage}} - Загрузка…)",
"filter": "Фильтр библиотеки", "filter": "Поиск",
"home": "Главная", "home": "Главная",
"queued": "{{title}} (В очереди)", "queued": "{{title}} (В очереди)",
"game_has_no_executable": "Файл запуска игры не выбран", "game_has_no_executable": "Файл запуска игры не выбран",
@ -49,10 +50,10 @@
"previous_page": "Предыдущая страница" "previous_page": "Предыдущая страница"
}, },
"game_details": { "game_details": {
"open_download_options": "Открыть варианты загрузки", "open_download_options": "Открыть источники",
"download_options_zero": "Нет вариантов загрузки", "download_options_zero": "Нет источников",
"download_options_one": "{{count}} вариант загрузки", "download_options_one": "{{count}} источник",
"download_options_other": "{{count}} вариантов загрузки", "download_options_other": "{{count}} источников",
"updated_at": "Обновлено {{updated_at}}", "updated_at": "Обновлено {{updated_at}}",
"install": "Установить", "install": "Установить",
"resume": "Возобновить", "resume": "Возобновить",
@ -63,7 +64,7 @@
"eta": "Окончание {{eta}}", "eta": "Окончание {{eta}}",
"calculating_eta": "Подсчёт оставшегося времени…", "calculating_eta": "Подсчёт оставшегося времени…",
"downloading_metadata": "Загрузка метаданных…", "downloading_metadata": "Загрузка метаданных…",
"filter": "Фильтр репаков", "filter": "Поиск репаков",
"requirements": "Системные требования", "requirements": "Системные требования",
"minimum": "Минимальные", "minimum": "Минимальные",
"recommended": "Рекомендуемые", "recommended": "Рекомендуемые",
@ -77,7 +78,7 @@
"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}}",
@ -91,7 +92,7 @@
"select_folder_hint": "Чтобы изменить папку загрузок по умолчанию, откройте <0>Настройки</0>", "select_folder_hint": "Чтобы изменить папку загрузок по умолчанию, откройте <0>Настройки</0>",
"download_now": "Загрузить сейчас", "download_now": "Загрузить сейчас",
"no_shop_details": "Не удалось получить описание", "no_shop_details": "Не удалось получить описание",
"download_options": "Вариантов загрузки", "download_options": "Источники",
"download_path": "Путь для загрузок", "download_path": "Путь для загрузок",
"previous_screenshot": "Предыдущий скриншот", "previous_screenshot": "Предыдущий скриншот",
"next_screenshot": "Следующий скриншот", "next_screenshot": "Следующий скриншот",
@ -119,16 +120,53 @@
"last_downloaded_option": "Последний вариант загрузки", "last_downloaded_option": "Последний вариант загрузки",
"create_shortcut_success": "Ярлык создан", "create_shortcut_success": "Ярлык создан",
"create_shortcut_error": "Не удалось создать ярлык", "create_shortcut_error": "Не удалось создать ярлык",
"allow_nsfw_content": "Продолжать", "allow_nsfw_content": "Продолжить",
"download": "Скачать", "download": "Скачать",
"download_count": "Загрузки", "download_count": "Загрузки",
"download_error": "Этот вариант загрузки недоступен", "download_error": "Этот вариант загрузки недоступен",
"executable_path_in_use": "Исполняемый файл уже используется \"{{game}}\"", "executable_path_in_use": "Исполняемый файл уже используется \"{{game}}\"",
"nsfw_content_description": "{{title}} содержит контент, который может не подходить для всех возрастов. \nВы уверены, что хотите продолжить?", "nsfw_content_description": "{{title}} содержит контент, который может не подходить для всех возрастов. \nВы уверены, что хотите продолжить?",
"nsfw_content_title": "Эта игра содержит неприемлемый контент", "nsfw_content_title": "Эта игра содержит неприемлемый контент",
"refuse_nsfw_content": "Назад",
"stats": "Статистика",
"player_count": "Активные игроки", "player_count": "Активные игроки",
"refuse_nsfw_content": "Возвращаться", "warning": "Внимание:",
"stats": "Статистика" "hydra_needs_to_remain_open": "Для этой загрузки Hydra должна оставаться открытой до завершения. Если Hydra закроется до завершения, вы потеряете прогресс.",
"achievements": "Достижения",
"achievements_count": "Достижения {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Облачное сохранение",
"cloud_save_description": "Сохраняйте ваш прогресс в облаке и продолжайте играть на любом устройстве",
"backups": "Резервные копии",
"install_backup": "Установить",
"delete_backup": "Удалить",
"create_backup": "Создать новую резервную копию",
"last_backup_date": "Последняя резервная копия от {{date}}",
"no_backup_preview": "Сохранения для этого заголовка не найдены",
"restoring_backup": "Восстановление резервной копии ({{progress}} завершено)…",
"uploading_backup": "Загрузка резервной копии…",
"no_backups": "Вы еще не создали резервных копий для этой игры",
"backup_uploaded": "Резервная копия загружена",
"backup_deleted": "Резервная копия удалена",
"backup_restored": "Резервная копия восстановлена",
"see_all_achievements": "Просмотреть все достижения",
"sign_in_to_see_achievements": "Войдите, чтобы увидеть достижения",
"mapping_method_automatic": "Автоматическая",
"mapping_method_manual": "Ручная",
"mapping_method_label": "Метод сопоставления",
"files_automatically_mapped": "Файлы автоматически сопоставлены",
"no_backups_created": "Для этой игры не создано резервных копий",
"manage_files": "Управление файлами",
"loading_save_preview": "Поиск сохранений…",
"wine_prefix": "Префикс Wine",
"wine_prefix_description": "Префикс Wine, используемый для запуска этой игры",
"no_download_option_info": "Информация недоступна",
"backup_deletion_failed": "Не удалось удалить резервную копию",
"max_number_of_artifacts_reached": "Достигнуто максимальное количество резервных копий для этой игры",
"achievements_not_sync": "Ваши достижения не синхронизированы",
"manage_files_description": "Управляйте файлами, которые будут сохраняться и восстанавливаться",
"select_folder": "Выбрать папку",
"backup_from": "Резервная копия от {{date}}",
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии"
}, },
"activation": { "activation": {
"title": "Активировать Hydra", "title": "Активировать Hydra",
@ -147,7 +185,7 @@
"completed": "Завершено", "completed": "Завершено",
"removed": "Не скачано", "removed": "Не скачано",
"cancel": "Отмена", "cancel": "Отмена",
"filter": "Фильтр загруженных игр", "filter": "Поиск загруженных игр",
"remove": "Удалить", "remove": "Удалить",
"downloading_metadata": "Загрузка метаданных…", "downloading_metadata": "Загрузка метаданных…",
"deleting": "Удаление установщика…", "deleting": "Удаление установщика…",
@ -168,10 +206,13 @@
"change": "Изменить", "change": "Изменить",
"notifications": "Уведомления", "notifications": "Уведомления",
"enable_download_notifications": "По завершении загрузки", "enable_download_notifications": "По завершении загрузки",
"enable_achievement_notifications": "Когда достижение разблокировано",
"enable_repack_list_notifications": "При добавлении нового репака", "enable_repack_list_notifications": "При добавлении нового репака",
"real_debrid_api_token_label": "Real-Debrid API-токен", "real_debrid_api_token_label": "Real-Debrid API-токен",
"quit_app_instead_hiding": "Закрывать приложение вместо сворачивания в трей", "quit_app_instead_hiding": "Закрывать приложение вместо сворачивания в трей",
"launch_with_system": "Запускать Hydra вместе с системой", "launch_with_system": "Запускать Hydra вместе с системой",
"launch_minimized": "Запустить Hydra в свернутом виде",
"disable_nsfw_alert": "Отключить предупреждение о непристойном контенте",
"general": "Основные", "general": "Основные",
"behavior": "Поведение", "behavior": "Поведение",
"download_sources": "Источники загрузки", "download_sources": "Источники загрузки",
@ -196,7 +237,7 @@
"add_download_source_description": "Вставьте ссылку на .json-файл", "add_download_source_description": "Вставьте ссылку на .json-файл",
"download_source_up_to_date": "Обновлён", "download_source_up_to_date": "Обновлён",
"download_source_errored": "Ошибка", "download_source_errored": "Ошибка",
"sync_download_sources": "Синхронизировать источники", "sync_download_sources": "Обновить источники",
"removed_download_source": "Источник загрузок удален", "removed_download_source": "Источник загрузок удален",
"added_download_source": "Источник загрузок добавлен", "added_download_source": "Источник загрузок добавлен",
"download_sources_synced": "Все источники загрузок синхронизированы", "download_sources_synced": "Все источники загрузок синхронизированы",
@ -206,13 +247,13 @@
"found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки", "found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки",
"import": "Импортировать", "import": "Импортировать",
"blocked_users": "Заблокированные пользователи", "blocked_users": "Заблокированные пользователи",
"friends_only": "Только друзья", "friends_only": "Только для друзей",
"must_be_valid_url": "Источник должен быть действительным URL-адресом.", "must_be_valid_url": "Источник должен быть действительным URL-адресом.",
"privacy": "Конфиденциальность", "privacy": "Конфиденциальность",
"private": "Частный", "private": "Частный",
"profile_visibility": "Видимость профиля", "profile_visibility": "Видимость профиля",
"profile_visibility_description": "Выберите, кто может видеть ваш профиль и библиотеку", "profile_visibility_description": "Выберите, кто может видеть ваш профиль и библиотеку",
"public": "Общественный", "public": "Публичный",
"required_field": "Это поле обязательно к заполнению", "required_field": "Это поле обязательно к заполнению",
"source_already_exists": "Этот источник уже добавлен", "source_already_exists": "Этот источник уже добавлен",
"user_unblocked": "Пользователь разблокирован" "user_unblocked": "Пользователь разблокирован"
@ -223,15 +264,17 @@
"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_body": "были разблокированы {{achievement}} и другие {{count}}"
}, },
"system_tray": { "system_tray": {
"open": "Открыть Hydra", "open": "Открыть Hydra",
"quit": "Выйти" "quit": "Выйти"
}, },
"game_card": { "game_card": {
"no_downloads": "Нет доступных загрузок" "no_downloads": "Нет доступных источников"
}, },
"binary_not_found_modal": { "binary_not_found_modal": {
"title": "Программы не установлены", "title": "Программы не установлены",
@ -310,5 +353,25 @@
"report_reason_violence": "Насилие", "report_reason_violence": "Насилие",
"required_field": "Это поле обязательно к заполнению", "required_field": "Это поле обязательно к заполнению",
"undo_friendship_modal_text": "Это отменит вашу дружбу с {{displayName}}." "undo_friendship_modal_text": "Это отменит вашу дружбу с {{displayName}}."
},
"achievement": {
"achievement_unlocked": "Достижение разблокировано",
"user_achievements": "Достижения {{displayName}}",
"your_achievements": "Ваши достижения",
"unlocked_at": "Разблокировано:",
"subscription_needed": "Для просмотра этого содержимого необходима подписка на Hydra Cloud",
"new_achievements_unlocked": "Разблокировано {{achievementCount}} новых достижений из {{gameCount}} игр",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} достижений",
"achievements_unlocked_for_game": "Разблокировано {{achievementCount}} новых достижений для {{gameTitle}}"
},
"tour": {
"subscription_tour_title": "Подписка Hydra Cloud",
"subscribe_now": "Подпишитесь прямо сейчас",
"cloud_saving": "Сохранение в облаке",
"cloud_achievements": "Сохраняйте свои достижения в облаке",
"animated_profile_picture": "Анимированные фотографии профиля",
"premium_support": "Премиальная поддержка",
"show_and_compare_achievements": "Показывайте и сравнивайте свои достижения с достижениями других пользователей",
"animated_profile_banner": "Анимированный баннер профиля"
} }
} }

View File

@ -41,6 +41,9 @@ export class UserPreferences {
@Column("boolean", { default: false }) @Column("boolean", { default: false })
disableNsfwAlert: boolean; disableNsfwAlert: boolean;
@Column("boolean", { default: false })
showHiddenAchievementsDescription: boolean;
@CreateDateColumn() @CreateDateColumn()
createdAt: Date; createdAt: Date;

View File

@ -24,7 +24,11 @@ const closeGame = async (
if (!game) return; if (!game) return;
const gameProcess = processes.find((runningProcess) => { const gameProcess = processes.find((runningProcess) => {
if (process.platform === "linux") {
return runningProcess.name === game.executablePath?.split("/").at(-1);
} else {
return runningProcess.exe === game.executablePath; return runningProcess.exe === game.executablePath;
}
}); });
if (gameProcess) { if (gameProcess) {

View File

@ -1,6 +1,6 @@
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import type { StartGameDownloadPayload } from "@types"; import type { StartGameDownloadPayload } from "@types";
import { DownloadManager, HydraApi, logger } from "@main/services"; import { DownloadManager, HydraApi } from "@main/services";
import { Not } from "typeorm"; import { Not } from "typeorm";
import { steamGamesWorker } from "@main/workers"; import { steamGamesWorker } from "@main/workers";
@ -76,8 +76,14 @@ const startGameDownload = async (
}, },
}); });
createGame(updatedGame!).catch(() => {}); await DownloadManager.cancelDownload(updatedGame!.id);
await DownloadManager.startDownload(updatedGame!);
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
await downloadQueueRepository.insert({ game: { id: updatedGame!.id } });
await Promise.all([
createGame(updatedGame!).catch(() => {}),
HydraApi.post( HydraApi.post(
"/games/download", "/games/download",
{ {
@ -85,15 +91,8 @@ const startGameDownload = async (
shop: updatedGame!.shop, shop: updatedGame!.shop,
}, },
{ needsAuth: false } { needsAuth: false }
).catch((err) => { ).catch(() => {}),
logger.error("Failed to create game download", err); ]);
});
await DownloadManager.cancelDownload(updatedGame!.id);
await DownloadManager.startDownload(updatedGame!);
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
await downloadQueueRepository.insert({ game: { id: updatedGame!.id } });
}); });
}; };

View File

@ -1,6 +1,9 @@
import type { GameShop, UnlockedAchievement, UserAchievement } from "@types"; import type { GameShop, UnlockedAchievement, UserAchievement } from "@types";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { gameAchievementRepository } from "@main/repository"; import {
gameAchievementRepository,
userPreferencesRepository,
} from "@main/repository";
import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data"; import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data";
export const getUnlockedAchievements = async ( export const getUnlockedAchievements = async (
@ -12,10 +15,17 @@ export const getUnlockedAchievements = async (
where: { objectId, shop }, where: { objectId, shop },
}); });
const userPreferences = await userPreferencesRepository.findOne({
where: { id: 1 },
});
const showHiddenAchievementsDescription =
userPreferences?.showHiddenAchievementsDescription || false;
const achievementsData = await getGameAchievementData( const achievementsData = await getGameAchievementData(
objectId, objectId,
shop, shop,
useCachedData useCachedData ? cachedAchievements : null
); );
const unlockedAchievements = JSON.parse( const unlockedAchievements = JSON.parse(
@ -50,6 +60,10 @@ export const getUnlockedAchievements = async (
unlocked: false, unlocked: false,
unlockTime: null, unlockTime: null,
icongray: icongray, icongray: icongray,
description:
!achievementData.hidden || showHiddenAchievementsDescription
? achievementData.description
: undefined,
} as UserAchievement; } as UserAchievement;
}) })
.sort((a, b) => { .sort((a, b) => {

View File

@ -13,6 +13,8 @@ import { AddBackgroundImageUrl } from "./migrations/20241016100249_add_backgroun
import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix_to_game"; import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix_to_game";
import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column"; import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column";
import { AddDisableNsfwAlertColumn } from "./migrations/20241106053733_add_disable_nsfw_alert_column"; import { AddDisableNsfwAlertColumn } from "./migrations/20241106053733_add_disable_nsfw_alert_column";
import { AddHiddenAchievementDescriptionColumn } from "./migrations/20241216140633_add_hidden_achievement_description_column ";
export type HydraMigration = Knex.Migration & { name: string }; export type HydraMigration = Knex.Migration & { name: string };
class MigrationSource implements Knex.MigrationSource<HydraMigration> { class MigrationSource implements Knex.MigrationSource<HydraMigration> {
@ -30,6 +32,7 @@ class MigrationSource implements Knex.MigrationSource<HydraMigration> {
AddWinePrefixToGame, AddWinePrefixToGame,
AddStartMinimizedColumn, AddStartMinimizedColumn,
AddDisableNsfwAlertColumn, AddDisableNsfwAlertColumn,
AddHiddenAchievementDescriptionColumn,
]); ]);
} }
getMigrationName(migration: HydraMigration): string { getMigrationName(migration: HydraMigration): string {

View File

@ -0,0 +1,20 @@
import type { HydraMigration } from "@main/knex-client";
import type { Knex } from "knex";
export const AddHiddenAchievementDescriptionColumn: HydraMigration = {
name: "AddHiddenAchievementDescriptionColumn",
up: (knex: Knex) => {
return knex.schema.alterTable("user_preferences", (table) => {
return table
.boolean("showHiddenAchievementsDescription")
.notNullable()
.defaultTo(0);
});
},
down: async (knex: Knex) => {
return knex.schema.alterTable("user_preferences", (table) => {
return table.dropColumn("showHiddenAchievementsDescription");
});
},
};

View File

@ -1,40 +0,0 @@
import path from "node:path";
import fs from "node:fs";
import { getSteamGameClientIcon, logger } from "@main/services";
import { chunk } from "lodash-es";
import { seedsPath } from "@main/constants";
import type { SteamGame } from "@types";
const steamGamesPath = path.join(seedsPath, "steam-games.json");
const steamGames = JSON.parse(
fs.readFileSync(steamGamesPath, "utf-8")
) as SteamGame[];
const chunks = chunk(steamGames, 1500);
for (const chunk of chunks) {
await Promise.all(
chunk.map(async (steamGame) => {
if (steamGame.clientIcon) return;
const index = steamGames.findIndex((game) => game.id === steamGame.id);
try {
const clientIcon = await getSteamGameClientIcon(String(steamGame.id));
steamGames[index].clientIcon = clientIcon;
logger.log("info", `Set ${steamGame.name} client icon`);
} catch (err) {
steamGames[index].clientIcon = null;
logger.log("info", `Could not set icon for ${steamGame.name}`);
}
})
);
fs.writeFileSync(steamGamesPath, JSON.stringify(steamGames));
logger.log("info", "Updated steam games");
}

View File

@ -236,6 +236,7 @@ export class AchievementWatcherManager {
}; };
public static preSearchAchievements = async () => { public static preSearchAchievements = async () => {
try {
const newAchievementsCount = const newAchievementsCount =
process.platform === "win32" process.platform === "win32"
? await this.preSearchAchievementsWindows() ? await this.preSearchAchievementsWindows()
@ -255,6 +256,9 @@ export class AchievementWatcherManager {
totalNewGamesWithAchievements totalNewGamesWithAchievements
); );
} }
} catch (err) {
achievementsLogger.error("Error on preSearchAchievements", err);
}
this.hasFinishedMergingWithRemote = true; this.hasFinishedMergingWithRemote = true;
}; };

View File

@ -6,21 +6,16 @@ import { HydraApi } from "../hydra-api";
import type { AchievementData, GameShop } from "@types"; import type { AchievementData, GameShop } from "@types";
import { UserNotLoggedInError } from "@shared"; import { UserNotLoggedInError } from "@shared";
import { logger } from "../logger"; import { logger } from "../logger";
import { GameAchievement } from "@main/entity";
export const getGameAchievementData = async ( export const getGameAchievementData = async (
objectId: string, objectId: string,
shop: GameShop, shop: GameShop,
useCachedData: boolean cachedAchievements: GameAchievement | null
) => { ) => {
if (useCachedData) {
const cachedAchievements = await gameAchievementRepository.findOne({
where: { objectId, shop },
});
if (cachedAchievements && cachedAchievements.achievements) { if (cachedAchievements && cachedAchievements.achievements) {
return JSON.parse(cachedAchievements.achievements) as AchievementData[]; return JSON.parse(cachedAchievements.achievements) as AchievementData[];
} }
}
const userPreferences = await userPreferencesRepository.findOne({ const userPreferences = await userPreferencesRepository.findOne({
where: { id: 1 }, where: { id: 1 },

View File

@ -9,6 +9,7 @@ export const parseAchievementFile = (
): UnlockedAchievement[] => { ): UnlockedAchievement[] => {
if (!existsSync(filePath)) return []; if (!existsSync(filePath)) return [];
try {
if (type == Cracker.codex) { if (type == Cracker.codex) {
const parsed = iniParse(filePath); const parsed = iniParse(filePath);
return processDefault(parsed); return processDefault(parsed);
@ -78,10 +79,13 @@ export const parseAchievementFile = (
`Unprocessed ${type} achievements found on ${filePath}` `Unprocessed ${type} achievements found on ${filePath}`
); );
return []; return [];
} catch (err) {
achievementsLogger.error(`Error parsing ${type} - ${filePath}`, err);
return [];
}
}; };
const iniParse = (filePath: string) => { const iniParse = (filePath: string) => {
try {
const fileContent = readFileSync(filePath, "utf-8"); const fileContent = readFileSync(filePath, "utf-8");
const lines = const lines =
@ -105,23 +109,13 @@ const iniParse = (filePath: string) => {
} }
return object; return object;
} catch (err) {
achievementsLogger.error(`Error parsing ${filePath}`, err);
return {};
}
}; };
const jsonParse = (filePath: string) => { const jsonParse = (filePath: string) => {
try {
return JSON.parse(readFileSync(filePath, "utf-8")); return JSON.parse(readFileSync(filePath, "utf-8"));
} catch (err) {
achievementsLogger.error(`Error parsing ${filePath}`, err);
return {};
}
}; };
const processRazor1911 = (filePath: string): UnlockedAchievement[] => { const processRazor1911 = (filePath: string): UnlockedAchievement[] => {
try {
const fileContent = readFileSync(filePath, "utf-8"); const fileContent = readFileSync(filePath, "utf-8");
const lines = const lines =
@ -143,10 +137,6 @@ const processRazor1911 = (filePath: string): UnlockedAchievement[] => {
} }
return achievements; return achievements;
} catch (err) {
achievementsLogger.error(`Error processing ${filePath}`, err);
return [];
}
}; };
const processOnlineFix = (unlockedAchievements: any): UnlockedAchievement[] => { const processOnlineFix = (unlockedAchievements: any): UnlockedAchievement[] => {

View File

@ -35,4 +35,5 @@ export interface LibtorrentPayload {
export interface ProcessPayload { export interface ProcessPayload {
exe: string; exe: string;
pid: number; pid: number;
name: string;
} }

View File

@ -1,7 +1,6 @@
export * from "./logger"; export * from "./logger";
export * from "./steam"; export * from "./steam";
export * from "./steam-250"; export * from "./steam-250";
export * from "./steam-grid";
export * from "./window-manager"; export * from "./window-manager";
export * from "./download"; export * from "./download";
export * from "./process-watcher"; export * from "./process-watcher";

View File

@ -1,38 +1,172 @@
import { IsNull, Not } from "typeorm";
import { gameRepository } from "@main/repository"; import { gameRepository } from "@main/repository";
import { WindowManager } from "./window-manager"; import { WindowManager } from "./window-manager";
import { createGame, updateGamePlaytime } from "./library-sync"; import { createGame, updateGamePlaytime } from "./library-sync";
import type { GameRunning } from "@types"; import type { GameRunning } from "@types";
import { PythonInstance } from "./download"; import { PythonInstance } from "./download";
import { Game } from "@main/entity"; import { Game } from "@main/entity";
import axios from "axios";
import { exec } from "child_process";
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 ""}'`,
findWineExecutables: `lsof -c wine 2>/dev/null | grep '\\.exe$' | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`,
};
export const gamesPlaytime = new Map< export const gamesPlaytime = new Map<
number, number,
{ lastTick: number; firstTick: number; lastSyncTick: number } { lastTick: number; firstTick: number; lastSyncTick: number }
>(); >();
interface ExecutableInfo {
name: string;
os: string;
}
interface GameExecutables {
[key: string]: ExecutableInfo[];
}
const TICKS_TO_UPDATE_API = 120; const TICKS_TO_UPDATE_API = 120;
let currentTick = 1; let currentTick = 1;
const gameExecutables = (
await axios
.get(
import.meta.env.MAIN_VITE_EXTERNAL_RESOURCES_URL +
"/game-executables.json"
)
.catch(() => {
return { data: {} };
})
).data as GameExecutables;
const findGamePathByProcess = (
processMap: Map<string, Set<string>>,
gameId: string
) => {
const executables = gameExecutables[gameId].filter((info) => {
if (process.platform === "linux" && info.os === "linux") return true;
return info.os === "win32";
});
for (const executable of executables) {
const exe = executable.name.slice(executable.name.lastIndexOf("/") + 1);
if (!exe) continue;
const pathSet = processMap.get(exe);
if (pathSet) {
const executableName =
process.platform === "win32"
? executable.name.replace(/\//g, "\\")
: executable.name;
pathSet.forEach((path) => {
if (path.toLowerCase().endsWith(executableName)) {
gameRepository.update(
{ objectID: gameId, shop: "steam" },
{ executablePath: path }
);
if (process.platform === "linux") {
exec(commands.findWineDir, (err, out) => {
if (err) return;
gameRepository.update(
{ objectID: gameId, shop: "steam" },
{
winePrefixPath: out.trim().replace("/drive_c/windows", ""),
}
);
});
}
}
});
}
}
};
const getSystemProcessMap = async () => {
const processes = await PythonInstance.getProcessList();
const map = new Map<string, Set<string>>();
processes.forEach((process) => {
const key = process.name.toLowerCase();
const value = process.exe;
if (!key || !value) return;
const currentSet = map.get(key) ?? new Set();
map.set(key, currentSet.add(value));
});
if (process.platform === "linux") {
await new Promise((res) => {
exec(commands.findWineExecutables, (err, out) => {
if (err) {
res(null);
return;
}
const pathSet = new Set(
out
.trim()
.split("\n")
.map((path) => path.trim())
);
pathSet.forEach((path) => {
if (path.startsWith("/usr")) return;
const key = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
if (!key || !path) return;
const currentSet = map.get(key) ?? new Set();
map.set(key, currentSet.add(path));
});
res(null);
});
});
}
return map;
};
export const watchProcesses = async () => { export const watchProcesses = async () => {
const games = await gameRepository.find({ const games = await gameRepository.find({
where: { where: {
executablePath: Not(IsNull()),
isDeleted: false, isDeleted: false,
}, },
}); });
if (games.length === 0) return; if (!games.length) return;
const processes = await PythonInstance.getProcessList();
const processSet = new Set(processes.map((process) => process.exe)); const processMap = await getSystemProcessMap();
for (const game of games) { for (const game of games) {
const executablePath = game.executablePath!; const executablePath = game.executablePath;
const gameProcess = processSet.has(executablePath); if (!executablePath) {
if (gameExecutables[game.objectID]) {
findGamePathByProcess(processMap, game.objectID);
}
continue;
}
if (gameProcess) { const executable = executablePath
.slice(
executablePath.lastIndexOf(process.platform === "win32" ? "\\" : "/") +
1
)
.toLowerCase();
const hasProcess = processMap.get(executable)?.has(executablePath);
if (hasProcess) {
if (gamesPlaytime.has(game.id)) { if (gamesPlaytime.has(game.id)) {
onTickGame(game); onTickGame(game);
} else { } else {

View File

@ -1,69 +0,0 @@
import type { GameShop } from "@types";
import axios from "axios";
export interface SteamGridResponse {
success: boolean;
data: {
id: number;
};
}
export interface SteamGridGameResponse {
data: {
platforms: {
steam: {
metadata: {
clienticon: string;
};
};
};
};
}
export const getSteamGridData = async (
objectId: string,
path: string,
shop: GameShop,
params: Record<string, string> = {}
): Promise<SteamGridResponse> => {
const searchParams = new URLSearchParams(params);
if (!import.meta.env.MAIN_VITE_STEAMGRIDDB_API_KEY) {
throw new Error("MAIN_VITE_STEAMGRIDDB_API_KEY is not set");
}
const response = await axios.get(
`https://www.steamgriddb.com/api/v2/${path}/${shop}/${objectId}?${searchParams.toString()}`,
{
headers: {
Authorization: `Bearer ${import.meta.env.MAIN_VITE_STEAMGRIDDB_API_KEY}`,
},
}
);
return response.data;
};
export const getSteamGridGameById = async (
id: number
): Promise<SteamGridGameResponse> => {
const response = await axios.get(
`https://www.steamgriddb.com/api/public/game/${id}`,
{
headers: {
Referer: "https://www.steamgriddb.com/",
},
}
);
return response.data;
};
export const getSteamGameClientIcon = async (objectId: string) => {
const {
data: { id: steamGridGameId },
} = await getSteamGridData(objectId, "games", "steam");
const steamGridGame = await getSteamGridGameById(steamGridGameId);
return steamGridGame.data.platforms.steam.metadata.clienticon;
};

View File

@ -195,7 +195,7 @@ export class WindowManager {
this.mainWindow?.focus(); this.mainWindow?.focus();
} }
public static createSystemTray(language: string) { public static async createSystemTray(language: string) {
let tray: Tray; let tray: Tray;
if (process.platform === "darwin") { if (process.platform === "darwin") {
@ -263,6 +263,7 @@ export class WindowManager {
}, },
]); ]);
tray.setContextMenu(contextMenu);
return contextMenu; return contextMenu;
}; };
@ -274,6 +275,8 @@ export class WindowManager {
tray.setToolTip("Hydra"); tray.setToolTip("Hydra");
if (process.platform !== "darwin") { if (process.platform !== "darwin") {
await updateSystemTray();
tray.addListener("click", () => { tray.addListener("click", () => {
if (this.mainWindow) { if (this.mainWindow) {
if ( if (

View File

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

View File

@ -11,10 +11,10 @@ import type {
GameRunning, GameRunning,
FriendRequestAction, FriendRequestAction,
UpdateProfileRequest, UpdateProfileRequest,
GameAchievement,
} from "@types"; } from "@types";
import type { CatalogueCategory } from "@shared"; import type { CatalogueCategory } from "@shared";
import type { AxiosProgressEvent } from "axios"; import type { AxiosProgressEvent } from "axios";
import { GameAchievement } from "@main/entity";
contextBridge.exposeInMainWorld("electron", { contextBridge.exposeInMainWorld("electron", {
/* Torrenting */ /* Torrenting */

View File

@ -126,7 +126,7 @@ export function App() {
const $script = document.createElement("script"); const $script = document.createElement("script");
$script.id = "external-resources"; $script.id = "external-resources";
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}?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, syncFriendRequests, updateUserDetails, dispatch]);

View File

@ -1,15 +1,17 @@
export function addCookieInterceptor() { export function addCookieInterceptor(isStaging: boolean) {
const cookieKey = isStaging ? "cookies-staging" : "cookies";
Object.defineProperty(document, "cookie", { Object.defineProperty(document, "cookie", {
enumerable: true, enumerable: true,
configurable: true, configurable: true,
get() { get() {
return localStorage.getItem("cookies") || ""; return localStorage.getItem(cookieKey) || "";
}, },
set(cookieString) { set(cookieString) {
try { try {
const [cookieName, cookieValue] = cookieString.split(";")[0].split("="); const [cookieName, cookieValue] = cookieString.split(";")[0].split("=");
const currentCookies = localStorage.getItem("cookies") || ""; const currentCookies = localStorage.getItem(cookieKey) || "";
const cookiesObject = parseCookieStringsToObjects(currentCookies); const cookiesObject = parseCookieStringsToObjects(currentCookies);
cookiesObject[cookieName] = cookieValue; cookiesObject[cookieName] = cookieValue;
@ -20,7 +22,7 @@ export function addCookieInterceptor() {
}) })
.join("; "); .join("; ");
localStorage.setItem("cookies", newString); localStorage.setItem(cookieKey, newString);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }

View File

@ -38,7 +38,8 @@ const Achievements = React.lazy(
console.log = logger.log; console.log = logger.log;
addCookieInterceptor(); const isStaging = await window.electron.isStaging();
addCookieInterceptor(isStaging);
i18n i18n
.use(LanguageDetector) .use(LanguageDetector)

View File

@ -19,6 +19,7 @@ export function SettingsBehavior() {
runAtStartup: false, runAtStartup: false,
startMinimized: false, startMinimized: false,
disableNsfwAlert: false, disableNsfwAlert: false,
showHiddenAchievementsDescription: false,
}); });
const { t } = useTranslation("settings"); const { t } = useTranslation("settings");
@ -30,6 +31,8 @@ export function SettingsBehavior() {
runAtStartup: userPreferences.runAtStartup, runAtStartup: userPreferences.runAtStartup,
startMinimized: userPreferences.startMinimized, startMinimized: userPreferences.startMinimized,
disableNsfwAlert: userPreferences.disableNsfwAlert, disableNsfwAlert: userPreferences.disableNsfwAlert,
showHiddenAchievementsDescription:
userPreferences.showHiddenAchievementsDescription,
}); });
} }
}, [userPreferences]); }, [userPreferences]);
@ -96,6 +99,17 @@ export function SettingsBehavior() {
handleChange({ disableNsfwAlert: !form.disableNsfwAlert }) handleChange({ disableNsfwAlert: !form.disableNsfwAlert })
} }
/> />
<CheckboxField
label={t("show_hidden_achievement_description")}
checked={form.showHiddenAchievementsDescription}
onChange={() =>
handleChange({
showHiddenAchievementsDescription:
!form.showHiddenAchievementsDescription,
})
}
/>
</> </>
); );
} }

View File

@ -162,6 +162,7 @@ export interface UserPreferences {
runAtStartup: boolean; runAtStartup: boolean;
startMinimized: boolean; startMinimized: boolean;
disableNsfwAlert: boolean; disableNsfwAlert: boolean;
showHiddenAchievementsDescription: boolean;
} }
export interface Steam250Game { export interface Steam250Game {

View File

@ -60,7 +60,7 @@ class Handler(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
return return
process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'username'])] process_list = [proc.info for proc in psutil.process_iter(['exe', 'pid', 'name'])]
self.send_response(200) self.send_response(200)
self.send_header("Content-type", "application/json") self.send_header("Content-type", "application/json")

621
yarn.lock

File diff suppressed because it is too large Load Diff