mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-29 05:41:03 +00:00
Compare commits
24 Commits
fix/LBX-45
...
feat/LBX-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf70cd2b4 | ||
|
|
2e2a95c314 | ||
|
|
53bd31dec0 | ||
|
|
9f00df3fb0 | ||
|
|
ce66710dac | ||
|
|
884aabbfc0 | ||
|
|
d4bd8f7bf1 | ||
|
|
ad812c393d | ||
|
|
57c2e74013 | ||
|
|
17528e74bb | ||
|
|
355993b954 | ||
|
|
ed79b8faeb | ||
|
|
4aaaecbee5 | ||
|
|
802b4bd26b | ||
|
|
c9afd65536 | ||
|
|
d448a699da | ||
|
|
eea7148108 | ||
|
|
fb1380356e | ||
|
|
3f82b13b1f | ||
|
|
baa2c8471a | ||
|
|
63bb5ca511 | ||
|
|
128f864ca7 | ||
|
|
a2b993bb9b | ||
|
|
cafa536f79 |
@@ -88,12 +88,12 @@
|
|||||||
"sound-play": "^1.1.0",
|
"sound-play": "^1.1.0",
|
||||||
"steam-shortcut-editor": "https://github.com/hydralauncher/steam-shortcut-editor",
|
"steam-shortcut-editor": "https://github.com/hydralauncher/steam-shortcut-editor",
|
||||||
"sudo-prompt": "^9.2.1",
|
"sudo-prompt": "^9.2.1",
|
||||||
"tar": "^7.5.2",
|
"tar": "^7.5.4",
|
||||||
"tough-cookie": "^5.1.1",
|
"tough-cookie": "^5.1.1",
|
||||||
"user-agents": "^1.1.387",
|
"user-agents": "^1.1.387",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
"winreg": "^1.2.5",
|
"winreg": "^1.2.5",
|
||||||
"workwonders-sdk": "0.0.10",
|
"workwonders-sdk": "0.1.1",
|
||||||
"ws": "^8.18.1",
|
"ws": "^8.18.1",
|
||||||
"yaml": "^2.6.1",
|
"yaml": "^2.6.1",
|
||||||
"yup": "^1.5.0"
|
"yup": "^1.5.0"
|
||||||
|
|||||||
@@ -185,6 +185,7 @@
|
|||||||
"repacks_modal_description": "Choose the repack you want to download",
|
"repacks_modal_description": "Choose the repack you want to download",
|
||||||
"select_folder_hint": "To change the default folder, go to the <0>Settings</0>",
|
"select_folder_hint": "To change the default folder, go to the <0>Settings</0>",
|
||||||
"download_now": "Download now",
|
"download_now": "Download now",
|
||||||
|
"add_to_queue": "Add to queue",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"no_shop_details": "Could not retrieve shop details.",
|
"no_shop_details": "Could not retrieve shop details.",
|
||||||
"download_options": "Download options",
|
"download_options": "Download options",
|
||||||
@@ -447,7 +448,9 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"network": "NETWORK",
|
"network": "NETWORK",
|
||||||
"peak": "PEAK"
|
"peak": "PEAK",
|
||||||
|
"move_up": "Move up",
|
||||||
|
"move_down": "Move down"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Downloads path",
|
"downloads_path": "Downloads path",
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
"filter": "Поиск репаков",
|
"filter": "Поиск репаков",
|
||||||
"requirements": "Системные требования",
|
"requirements": "Системные требования",
|
||||||
"minimum": "Минимальные",
|
"minimum": "Минимальные",
|
||||||
"recommended": "Рекомендуемые",
|
"recommended": "Рекомендованные",
|
||||||
"paused": "Приостановлено",
|
"paused": "Приостановлено",
|
||||||
"release_date": "Выпущено {{date}}",
|
"release_date": "Выпущено {{date}}",
|
||||||
"publisher": "Издатель {{publisher}}",
|
"publisher": "Издатель {{publisher}}",
|
||||||
@@ -189,7 +189,6 @@
|
|||||||
"downloader_not_configured": "Доступен, но не настроен",
|
"downloader_not_configured": "Доступен, но не настроен",
|
||||||
"downloader_offline": "Ссылка недоступна",
|
"downloader_offline": "Ссылка недоступна",
|
||||||
"downloader_not_available": "Недоступно",
|
"downloader_not_available": "Недоступно",
|
||||||
"recommended": "Рекомендуется",
|
|
||||||
"go_to_settings": "Перейти в настройки",
|
"go_to_settings": "Перейти в настройки",
|
||||||
"select_executable": "Выбрать",
|
"select_executable": "Выбрать",
|
||||||
"no_executable_selected": "Файл не выбран",
|
"no_executable_selected": "Файл не выбран",
|
||||||
@@ -243,11 +242,11 @@
|
|||||||
"show_more": "Показать больше",
|
"show_more": "Показать больше",
|
||||||
"show_less": "Показать меньше",
|
"show_less": "Показать меньше",
|
||||||
"reviews": "Отзывы",
|
"reviews": "Отзывы",
|
||||||
|
"review_played_for": "Играли",
|
||||||
"leave_a_review": "Оставить отзыв",
|
"leave_a_review": "Оставить отзыв",
|
||||||
"write_review_placeholder": "Поделитесь своими мыслями об этой игре...",
|
"write_review_placeholder": "Поделитесь своими мыслями об этой игре...",
|
||||||
"sort_newest": "Сначала новые",
|
"sort_newest": "Сначала новые",
|
||||||
"no_reviews_yet": "Пока нет отзывов",
|
"no_reviews_yet": "Пока нет отзывов",
|
||||||
"review_played_for": "Играли",
|
|
||||||
"be_first_to_review": "Станьте первым, кто поделится своими мыслями об этой игре!",
|
"be_first_to_review": "Станьте первым, кто поделится своими мыслями об этой игре!",
|
||||||
"sort_oldest": "Сначала старые",
|
"sort_oldest": "Сначала старые",
|
||||||
"sort_highest_score": "Высший балл",
|
"sort_highest_score": "Высший балл",
|
||||||
@@ -372,8 +371,6 @@
|
|||||||
"audio": "Аудио",
|
"audio": "Аудио",
|
||||||
"filter_by_source": "Фильтр по источнику",
|
"filter_by_source": "Фильтр по источнику",
|
||||||
"no_repacks_found": "Источники для этой игры не найдены",
|
"no_repacks_found": "Источники для этой игры не найдены",
|
||||||
"show": "Показать",
|
|
||||||
"hide": "Скрыть",
|
|
||||||
"delete_review": "Удалить отзыв",
|
"delete_review": "Удалить отзыв",
|
||||||
"remove_review": "Удалить отзыв",
|
"remove_review": "Удалить отзыв",
|
||||||
"delete_review_modal_title": "Вы уверены, что хотите удалить свой отзыв?",
|
"delete_review_modal_title": "Вы уверены, что хотите удалить свой отзыв?",
|
||||||
@@ -385,7 +382,9 @@
|
|||||||
"show_translation": "Показать перевод",
|
"show_translation": "Показать перевод",
|
||||||
"show_original_translated_from": "Показать оригинал (переведено с {{language}})",
|
"show_original_translated_from": "Показать оригинал (переведено с {{language}})",
|
||||||
"hide_original": "Скрыть оригинал",
|
"hide_original": "Скрыть оригинал",
|
||||||
"review_from_blocked_user": "Отзыв от заблокированного пользователя"
|
"review_from_blocked_user": "Отзыв от заблокированного пользователя",
|
||||||
|
"show": "Показать",
|
||||||
|
"hide": "Скрыть"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Активировать Hydra",
|
"title": "Активировать Hydra",
|
||||||
@@ -722,7 +721,7 @@
|
|||||||
"report_reason_spam": "Спам",
|
"report_reason_spam": "Спам",
|
||||||
"report_reason_other": "Другое",
|
"report_reason_other": "Другое",
|
||||||
"profile_reported": "Жалоба на профиль отправлена",
|
"profile_reported": "Жалоба на профиль отправлена",
|
||||||
"your_friend_code": "Код вашего друга:",
|
"your_friend_code": "Ваш код друга:",
|
||||||
"copy_friend_code": "Копировать код друга",
|
"copy_friend_code": "Копировать код друга",
|
||||||
"copied": "Скопировано!",
|
"copied": "Скопировано!",
|
||||||
"upload_banner": "Загрузить баннер",
|
"upload_banner": "Загрузить баннер",
|
||||||
@@ -751,10 +750,30 @@
|
|||||||
"karma": "Карма",
|
"karma": "Карма",
|
||||||
"karma_count": "карма",
|
"karma_count": "карма",
|
||||||
"user_reviews": "Отзывы",
|
"user_reviews": "Отзывы",
|
||||||
|
"delete_review": "Удалить отзыв",
|
||||||
"loading_reviews": "Загрузка отзывов...",
|
"loading_reviews": "Загрузка отзывов...",
|
||||||
"wrapped_2025": "Wrapped 2025",
|
"wrapped_2025": "Wrapped 2025"
|
||||||
"no_reviews": "Пока нет отзывов",
|
},
|
||||||
"delete_review": "Удалить отзыв"
|
"library": {
|
||||||
|
"library": "Библиотека",
|
||||||
|
"play": "Играть",
|
||||||
|
"download": "Скачать",
|
||||||
|
"downloading": "Скачивание",
|
||||||
|
"game": "игра",
|
||||||
|
"games": "игры",
|
||||||
|
"grid_view": "Вид сетки",
|
||||||
|
"compact_view": "Компактный вид",
|
||||||
|
"large_view": "Большой вид",
|
||||||
|
"no_games_title": "Ваша библиотека пуста",
|
||||||
|
"no_games_description": "Добавьте игры из каталога или скачайте их, чтобы начать",
|
||||||
|
"amount_hours": "{{amount}} часов",
|
||||||
|
"amount_minutes": "{{amount}} минут",
|
||||||
|
"amount_hours_short": "{{amount}}ч",
|
||||||
|
"amount_minutes_short": "{{amount}}м",
|
||||||
|
"manual_playtime_tooltip": "Время игры было обновлено вручную",
|
||||||
|
"all_games": "Все игры",
|
||||||
|
"recently_played": "Недавно сыгранные",
|
||||||
|
"favorites": "Избранное"
|
||||||
},
|
},
|
||||||
"achievement": {
|
"achievement": {
|
||||||
"achievement_unlocked": "Достижение разблокировано",
|
"achievement_unlocked": "Достижение разблокировано",
|
||||||
@@ -785,27 +804,6 @@
|
|||||||
"learn_more": "Подробнее",
|
"learn_more": "Подробнее",
|
||||||
"debrid_description": "Скачивайте в 4 раза быстрее с Nimbus"
|
"debrid_description": "Скачивайте в 4 раза быстрее с Nimbus"
|
||||||
},
|
},
|
||||||
"library": {
|
|
||||||
"library": "Библиотека",
|
|
||||||
"play": "Играть",
|
|
||||||
"download": "Скачать",
|
|
||||||
"downloading": "Скачивание",
|
|
||||||
"game": "игра",
|
|
||||||
"games": "игры",
|
|
||||||
"grid_view": "Вид сетки",
|
|
||||||
"compact_view": "Компактный вид",
|
|
||||||
"large_view": "Большой вид",
|
|
||||||
"no_games_title": "Ваша библиотека пуста",
|
|
||||||
"no_games_description": "Добавьте игры из каталога или скачайте их, чтобы начать",
|
|
||||||
"amount_hours": "{{amount}} часов",
|
|
||||||
"amount_minutes": "{{amount}} минут",
|
|
||||||
"amount_hours_short": "{{amount}}ч",
|
|
||||||
"amount_minutes_short": "{{amount}}м",
|
|
||||||
"manual_playtime_tooltip": "Время игры было обновлено вручную",
|
|
||||||
"all_games": "Все игры",
|
|
||||||
"recently_played": "Недавно сыгранные",
|
|
||||||
"favorites": "Избранное"
|
|
||||||
},
|
|
||||||
"notifications_page": {
|
"notifications_page": {
|
||||||
"title": "Уведомления",
|
"title": "Уведомления",
|
||||||
"mark_all_as_read": "Отметить все как прочитанные",
|
"mark_all_as_read": "Отметить все как прочитанные",
|
||||||
|
|||||||
844
src/locales/sl/translation.json
Normal file
844
src/locales/sl/translation.json
Normal file
@@ -0,0 +1,844 @@
|
|||||||
|
{
|
||||||
|
"language_name": "Slovenščina",
|
||||||
|
"app": {
|
||||||
|
"successfully_signed_in": "Uspešno ste se prijavili"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"surprise_me": "Preseneti me",
|
||||||
|
"no_results": "Ni najdenih rezultatov",
|
||||||
|
"start_typing": "Začnite tipkati za iskanje...",
|
||||||
|
"hot": "Trenutno vroče",
|
||||||
|
"weekly": "📅 Najboljše igre tedna",
|
||||||
|
"achievements": "🏆 Igre za premagati"
|
||||||
|
},
|
||||||
|
"sidebar": {
|
||||||
|
"catalogue": "Katalog",
|
||||||
|
"library": "Knjižnica",
|
||||||
|
"downloads": "Prenosi",
|
||||||
|
"settings": "Nastavitve",
|
||||||
|
"my_library": "Moja knjižnica",
|
||||||
|
"downloading_metadata": "{{title}} (Prenos metapodatkov…)",
|
||||||
|
"paused": "{{title}} (V premoru)",
|
||||||
|
"downloading": "{{title}} ({{percentage}} - Prenos…)",
|
||||||
|
"filter": "Filtriraj knjižnico",
|
||||||
|
"home": "Domov",
|
||||||
|
"queued": "{{title}} (V čakalni vrsti)",
|
||||||
|
"game_has_no_executable": "Igra nima izbrane izvršljive datoteke",
|
||||||
|
"sign_in": "Prijavite se",
|
||||||
|
"friends": "Prijatelji",
|
||||||
|
"notifications": "Obvestila",
|
||||||
|
"need_help": "Potrebujete pomoč?",
|
||||||
|
"favorites": "Priljubljene",
|
||||||
|
"playable_button_title": "Pokaži le igre, ki jih lahko igrate zdaj",
|
||||||
|
"add_custom_game_tooltip": "Dodaj igro po meri",
|
||||||
|
"show_playable_only_tooltip": "Pokaži samo igrljive",
|
||||||
|
"custom_game_modal": "Dodaj igro po meri",
|
||||||
|
"custom_game_modal_description": "Dodajte igro po meri v vašo knjižnico z izbiro izvršljive datoteke",
|
||||||
|
"custom_game_modal_executable_path": "Pot do izvršljive datoteke",
|
||||||
|
"custom_game_modal_select_executable": "Izberite izvršljivo datoteko",
|
||||||
|
"custom_game_modal_title": "Naslov",
|
||||||
|
"custom_game_modal_enter_title": "Vnesite naslov",
|
||||||
|
"custom_game_modal_browse": "Brskaj",
|
||||||
|
"custom_game_modal_cancel": "Prekliči",
|
||||||
|
"custom_game_modal_add": "Dodaj igro",
|
||||||
|
"custom_game_modal_adding": "Dodajanje igre...",
|
||||||
|
"custom_game_modal_success": "Igra po meri je bila uspešno dodana",
|
||||||
|
"custom_game_modal_failed": "Dodajanje igre po meri ni uspelo",
|
||||||
|
"custom_game_modal_executable": "Izvršljiva datoteka",
|
||||||
|
"edit_game_modal": "Prilagodi sredstva",
|
||||||
|
"edit_game_modal_description": "Prilagodite sredstva in podrobnosti igre",
|
||||||
|
"edit_game_modal_title": "Naslov",
|
||||||
|
"edit_game_modal_enter_title": "Vnesite naslov",
|
||||||
|
"edit_game_modal_image": "Slika",
|
||||||
|
"edit_game_modal_select_image": "Izberite sliko",
|
||||||
|
"edit_game_modal_browse": "Brskaj",
|
||||||
|
"edit_game_modal_image_preview": "Predogled slike",
|
||||||
|
"edit_game_modal_icon": "Ikona",
|
||||||
|
"edit_game_modal_select_icon": "Izberite ikono",
|
||||||
|
"edit_game_modal_icon_preview": "Predogled ikone",
|
||||||
|
"edit_game_modal_logo": "Logotip",
|
||||||
|
"edit_game_modal_select_logo": "Izberite logotip",
|
||||||
|
"edit_game_modal_logo_preview": "Predogled logotipa",
|
||||||
|
"edit_game_modal_hero": "Hero knjižnice",
|
||||||
|
"edit_game_modal_select_hero": "Izberite sliko hero knjižnice",
|
||||||
|
"edit_game_modal_hero_preview": "Predogled hero slike knjižnice",
|
||||||
|
"edit_game_modal_cancel": "Prekliči",
|
||||||
|
"edit_game_modal_update": "Posodobi",
|
||||||
|
"edit_game_modal_updating": "Posodabljanje...",
|
||||||
|
"edit_game_modal_fill_required": "Prosimo, izpolnite vsa obvezna polja",
|
||||||
|
"edit_game_modal_success": "Sredstva so bila uspešno posodobljena",
|
||||||
|
"edit_game_modal_failed": "Posodabljanje sredstev ni uspelo",
|
||||||
|
"edit_game_modal_image_filter": "Slika",
|
||||||
|
"edit_game_modal_icon_resolution": "Priporočena resolucija: 256x256px",
|
||||||
|
"edit_game_modal_logo_resolution": "Priporočena resolucija: 640x360px",
|
||||||
|
"edit_game_modal_hero_resolution": "Priporočena resolucija: 1920x620px",
|
||||||
|
"edit_game_modal_assets": "Sredstva",
|
||||||
|
"edit_game_modal_drop_icon_image_here": "Spustite ikono tukaj",
|
||||||
|
"edit_game_modal_drop_logo_image_here": "Spustite logotip tukaj",
|
||||||
|
"edit_game_modal_drop_hero_image_here": "Spustite hero sliko tukaj",
|
||||||
|
"edit_game_modal_drop_to_replace_icon": "Spustite za zamenjavo ikone",
|
||||||
|
"edit_game_modal_drop_to_replace_logo": "Spustite za zamenjavo logotipa",
|
||||||
|
"edit_game_modal_drop_to_replace_hero": "Spustite za zamenjavo hero slike",
|
||||||
|
"install_decky_plugin": "Namesti Decky vtičnik",
|
||||||
|
"update_decky_plugin": "Posodobi Decky vtičnik",
|
||||||
|
"decky_plugin_installed_version": "Decky vtičnik (v{{version}})",
|
||||||
|
"install_decky_plugin_title": "Namesti Hydra Decky vtičnik",
|
||||||
|
"install_decky_plugin_message": "To bo preneslo in namestilo Hydra vtičnik za Decky Loader. To lahko zahteva povišane pravice. Nadaljujem?",
|
||||||
|
"update_decky_plugin_title": "Posodobi Hydra Decky vtičnik",
|
||||||
|
"update_decky_plugin_message": "Na voljo je nova različica Hydra Decky vtičnika. Ali želite posodobiti zdaj?",
|
||||||
|
"decky_plugin_installed": "Decky vtičnik v{{version}} je bil uspešno nameščen",
|
||||||
|
"decky_plugin_installation_failed": "Namestitev Decky vtičnika ni uspela: {{error}}",
|
||||||
|
"decky_plugin_installation_error": "Napaka pri nameščanju Decky vtičnika: {{error}}",
|
||||||
|
"confirm": "Potrdi",
|
||||||
|
"cancel": "Prekliči"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"search": "Išči igre",
|
||||||
|
"search_library": "Išči v knjižnici",
|
||||||
|
"recent_searches": "Nedavna iskanja",
|
||||||
|
"suggestions": "Predlogi",
|
||||||
|
"clear_history": "Počisti",
|
||||||
|
"remove_from_history": "Odstrani iz zgodovine",
|
||||||
|
"loading": "Nalaganje...",
|
||||||
|
"no_results": "Ni rezultatov",
|
||||||
|
"home": "Domov",
|
||||||
|
"catalogue": "Katalog",
|
||||||
|
"library": "Knjižnica",
|
||||||
|
"downloads": "Prenosi",
|
||||||
|
"search_results": "Rezultati iskanja",
|
||||||
|
"settings": "Nastavitve",
|
||||||
|
"version_available_install": "Različica {{version}} je na voljo. Kliknite tukaj za ponovni zagon in namestitev.",
|
||||||
|
"version_available_download": "Različica {{version}} je na voljo. Kliknite tukaj za prenos."
|
||||||
|
},
|
||||||
|
"bottom_panel": {
|
||||||
|
"no_downloads_in_progress": "Ni prenosa v teku",
|
||||||
|
"downloading_metadata": "Prenos metapodatkov {{title}}…",
|
||||||
|
"downloading": "Prenos {{title}}… ({{percentage}} končano) - Čas {{eta}} - {{speed}}",
|
||||||
|
"calculating_eta": "Prenos {{title}}… ({{percentage}} končano) - Izračun preostalega časa…",
|
||||||
|
"checking_files": "Preverjanje datotek {{title}}… ({{percentage}} končano)",
|
||||||
|
"extracting": "Razpakiranje {{title}}… ({{percentage}} končano)",
|
||||||
|
"installing_common_redist": "{{log}}…",
|
||||||
|
"installation_complete": "Namestitev zaključena",
|
||||||
|
"installation_complete_message": "Skupni redistributables so bili uspešno nameščeni"
|
||||||
|
},
|
||||||
|
"catalogue": {
|
||||||
|
"search": "Filtriraj…",
|
||||||
|
"developers": "Razvijalci",
|
||||||
|
"genres": "Žanri",
|
||||||
|
"tags": "Oznake",
|
||||||
|
"publishers": "Izdajatelji",
|
||||||
|
"download_sources": "Viri prenosa",
|
||||||
|
"result_count": "{{resultCount}} rezultatov",
|
||||||
|
"filter_count": "{{filterCount}} na voljo",
|
||||||
|
"clear_filters": "Počisti {{filterCount}} izbranih"
|
||||||
|
},
|
||||||
|
"game_details": {
|
||||||
|
"open_download_options": "Odpri možnosti prenosa",
|
||||||
|
"download_options_zero": "Ni možnosti prenosa",
|
||||||
|
"download_options_one": "{{count}} možnost prenosa",
|
||||||
|
"download_options_other": "{{count}} možnosti prenosa",
|
||||||
|
"updated_at": "Posodobljeno {{updated_at}}",
|
||||||
|
"install": "Namesti",
|
||||||
|
"resume": "Nadaljuj",
|
||||||
|
"pause": "Premor",
|
||||||
|
"cancel": "Prekliči",
|
||||||
|
"remove": "Odstrani",
|
||||||
|
"space_left_on_disk": "{{space}} prosto na disku",
|
||||||
|
"eta": "Zaključek {{eta}}",
|
||||||
|
"calculating_eta": "Izračun preostalega časa…",
|
||||||
|
"downloading_metadata": "Prenos metapodatkov…",
|
||||||
|
"filter": "Filtriraj repake",
|
||||||
|
"requirements": "Sistemske zahteve",
|
||||||
|
"minimum": "Minimum",
|
||||||
|
"recommended": "Priporočeno",
|
||||||
|
"paused": "V premoru",
|
||||||
|
"release_date": "Izid dne {{date}}",
|
||||||
|
"publisher": "Objavljeno s strani {{publisher}}",
|
||||||
|
"hours": "ur",
|
||||||
|
"minutes": "minut",
|
||||||
|
"amount_hours": "{{amount}} ur",
|
||||||
|
"amount_minutes": "{{amount}} minut",
|
||||||
|
"accuracy": "{{accuracy}}% natančnost",
|
||||||
|
"add_to_library": "Dodaj v knjižnico",
|
||||||
|
"already_in_library": "Že v knjižnici",
|
||||||
|
"remove_from_library": "Odstrani iz knjižnice",
|
||||||
|
"no_downloads": "Ni razpoložljivih prenosov",
|
||||||
|
"play_time": "Odigrano {{amount}}",
|
||||||
|
"last_time_played": "Nazadnje igrano {{period}}",
|
||||||
|
"not_played_yet": "Še niste igrali {{title}}",
|
||||||
|
"next_suggestion": "Naslednji predlog",
|
||||||
|
"play": "Igraj",
|
||||||
|
"deleting": "Brisanje namestitvenega programa…",
|
||||||
|
"close": "Zapri",
|
||||||
|
"playing_now": "Trenutno igranje",
|
||||||
|
"change": "Spremeni",
|
||||||
|
"repacks_modal_description": "Izberite repak, ki ga želite prenesti",
|
||||||
|
"select_folder_hint": "Za spremembo privzete mape pojdite v <0>Nastavitve</0>",
|
||||||
|
"download_now": "Prenesi zdaj",
|
||||||
|
"loading": "Nalaganje...",
|
||||||
|
"no_shop_details": "Podatkov o trgovini ni bilo mogoče pridobiti.",
|
||||||
|
"download_options": "Možnosti prenosa",
|
||||||
|
"download_path": "Pot prenosa",
|
||||||
|
"previous_screenshot": "Prejšnji posnetek zaslona",
|
||||||
|
"next_screenshot": "Naslednji posnetek zaslona",
|
||||||
|
"screenshot": "Posnetek zaslona {{number}}",
|
||||||
|
"open_screenshot": "Odpri posnetek zaslona {{number}}",
|
||||||
|
"download_settings": "Nastavitve prenosa",
|
||||||
|
"downloader": "Prenosnik",
|
||||||
|
"downloader_online": "Spletno",
|
||||||
|
"downloader_not_configured": "Na voljo, vendar ni nastavljeno",
|
||||||
|
"downloader_offline": "Povezava je brez povezave",
|
||||||
|
"downloader_not_available": "Ni na voljo",
|
||||||
|
"recommended": "Priporočeno",
|
||||||
|
"go_to_settings": "Pojdi v nastavitve",
|
||||||
|
"select_executable": "Izberi",
|
||||||
|
"no_executable_selected": "Ni izbrane izvršljive datoteke",
|
||||||
|
"open_folder": "Odpri mapo",
|
||||||
|
"open_download_location": "Poglej prenesene datoteke",
|
||||||
|
"create_shortcut": "Ustvari bližnjico na namizju",
|
||||||
|
"create_shortcut_simple": "Ustvari bližnjico",
|
||||||
|
"clear": "Počisti",
|
||||||
|
"remove_files": "Odstrani datoteke",
|
||||||
|
"remove_from_library_title": "Ali ste prepričani?",
|
||||||
|
"remove_from_library_description": "To bo odstranilo {{game}} iz vaše knjižnice",
|
||||||
|
"options": "Možnosti",
|
||||||
|
"properties": "Lastnosti",
|
||||||
|
"executable_section_title": "Izvršljiva datoteka",
|
||||||
|
"executable_section_description": "Pot do datoteke, ki se bo izvedla ob kliku na \"Igraj\"",
|
||||||
|
"downloads_section_title": "Prenosi",
|
||||||
|
"downloads_section_description": "Preverite posodobitve ali druge različice te igre",
|
||||||
|
"danger_zone_section_title": "Nevarno območje",
|
||||||
|
"danger_zone_section_description": "Odstranite to igro iz knjižnice ali datoteke, ki jih je prenesel Hydra",
|
||||||
|
"download_in_progress": "Prenos v teku",
|
||||||
|
"download_paused": "Prenos v premoru",
|
||||||
|
"extracting": "Razpakiranje",
|
||||||
|
"last_downloaded_option": "Zadnja prenesena možnost",
|
||||||
|
"new_download_option": "Novo",
|
||||||
|
"create_steam_shortcut": "Ustvari Steam bližnjico",
|
||||||
|
"create_shortcut_success": "Bližnjica je bila uspešno ustvarjena",
|
||||||
|
"you_might_need_to_restart_steam": "Morda boste morali ponovno zagnati Steam, da vidite spremembe",
|
||||||
|
"create_shortcut_error": "Napaka pri ustvarjanju bližnjice",
|
||||||
|
"add_to_favorites": "Dodaj med priljubljene",
|
||||||
|
"remove_from_favorites": "Odstrani iz priljubljenih",
|
||||||
|
"failed_update_favorites": "Posodabljanje priljubljenih ni uspelo",
|
||||||
|
"game_removed_from_library": "Igra odstranjena iz knjižnice",
|
||||||
|
"failed_remove_from_library": "Odstranjevanje iz knjižnice ni uspelo",
|
||||||
|
"files_removed_success": "Datoteke so bile uspešno odstranjene",
|
||||||
|
"failed_remove_files": "Odstranjevanje datotek ni uspelo",
|
||||||
|
"nsfw_content_title": "Ta igra vsebuje neprimerno vsebino",
|
||||||
|
"nsfw_content_description": "{{title}} vsebuje vsebino, ki morda ni primerna za vse starosti. Ali ste prepričani, da želite nadaljevati?",
|
||||||
|
"allow_nsfw_content": "Nadaljuj",
|
||||||
|
"refuse_nsfw_content": "Nazaj",
|
||||||
|
"stats": "Statistika",
|
||||||
|
"download_count": "Prenosi",
|
||||||
|
"player_count": "Aktivni igralci",
|
||||||
|
"rating_count": "Ocena",
|
||||||
|
"download_error": "Ta možnost prenosa ni na voljo",
|
||||||
|
"download": "Prenesi",
|
||||||
|
"executable_path_in_use": "Izvršljiva datoteka že uporablja \"{{game}}\"",
|
||||||
|
"warning": "Opozorilo:",
|
||||||
|
"hydra_needs_to_remain_open": "Za ta prenos mora Hydra ostati odprta, dokler ni končana. Če se Hydra zapre pred končanim prenosom, boste izgubili napredek.",
|
||||||
|
"achievements": "Dosežki",
|
||||||
|
"achievements_count": "Dosežki {{unlockedCount}}/{{achievementsCount}}",
|
||||||
|
"show_more": "Pokaži več",
|
||||||
|
"show_less": "Pokaži manj",
|
||||||
|
"reviews": "Mnenja",
|
||||||
|
"review_played_for": "Odigrano za",
|
||||||
|
"leave_a_review": "Oddajte mnenje",
|
||||||
|
"write_review_placeholder": "Delite svoje misli o tej igri...",
|
||||||
|
"sort_newest": "Najnovejše",
|
||||||
|
"no_reviews_yet": "Ni še mnenj",
|
||||||
|
"be_first_to_review": "Bodite prvi, ki delite svoje misli o tej igri!",
|
||||||
|
"sort_oldest": "Najstarejše",
|
||||||
|
"sort_highest_score": "Najvišja ocena",
|
||||||
|
"sort_lowest_score": "Najnižja ocena",
|
||||||
|
"sort_most_voted": "Največ glasov",
|
||||||
|
"rating": "Ocena",
|
||||||
|
"rating_stats": "Ocena",
|
||||||
|
"rating_very_negative": "Zelo negativno",
|
||||||
|
"rating_negative": "Negativno",
|
||||||
|
"rating_neutral": "Nevtralno",
|
||||||
|
"rating_positive": "Pozitivno",
|
||||||
|
"rating_very_positive": "Zelo pozitivno",
|
||||||
|
"submit_review": "Pošlji",
|
||||||
|
"submitting": "Pošiljanje...",
|
||||||
|
"review_submitted_successfully": "Mnenje je bilo uspešno poslano!",
|
||||||
|
"review_submission_failed": "Pošiljanje mnenja ni uspelo. Prosimo, poskusite znova.",
|
||||||
|
"review_cannot_be_empty": "Polje mnenja ne sme biti prazno.",
|
||||||
|
"review_deleted_successfully": "Mnenje je bilo uspešno izbrisano.",
|
||||||
|
"review_deletion_failed": "Brisanje mnenja ni uspelo. Prosimo, poskusite znova.",
|
||||||
|
"loading_reviews": "Nalagam mnenja...",
|
||||||
|
"loading_more_reviews": "Nalagam več mnenj...",
|
||||||
|
"load_more_reviews": "Naloži več mnenj",
|
||||||
|
"you_seemed_to_enjoy_this_game": "Zdi se, da uživate v tej igri",
|
||||||
|
"would_you_recommend_this_game": "Bi radi oddali mnenje o tej igri?",
|
||||||
|
"yes": "Da",
|
||||||
|
"maybe_later": "Mogoče kasneje",
|
||||||
|
"cloud_save": "Shranjevanje v oblaku",
|
||||||
|
"cloud_save_description": "Shranjujte napredek v oblak in nadaljujte igranje na katerikoli napravi",
|
||||||
|
"backups": "Varnostne kopije",
|
||||||
|
"install_backup": "Namesti",
|
||||||
|
"delete_backup": "Izbriši",
|
||||||
|
"create_backup": "Nova varnostna kopija",
|
||||||
|
"last_backup_date": "Zadnja varnostna kopija {{date}}",
|
||||||
|
"no_backup_preview": "Ni shranjenih iger za ta naslov",
|
||||||
|
"restoring_backup": "Obnavljanje varnostne kopije ({{progress}} končano)…",
|
||||||
|
"uploading_backup": "Nalaganje varnostne kopije…",
|
||||||
|
"no_backups": "Za to igro še niste ustvarili varnostnih kopij",
|
||||||
|
"backup_uploaded": "Varnostna kopija naložena",
|
||||||
|
"backup_failed": "Varnostna kopija ni uspela",
|
||||||
|
"backup_deleted": "Varnostna kopija izbrisana",
|
||||||
|
"backup_restored": "Varnostna kopija obnovljena",
|
||||||
|
"see_all_achievements": "Poglej vse dosežke",
|
||||||
|
"sign_in_to_see_achievements": "Prijavite se za ogled dosežkov",
|
||||||
|
"mapping_method_automatic": "Samodejno",
|
||||||
|
"mapping_method_manual": "Ročno",
|
||||||
|
"mapping_method_label": "Način mapiranja",
|
||||||
|
"files_automatically_mapped": "Datoteke so samodejno preslikane",
|
||||||
|
"no_backups_created": "Za to igro ni ustvarjenih varnostnih kopij",
|
||||||
|
"manage_files": "Upravljaj datoteke",
|
||||||
|
"loading_save_preview": "Iskanje shranjenih iger…",
|
||||||
|
"wine_prefix": "Wine predpona",
|
||||||
|
"wine_prefix_description": "Wine predpona, uporabljena za zagon te igre",
|
||||||
|
"launch_options": "Možnosti zagona",
|
||||||
|
"launch_options_description": "Napredni uporabniki lahko vpišejo spremembe v možnosti zagona (eksperimentalna funkcija)",
|
||||||
|
"launch_options_placeholder": "Ni določenega parametra",
|
||||||
|
"no_download_option_info": "Ni razpoložljivih informacij",
|
||||||
|
"backup_deletion_failed": "Brisanje varnostne kopije ni uspelo",
|
||||||
|
"max_number_of_artifacts_reached": "Doseženo je največje število varnostnih kopij za to igro",
|
||||||
|
"achievements_not_sync": "Oglejte si, kako sinhronizirati svoje dosežke",
|
||||||
|
"manage_files_description": "Upravljajte, katere datoteke bodo varnostno kopirane in obnovljene",
|
||||||
|
"select_folder": "Izberite mapo",
|
||||||
|
"backup_from": "Varnostna kopija od {{date}}",
|
||||||
|
"automatic_backup_from": "Samodejna varnostna kopija od {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Omogoči samodejno sinhronizacijo v oblaku",
|
||||||
|
"custom_backup_location_set": "Nastavljena je po meri lokacija varnostne kopije",
|
||||||
|
"no_directory_selected": "Ni izbrane mape",
|
||||||
|
"no_write_permission": "V to mapo ni mogoče prenesti. Kliknite tukaj za več informacij.",
|
||||||
|
"reset_achievements": "Ponastavi dosežke",
|
||||||
|
"reset_achievements_description": "To bo ponastavilo vse dosežke za {{game}}",
|
||||||
|
"reset_achievements_title": "Ali ste prepričani?",
|
||||||
|
"reset_achievements_success": "Dosežki so bili uspešno ponastavljeni",
|
||||||
|
"reset_achievements_error": "Ponastavitev dosežkov ni uspela",
|
||||||
|
"download_error_gofile_quota_exceeded": "Presegli ste mesečno kvoto Gofile. Prosimo, počakajte, da se kvota ponastavi.",
|
||||||
|
"download_error_real_debrid_account_not_authorized": "Vaš račun Real-Debrid ni pooblaščen za nove prenose. Preverite nastavitve računa in poskusite znova.",
|
||||||
|
"download_error_not_cached_on_real_debrid": "Ta prenos ni na voljo v Real-Debrid in preverjanje statusa prenosa iz Real-Debrid še ni na voljo.",
|
||||||
|
"update_playtime_title": "Posodobi čas igranja",
|
||||||
|
"update_playtime_description": "Ročno posodobite čas igranja za {{game}}",
|
||||||
|
"update_playtime": "Posodobi čas igranja",
|
||||||
|
"update_playtime_success": "Čas igranja je bil uspešno posodobljen",
|
||||||
|
"update_playtime_error": "Posodabljanje časa igranja ni uspelo",
|
||||||
|
"update_game_playtime": "Posodobi čas igranja igre",
|
||||||
|
"manual_playtime_warning": "Vaše ure bodo označene kot ročno posodobljene, tega ni mogoče razveljaviti.",
|
||||||
|
"manual_playtime_tooltip": "Ta čas igranja je bil ročno posodobljen",
|
||||||
|
"download_error_not_cached_on_torbox": "Ta prenos ni na voljo v TorBox in preverjanje statusa prenosa iz TorBox še ni na voljo.",
|
||||||
|
"download_error_not_cached_on_hydra": "Ta prenos ni na voljo v Nimbus.",
|
||||||
|
"game_removed_from_favorites": "Igra odstranjena iz priljubljenih",
|
||||||
|
"game_added_to_favorites": "Igra dodana med priljubljene",
|
||||||
|
"game_removed_from_pinned": "Igra odstranjena iz pripetih",
|
||||||
|
"game_added_to_pinned": "Igra pripeta",
|
||||||
|
"automatically_extract_downloaded_files": "Samodejno razpakiraj prenesene datoteke",
|
||||||
|
"create_start_menu_shortcut": "Ustvari bližnjico v Start meniju",
|
||||||
|
"invalid_wine_prefix_path": "Neveljavna pot Wine predpone",
|
||||||
|
"invalid_wine_prefix_path_description": "Pot do Wine predpone je neveljavna. Preverite pot in poskusite znova.",
|
||||||
|
"missing_wine_prefix": "Wine predpona je potrebna za ustvarjanje varnostne kopije na Linuxu",
|
||||||
|
"artifact_renamed": "Varnostna kopija je bila uspešno preimenovana",
|
||||||
|
"rename_artifact": "Preimenuj varnostno kopijo",
|
||||||
|
"rename_artifact_description": "Preimenujte varnostno kopijo v bolj opisno ime",
|
||||||
|
"artifact_name_label": "Ime varnostne kopije",
|
||||||
|
"artifact_name_placeholder": "Vnesite ime varnostne kopije",
|
||||||
|
"save_changes": "Shrani spremembe",
|
||||||
|
"required_field": "To polje je obvezno",
|
||||||
|
"max_length_field": "To polje mora biti krajše od {{length}} znakov",
|
||||||
|
"freeze_backup": "Pripni, da jo samodejne varnostne kopije ne prepišejo",
|
||||||
|
"unfreeze_backup": "Odpni",
|
||||||
|
"backup_frozen": "Varnostna kopija je pripeta",
|
||||||
|
"backup_unfrozen": "Varnostna kopija je odprijeta",
|
||||||
|
"backup_freeze_failed": "Pripenjanje varnostne kopije ni uspelo",
|
||||||
|
"backup_freeze_failed_description": "Morate pustiti vsaj en prost prostor za samodejne varnostne kopije",
|
||||||
|
"edit_game_modal_button": "Prilagodi sredstva igre",
|
||||||
|
"game_details": "Podrobnosti igre",
|
||||||
|
"currency_symbol": "$",
|
||||||
|
"currency_country": "us",
|
||||||
|
"prices": "Cene",
|
||||||
|
"no_prices_found": "Ni najdenih cen",
|
||||||
|
"view_all_prices": "Kliknite za ogled vseh cen",
|
||||||
|
"retail_price": "Maloprodajna cena",
|
||||||
|
"keyshop_price": "Cena v trgovini s ključki",
|
||||||
|
"historical_retail": "Zgodovinska maloprodajna cena",
|
||||||
|
"historical_keyshop": "Zgodovinska cena v trgovini s ključki",
|
||||||
|
"language": "Jezik",
|
||||||
|
"caption": "Naslov",
|
||||||
|
"audio": "Zvok",
|
||||||
|
"filter_by_source": "Filtriraj po viru",
|
||||||
|
"no_repacks_found": "Za to igro ni najdenih virov",
|
||||||
|
"delete_review": "Izbriši mnenje",
|
||||||
|
"remove_review": "Odstrani mnenje",
|
||||||
|
"delete_review_modal_title": "Ali ste prepričani, da želite izbrisati svoje mnenje?",
|
||||||
|
"delete_review_modal_description": "Tega dejanja ni mogoče razveljaviti.",
|
||||||
|
"delete_review_modal_delete_button": "Izbriši",
|
||||||
|
"delete_review_modal_cancel_button": "Prekliči",
|
||||||
|
"vote_failed": "Glasovanja ni uspelo. Prosimo, poskusite znova.",
|
||||||
|
"show_original": "Pokaži original",
|
||||||
|
"show_translation": "Pokaži prevod",
|
||||||
|
"show_original_translated_from": "Pokaži original (prevedeno iz {{language}})",
|
||||||
|
"hide_original": "Skrij original",
|
||||||
|
"review_from_blocked_user": "Mnenje blokiranega uporabnika",
|
||||||
|
"show": "Pokaži",
|
||||||
|
"hide": "Skrij"
|
||||||
|
},
|
||||||
|
"activation": {
|
||||||
|
"title": "Aktiviraj Hydra",
|
||||||
|
"installation_id": "ID namestitve:",
|
||||||
|
"enter_activation_code": "Vnesite aktivacijsko kodo",
|
||||||
|
"message": "Če ne veste, kje naj to pridobite, potem tega ne bi smeli imeti.",
|
||||||
|
"activate": "Aktiviraj",
|
||||||
|
"loading": "Nalaganje…"
|
||||||
|
},
|
||||||
|
"downloads": {
|
||||||
|
"resume": "Nadaljuj",
|
||||||
|
"pause": "Premor",
|
||||||
|
"eta": "Zaključek {{eta}}",
|
||||||
|
"paused": "V premoru",
|
||||||
|
"verifying": "Preverjanje…",
|
||||||
|
"completed": "Dokončano",
|
||||||
|
"removed": "Ni preneseno",
|
||||||
|
"cancel": "Prekliči",
|
||||||
|
"cancel_download": "Prekliči prenos?",
|
||||||
|
"cancel_download_description": "Ali ste prepričani, da želite prekiniti ta prenos? Vse prenesene datoteke bodo izbrisane.",
|
||||||
|
"keep_downloading": "Ne, nadaljuj prenos",
|
||||||
|
"yes_cancel": "Da, prekliči",
|
||||||
|
"filter": "Filtriraj prenesene igre",
|
||||||
|
"remove": "Odstrani",
|
||||||
|
"downloading_metadata": "Prenos metapodatkov…",
|
||||||
|
"deleting": "Brisanje namestitvenega programa…",
|
||||||
|
"delete": "Odstrani namestitveni program",
|
||||||
|
"delete_modal_title": "Ali ste prepričani?",
|
||||||
|
"delete_modal_description": "To bo odstranilo vse namestitvene datoteke z računalnika",
|
||||||
|
"install": "Namesti",
|
||||||
|
"download_in_progress": "V teku",
|
||||||
|
"queued_downloads": "Prenosi v čakalni vrsti",
|
||||||
|
"downloads_completed": "Dokončano",
|
||||||
|
"queued": "V čakalni vrsti",
|
||||||
|
"no_downloads_title": "Tako prazno",
|
||||||
|
"no_downloads_description": "Še niste prenesli ničesar z Hydra, a nikoli ni prepozno začeti.",
|
||||||
|
"checking_files": "Preverjanje datotek…",
|
||||||
|
"seeding": "Sejanje",
|
||||||
|
"stop_seeding": "Ustavi sejanje",
|
||||||
|
"resume_seeding": "Nadaljuj sejanje",
|
||||||
|
"options": "Upravljaj",
|
||||||
|
"extract": "Razpakiraj datoteke",
|
||||||
|
"extracting": "Razpakiranje datotek…",
|
||||||
|
"delete_archive_title": "Ali želite izbrisati {{fileName}}?",
|
||||||
|
"delete_archive_description": "Datoteka je bila uspešno razpakirana in ni več potrebna.",
|
||||||
|
"yes": "Da",
|
||||||
|
"no": "Ne",
|
||||||
|
"network": "OMREŽJE",
|
||||||
|
"peak": "VRH"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"downloads_path": "Pot prenosa",
|
||||||
|
"change": "Posodobi",
|
||||||
|
"notifications": "Obvestila",
|
||||||
|
"enable_download_notifications": "Ko je prenos končan",
|
||||||
|
"enable_repack_list_notifications": "Ko je dodan nov repack",
|
||||||
|
"real_debrid_api_token_label": "Real-Debrid API žeton",
|
||||||
|
"quit_app_instead_hiding": "Ne skrij Hydre pri zapiranju",
|
||||||
|
"launch_with_system": "Zaženi Hydra ob zagonu sistema",
|
||||||
|
"general": "Splošno",
|
||||||
|
"behavior": "Obnašanje",
|
||||||
|
"download_sources": "Viri prenosa",
|
||||||
|
"language": "Jezik",
|
||||||
|
"api_token": "API žeton",
|
||||||
|
"enable_real_debrid": "Omogoči Real-Debrid",
|
||||||
|
"real_debrid_description": "Real-Debrid je neomejen prenašalnik, ki vam omogoča hitro prenašanje datotek, omejeno le s hitrostjo vašega interneta.",
|
||||||
|
"debrid_invalid_token": "Neveljaven API žeton",
|
||||||
|
"debrid_api_token_hint": "Žeton API lahko dobite <0>tukaj</0>",
|
||||||
|
"real_debrid_free_account_error": "Račun \"{{username}}\" je brezplačen. Prosimo, naročite se na Real-Debrid",
|
||||||
|
"debrid_linked_message": "Račun \"{{username}}\" povezan",
|
||||||
|
"save_changes": "Shrani spremembe",
|
||||||
|
"changes_saved": "Spremembe uspešno shranjene",
|
||||||
|
"download_sources_description": "Hydra bo pridobila povezave za prenos iz teh virov. URL vira mora biti neposredna povezava do .json datoteke, ki vsebuje povezave za prenos.",
|
||||||
|
"validate_download_source": "Preveri",
|
||||||
|
"remove_download_source": "Odstrani",
|
||||||
|
"add_download_source": "Dodaj vir",
|
||||||
|
"adding": "Dodajanje…",
|
||||||
|
"failed_add_download_source": "Dodajanje vira za prenos ni uspelo. Poskusite znova.",
|
||||||
|
"download_source_already_exists": "Ta URL vira za prenos že obstaja.",
|
||||||
|
"download_count_zero": "Ni možnosti prenosa",
|
||||||
|
"download_count_one": "{{countFormatted}} možnost prenosa",
|
||||||
|
"download_count_other": "{{countFormatted}} možnosti prenosa",
|
||||||
|
"download_source_url": "URL vira za prenos",
|
||||||
|
"add_download_source_description": "Vstavite URL .json datoteke",
|
||||||
|
"download_source_up_to_date": "Posodobljeno",
|
||||||
|
"download_source_errored": "Napaka",
|
||||||
|
"download_source_pending_matching": "Kmalu posodobljeno",
|
||||||
|
"download_source_matched": "Posodobljeno",
|
||||||
|
"download_source_matching": "Posodabljanje",
|
||||||
|
"download_source_failed": "Napaka",
|
||||||
|
"download_source_no_information": "Ni podatkov na voljo",
|
||||||
|
"sync_download_sources": "Sinhroniziraj vire",
|
||||||
|
"removed_download_source": "Vir prenosa odstranjen",
|
||||||
|
"removed_download_sources": "Viri prenosa odstranjeni",
|
||||||
|
"removed_all_download_sources": "Vsi viri prenosa odstranjeni",
|
||||||
|
"download_sources_synced_successfully": "Vsi viri prenosa so sinhronizirani",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Ne",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Da, izbriši vse",
|
||||||
|
"title_confirmation_delete_all_sources": "Izbriši vse vire prenosa",
|
||||||
|
"description_confirmation_delete_all_sources": "Izbrišete vse vire prenosa",
|
||||||
|
"button_delete_all_sources": "Odstrani vse",
|
||||||
|
"added_download_source": "Vir prenosa dodan",
|
||||||
|
"download_sources_synced": "Vsi viri prenosa so sinhronizirani",
|
||||||
|
"insert_valid_json_url": "Vnesite veljaven JSON URL",
|
||||||
|
"found_download_option_zero": "Ni možnosti prenosa",
|
||||||
|
"found_download_option_one": "Najdena {{countFormatted}} možnost prenosa",
|
||||||
|
"found_download_option_other": "Najdenih {{countFormatted}} možnosti prenosa",
|
||||||
|
"import": "Uvozi",
|
||||||
|
"importing": "Uvažanje...",
|
||||||
|
"public": "Javno",
|
||||||
|
"private": "Zasebno",
|
||||||
|
"friends_only": "Samo prijatelji",
|
||||||
|
"privacy": "Zasebnost",
|
||||||
|
"profile_visibility": "Vidnost profila",
|
||||||
|
"profile_visibility_description": "Izberite, kdo lahko vidi vaš profil in knjižnico",
|
||||||
|
"required_field": "To polje je obvezno",
|
||||||
|
"source_already_exists": "Ta vir je že bil dodan",
|
||||||
|
"must_be_valid_url": "Vir mora biti veljaven URL",
|
||||||
|
"blocked_users": "Blokirani uporabniki",
|
||||||
|
"user_unblocked": "Uporabnik je odblokiran",
|
||||||
|
"enable_achievement_notifications": "Ko je dosežek odklenjen",
|
||||||
|
"launch_minimized": "Zaženi Hydra minimizirano",
|
||||||
|
"disable_nsfw_alert": "Onemogoči opozorilo NSFW",
|
||||||
|
"seed_after_download_complete": "Sejanje po končanem prenosu",
|
||||||
|
"show_hidden_achievement_description": "Pokaži opis skritih dosežkov pred njihovim odklepanjem",
|
||||||
|
"account": "Račun",
|
||||||
|
"hydra_cloud": "Hydra Cloud",
|
||||||
|
"no_users_blocked": "Nimate blokiranih uporabnikov",
|
||||||
|
"subscription_active_until": "Vaš Hydra Cloud je aktiven do {{date}}",
|
||||||
|
"manage_subscription": "Upravljaj naročnino",
|
||||||
|
"update_email": "Posodobi e-pošto",
|
||||||
|
"update_password": "Posodobi geslo",
|
||||||
|
"current_email": "Trenutna e-pošta:",
|
||||||
|
"no_email_account": "Še niste nastavili e-pošte",
|
||||||
|
"account_data_updated_successfully": "Podatki računa so bili uspešno posodobljeni",
|
||||||
|
"renew_subscription": "Obnovi Hydra Cloud",
|
||||||
|
"subscription_expired_at": "Vaša naročnina je potekla {{date}}",
|
||||||
|
"no_subscription": "Uživajte v Hydri na najboljši način",
|
||||||
|
"become_subscriber": "Postanite Hydra Cloud uporabnik",
|
||||||
|
"subscription_renew_cancelled": "Samodejno podaljševanje je onemogočeno",
|
||||||
|
"subscription_renews_on": "Vaša naročnina se podaljša {{date}}",
|
||||||
|
"bill_sent_until": "Naslednji račun bo poslan do tega dne",
|
||||||
|
"no_themes": "Zdi se, da še nimate tem, vendar brez skrbi, kliknite tukaj, da ustvarite svojo prvo mojstrovino.",
|
||||||
|
"editor_tab_code": "Koda",
|
||||||
|
"editor_tab_info": "Info",
|
||||||
|
"editor_tab_save": "Shrani",
|
||||||
|
"web_store": "Spletna trgovina",
|
||||||
|
"clear_themes": "Počisti",
|
||||||
|
"create_theme": "Ustvari",
|
||||||
|
"create_theme_modal_title": "Ustvari prilagojeno temo",
|
||||||
|
"create_theme_modal_description": "Ustvarite novo temo za prilagajanje videza Hydre",
|
||||||
|
"theme_name": "Ime",
|
||||||
|
"insert_theme_name": "Vstavite ime teme",
|
||||||
|
"set_theme": "Nastavi temo",
|
||||||
|
"unset_theme": "Odstrani temo",
|
||||||
|
"delete_theme": "Izbriši temo",
|
||||||
|
"edit_theme": "Uredi temo",
|
||||||
|
"delete_all_themes": "Izbriši vse teme",
|
||||||
|
"delete_all_themes_description": "To bo izbrisalo vse vaše prilagojene teme",
|
||||||
|
"delete_theme_description": "To bo izbrisalo temo {{theme}}",
|
||||||
|
"cancel": "Prekliči",
|
||||||
|
"appearance": "Videz",
|
||||||
|
"debrid": "Debrid",
|
||||||
|
"debrid_description": "Debrid storitve so premium neomejeni prenašalniki, ki vam omogočajo hitro prenašanje datotek, gostovanih na različnih storitvah za gostovanje datotek, omejeno le s hitrostjo vašega interneta.",
|
||||||
|
"enable_torbox": "Omogoči TorBox",
|
||||||
|
"torbox_description": "TorBox je vaša premium seedbox storitev, ki se lahko kosuje tudi najboljšim strežnikom na trgu.",
|
||||||
|
"torbox_account_linked": "TorBox račun povezan",
|
||||||
|
"create_real_debrid_account": "Kliknite tukaj, če še nimate Real-Debrid računa",
|
||||||
|
"create_torbox_account": "Kliknite tukaj, če še nimate TorBox računa",
|
||||||
|
"real_debrid_account_linked": "Real-Debrid račun povezan",
|
||||||
|
"name_min_length": "Ime teme mora imeti vsaj 3 znake",
|
||||||
|
"import_theme": "Uvozi temo",
|
||||||
|
"import_theme_description": "Uvožili boste {{theme}} iz trgovine tem",
|
||||||
|
"error_importing_theme": "Napaka pri uvozu teme",
|
||||||
|
"theme_imported": "Tema uspešno uvožena",
|
||||||
|
"enable_friend_request_notifications": "Ko je prejet prijateljski zahtevek",
|
||||||
|
"enable_auto_install": "Samodejno prenesi posodobitve",
|
||||||
|
"common_redist": "Skupni redistributable-ji",
|
||||||
|
"common_redist_description": "Skupni redistributable-ji so potrebni za zagon nekaterih iger. Priporočamo njihovo namestitev, da se izognete težavam.",
|
||||||
|
"install_common_redist": "Namesti",
|
||||||
|
"installing_common_redist": "Nameščanje…",
|
||||||
|
"show_download_speed_in_megabytes": "Pokaži hitrost prenosa v megabajtih na sekundo",
|
||||||
|
"extract_files_by_default": "Privzeto razpakiraj datoteke po prenosu",
|
||||||
|
"enable_steam_achievements": "Omogoči iskanje po Steam dosežkih",
|
||||||
|
"enable_new_download_options_badges": "Pokaži značke novih možnosti prenosa",
|
||||||
|
"achievement_custom_notification_position": "Lastna pozicija obvestil o dosežkih",
|
||||||
|
"top-left": "Zgoraj levo",
|
||||||
|
"top-center": "Zgoraj na sredini",
|
||||||
|
"top-right": "Zgoraj desno",
|
||||||
|
"bottom-left": "Spodaj levo",
|
||||||
|
"bottom-center": "Spodaj na sredini",
|
||||||
|
"bottom-right": "Spodaj desno",
|
||||||
|
"enable_achievement_custom_notifications": "Omogoči lastna obvestila o dosežkih",
|
||||||
|
"alignment": "Poravnava",
|
||||||
|
"variation": "Variacija",
|
||||||
|
"default": "Privzeto",
|
||||||
|
"rare": "Redko",
|
||||||
|
"platinum": "Platinasto",
|
||||||
|
"hidden": "Skrito",
|
||||||
|
"test_notification": "Preizkusno obvestilo",
|
||||||
|
"achievement_sound_volume": "Glasnost zvoka dosežka",
|
||||||
|
"select_achievement_sound": "Izberite zvok dosežka",
|
||||||
|
"change_achievement_sound": "Spremeni zvok dosežka",
|
||||||
|
"remove_achievement_sound": "Odstrani zvok dosežka",
|
||||||
|
"preview_sound": "Predogled zvoka",
|
||||||
|
"select": "Izberi",
|
||||||
|
"preview": "Predogled",
|
||||||
|
"remove": "Odstrani",
|
||||||
|
"no_sound_file_selected": "Nobena zvočna datoteka ni izbrana",
|
||||||
|
"notification_preview": "Predogled obvestila o dosežku",
|
||||||
|
"enable_friend_start_game_notifications": "Ko prijatelj začne igrati igro",
|
||||||
|
"autoplay_trailers_on_game_page": "Samodejno predvajaj napovednike na strani igre",
|
||||||
|
"hide_to_tray_on_game_start": "Skrij Hydreo v sistemsko vrstico ob zagonu igre",
|
||||||
|
"downloads": "Prenosi",
|
||||||
|
"use_native_http_downloader": "Uporabi izvorni HTTP prenašalnik (eksperimentalno)",
|
||||||
|
"cannot_change_downloader_while_downloading": "Nastavitve ni mogoče spremeniti med prenosom",
|
||||||
|
"notifications": {
|
||||||
|
"download_complete": "Prenos končan",
|
||||||
|
"game_ready_to_install": "{{title}} je pripravljen za namestitev",
|
||||||
|
"repack_list_updated": "Seznam repackov posodobljen",
|
||||||
|
"repack_count_one": "{{count}} repack dodan",
|
||||||
|
"repack_count_other": "{{count}} repackov dodanih",
|
||||||
|
"new_update_available": "Različica {{version}} na voljo",
|
||||||
|
"restart_to_install_update": "Znova zaženite Hydreo za namestitev posodobitve",
|
||||||
|
"notification_achievement_unlocked_title": "Dosežek odklenjen za {{game}}",
|
||||||
|
"notification_achievement_unlocked_body": "{{achievement}} in drugi {{count}} so bili odklenjeni",
|
||||||
|
"new_friend_request_description": "{{displayName}} vam je poslal prijateljsko zahtevo",
|
||||||
|
"new_friend_request_title": "Nova prijateljska zahteva",
|
||||||
|
"extraction_complete": "Razpakiranje končano",
|
||||||
|
"game_extracted": "{{title}} je bil uspešno razpakiran",
|
||||||
|
"friend_started_playing_game": "{{displayName}} je začel igrati igro",
|
||||||
|
"test_achievement_notification_title": "To je preizkusno obvestilo",
|
||||||
|
"test_achievement_notification_description": "Kar kul, kajne?"
|
||||||
|
},
|
||||||
|
"system_tray": {
|
||||||
|
"open": "Odpri Hydreo",
|
||||||
|
"quit": "Izhod"
|
||||||
|
},
|
||||||
|
"game_card": {
|
||||||
|
"available_one": "Na voljo",
|
||||||
|
"available_other": "Na voljo",
|
||||||
|
"no_downloads": "Ni razpoložljivih prenosov",
|
||||||
|
"calculating": "Računam"
|
||||||
|
},
|
||||||
|
"binary_not_found_modal": {
|
||||||
|
"title": "Programi niso nameščeni",
|
||||||
|
"description": "Izvajalniki Wine ali Lutris niso bili najdeni na vašem sistemu",
|
||||||
|
"instructions": "Preverite pravi način za namestitev katerega od njih na vašo Linux distribucijo, da bi igra lahko normalno tekla"
|
||||||
|
},
|
||||||
|
"modal": {
|
||||||
|
"close": "Zapri gumb"
|
||||||
|
},
|
||||||
|
"forms": {
|
||||||
|
"toggle_password_visibility": "Preklopi vidnost gesla"
|
||||||
|
},
|
||||||
|
"user_profile": {
|
||||||
|
"amount_hours": "{{amount}} ur",
|
||||||
|
"amount_minutes": "{{amount}} minut",
|
||||||
|
"amount_hours_short": "{{amount}}h",
|
||||||
|
"amount_minutes_short": "{{amount}}m",
|
||||||
|
"last_time_played": "Nazadnje igrano {{period}}",
|
||||||
|
"activity": "Nedavna dejavnost",
|
||||||
|
"library": "Knjižnica",
|
||||||
|
"pinned": "Pripeto",
|
||||||
|
"sort_by": "Razvrsti po:",
|
||||||
|
"achievements_earned": "Odklenjeni dosežki",
|
||||||
|
"played_recently": "Nazadnje igrano",
|
||||||
|
"playtime": "Čas igranja",
|
||||||
|
"total_play_time": "Skupni čas igranja",
|
||||||
|
"manual_playtime_tooltip": "Ta čas igranja je bil ročno posodobljen",
|
||||||
|
"no_recent_activity_title": "Hmmm… nič tukaj",
|
||||||
|
"no_recent_activity_description": "Niste igrali nobene igre v zadnjem času. Čas je, da to spremenite!",
|
||||||
|
"display_name": "Prikazno ime",
|
||||||
|
"saving": "Shranjevanje",
|
||||||
|
"save": "Shrani",
|
||||||
|
"edit_profile": "Uredi profil",
|
||||||
|
"saved_successfully": "Uspešno shranjeno",
|
||||||
|
"try_again": "Prosimo, poskusite znova",
|
||||||
|
"sign_out_modal_title": "Ste prepričani?",
|
||||||
|
"cancel": "Prekliči",
|
||||||
|
"successfully_signed_out": "Uspešno odjavljeni",
|
||||||
|
"sign_out": "Odjavi se",
|
||||||
|
"playing_for": "Igra za {{amount}}",
|
||||||
|
"sign_out_modal_text": "Vaša knjižnica je povezana s trenutnim računom. Ob odjavi knjižnica ne bo več vidna, napredek pa se ne bo shranil. Nadaljujete z odjavo?",
|
||||||
|
"add_friends": "Dodaj prijatelje",
|
||||||
|
"add": "Dodaj",
|
||||||
|
"friend_code": "Koda prijatelja",
|
||||||
|
"see_profile": "Poglej profil",
|
||||||
|
"sending": "Pošiljanje",
|
||||||
|
"friend_request_sent": "Zahteva za prijateljstvo poslana",
|
||||||
|
"friends": "Prijatelji",
|
||||||
|
"badges": "Značke",
|
||||||
|
"friends_list": "Seznam prijateljev",
|
||||||
|
"user_not_found": "Uporabnik ni najden",
|
||||||
|
"block_user": "Blokiraj uporabnika",
|
||||||
|
"add_friend": "Dodaj prijatelja",
|
||||||
|
"request_sent": "Zahteva poslana",
|
||||||
|
"request_received": "Zahteva prejeta",
|
||||||
|
"accept_request": "Sprejmi zahtevo",
|
||||||
|
"ignore_request": "Ignoriraj zahtevo",
|
||||||
|
"cancel_request": "Prekliči zahtevo",
|
||||||
|
"undo_friendship": "Razveljavi prijateljstvo",
|
||||||
|
"friendship_removed": "Prijatelj odstranjen",
|
||||||
|
"request_accepted": "Zahteva sprejeta",
|
||||||
|
"user_blocked_successfully": "Uporabnik uspešno blokiran",
|
||||||
|
"user_block_modal_text": "To bo blokiralo {{displayName}}",
|
||||||
|
"blocked_users": "Blokirani uporabniki",
|
||||||
|
"unblock": "Odblokiraj",
|
||||||
|
"no_friends_added": "Nimate dodanih prijateljev",
|
||||||
|
"no_friends_yet": "Še niste dodali prijateljev",
|
||||||
|
"view_all": "Poglej vse",
|
||||||
|
"load_more": "Naloži več",
|
||||||
|
"loading": "Nalaganje",
|
||||||
|
"pending": "V teku",
|
||||||
|
"no_pending_invites": "Nimate čakajočih povabil",
|
||||||
|
"no_blocked_users": "Nimate blokiranih uporabnikov",
|
||||||
|
"friend_code_copied": "Koda prijatelja kopirana",
|
||||||
|
"undo_friendship_modal_text": "To bo razveljavilo vaše prijateljstvo z {{displayName}}",
|
||||||
|
"privacy_hint": "Za prilagoditev, kdo to vidi, pojdite na <0>Nastavitve</0>",
|
||||||
|
"locked_profile": "Ta profil je zaseben",
|
||||||
|
"image_process_failure": "Napaka pri obdelavi slike",
|
||||||
|
"required_field": "To polje je obvezno",
|
||||||
|
"displayname_min_length": "Prikazno ime mora biti dolgo vsaj 3 znake",
|
||||||
|
"displayname_max_length": "Prikazno ime mora imeti največ 50 znakov",
|
||||||
|
"report_profile": "Prijavi ta profil",
|
||||||
|
"report_reason": "Zakaj prijavljate ta profil?",
|
||||||
|
"report_description": "Dodatne informacije",
|
||||||
|
"report_description_placeholder": "Dodatne informacije",
|
||||||
|
"report": "Prijavi",
|
||||||
|
"report_reason_hate": "Sovražni govor",
|
||||||
|
"report_reason_sexual_content": "Seksualna vsebina",
|
||||||
|
"report_reason_violence": "Nasilje",
|
||||||
|
"report_reason_spam": "Spam",
|
||||||
|
"report_reason_other": "Drugo",
|
||||||
|
"profile_reported": "Profil prijavljen",
|
||||||
|
"your_friend_code": "Vaša koda prijatelja:",
|
||||||
|
"copy_friend_code": "Kopiraj kodo prijatelja",
|
||||||
|
"copied": "Kopirano!",
|
||||||
|
"upload_banner": "Naloži banner",
|
||||||
|
"uploading_banner": "Nalaganje bannerja…",
|
||||||
|
"change_banner": "Spremeni banner",
|
||||||
|
"replace_banner": "Zamenjaj banner",
|
||||||
|
"remove_banner": "Odstrani banner",
|
||||||
|
"remove_banner_modal_title": "Odstrani banner?",
|
||||||
|
"remove_banner_confirmation": "Ali ste prepričani, da želite odstraniti banner? Kadarkoli lahko izberete novega.",
|
||||||
|
"remove": "Odstrani",
|
||||||
|
"background_image_updated": "Pozadinska slika posodobljena",
|
||||||
|
"stats": "Statistika",
|
||||||
|
"achievements": "dosežki",
|
||||||
|
"games": "Igre",
|
||||||
|
"top_percentile": "Top {{percentile}}%",
|
||||||
|
"ranking_updated_weekly": "Uvrstitev se posodablja tedensko",
|
||||||
|
"playing": "Igra {{game}}",
|
||||||
|
"achievements_unlocked": "Dosežki odklenjeni",
|
||||||
|
"earned_points": "Zaslužene točke",
|
||||||
|
"show_achievements_on_profile": "Pokaži vaše dosežke na profilu",
|
||||||
|
"show_points_on_profile": "Pokaži vaše zaslužene točke na profilu",
|
||||||
|
"error_adding_friend": "Zahteve za prijatelja ni bilo mogoče poslati. Preverite kodo prijatelja",
|
||||||
|
"friend_code_length_error": "Koda prijatelja mora vsebovati 8 znakov",
|
||||||
|
"game_removed_from_pinned": "Igra odstranjena iz pripetih",
|
||||||
|
"game_added_to_pinned": "Igra dodana med pripete",
|
||||||
|
"karma": "Karma",
|
||||||
|
"karma_count": "karma",
|
||||||
|
"user_reviews": "Mnenja",
|
||||||
|
"delete_review": "Izbriši mnenje",
|
||||||
|
"loading_reviews": "Nalaganje mnenj...",
|
||||||
|
"wrapped_2025": "Wrapped 2025"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"library": "Knjižnica",
|
||||||
|
"play": "Igraj",
|
||||||
|
"download": "Prenesi",
|
||||||
|
"downloading": "Prenašanje",
|
||||||
|
"game": "igra",
|
||||||
|
"games": "igre",
|
||||||
|
"grid_view": "Mrežni pogled",
|
||||||
|
"compact_view": "Kompaktni pogled",
|
||||||
|
"large_view": "Velik pogled",
|
||||||
|
"no_games_title": "Vaša knjižnica je prazna",
|
||||||
|
"no_games_description": "Dodajte igre iz kataloga ali jih prenesite, da začnete",
|
||||||
|
"amount_hours": "{{amount}} ur",
|
||||||
|
"amount_minutes": "{{amount}} minut",
|
||||||
|
"amount_hours_short": "{{amount}}h",
|
||||||
|
"amount_minutes_short": "{{amount}}m",
|
||||||
|
"manual_playtime_tooltip": "Ta čas igranja je bil ročno posodobljen",
|
||||||
|
"all_games": "Vse igre",
|
||||||
|
"recently_played": "Nedavno igrane",
|
||||||
|
"favorites": "Priljubljene"
|
||||||
|
},
|
||||||
|
"achievement": {
|
||||||
|
"achievement_unlocked": "Dosežek odklenjen",
|
||||||
|
"user_achievements": "Dosežki uporabnika {{displayName}}",
|
||||||
|
"your_achievements": "Vaši dosežki",
|
||||||
|
"unlocked_at": "Odklenjeno: {{date}}",
|
||||||
|
"subscription_needed": "Naročnina na Hydra Cloud je potrebna za ogled te vsebine",
|
||||||
|
"new_achievements_unlocked": "Odklenili ste {{achievementCount}} novih dosežkov iz {{gameCount}} iger",
|
||||||
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} dosežkov",
|
||||||
|
"achievements_unlocked_for_game": "Odklenili ste {{achievementCount}} novih dosežkov za {{gameTitle}}",
|
||||||
|
"hidden_achievement_tooltip": "To je skriti dosežek",
|
||||||
|
"achievement_earn_points": "Z zaslužite {{points}} točk s tem dosežkom",
|
||||||
|
"earned_points": "Zaslužene točke:",
|
||||||
|
"available_points": "Razpoložljive točke:",
|
||||||
|
"how_to_earn_achievements_points": "Kako zaslužiti točke za dosežke?"
|
||||||
|
},
|
||||||
|
"hydra_cloud": {
|
||||||
|
"subscription_tour_title": "Naročnina Hydra Cloud",
|
||||||
|
"subscribe_now": "Naroči se zdaj",
|
||||||
|
"cloud_saving": "Shranjevanje v oblak",
|
||||||
|
"cloud_achievements": "Shrani svoje dosežke v oblak",
|
||||||
|
"animated_profile_picture": "Animirane profilne slike",
|
||||||
|
"premium_support": "Premium podpora",
|
||||||
|
"show_and_compare_achievements": "Pokaži in primerjaj svoje dosežke z drugimi uporabniki",
|
||||||
|
"animated_profile_banner": "Animirani profilni banner",
|
||||||
|
"hydra_cloud": "Hydra Cloud",
|
||||||
|
"hydra_cloud_feature_found": "Pravkar ste odkrili funkcijo Hydra Cloud!",
|
||||||
|
"learn_more": "Več informacij",
|
||||||
|
"debrid_description": "Prenesite do 4x hitreje z Nimbusom"
|
||||||
|
},
|
||||||
|
"notifications_page": {
|
||||||
|
"title": "Obvestila",
|
||||||
|
"mark_all_as_read": "Označi vse kot prebrano",
|
||||||
|
"clear_all": "Počisti vse",
|
||||||
|
"loading": "Nalagam...",
|
||||||
|
"empty_title": "Ni obvestil",
|
||||||
|
"empty_description": "Ste na tekočem! Preverite kasneje za nove posodobitve.",
|
||||||
|
"empty_filter_description": "Nobeno obvestilo ne ustreza tem filtram.",
|
||||||
|
"filter_all": "Vse",
|
||||||
|
"filter_unread": "Neprebrano",
|
||||||
|
"filter_friends": "Prijatelji",
|
||||||
|
"filter_badges": "Značke",
|
||||||
|
"filter_upvotes": "Glasovi za všečkanje",
|
||||||
|
"filter_local": "Lokalno",
|
||||||
|
"load_more": "Naloži več",
|
||||||
|
"dismiss": "Opusti",
|
||||||
|
"accept": "Sprejmi",
|
||||||
|
"refuse": "Zavrni",
|
||||||
|
"notification": "Obvestilo",
|
||||||
|
"friend_request_received_title": "Nova prijateljska zahteva!",
|
||||||
|
"friend_request_received_description": "{{displayName}} želi biti vaš prijatelj",
|
||||||
|
"friend_request_accepted_title": "Zahteva za prijateljstvo sprejeta!",
|
||||||
|
"friend_request_accepted_description": "{{displayName}} je sprejel vašo zahtevo",
|
||||||
|
"badge_received_title": "Prejeli ste novo značko!",
|
||||||
|
"badge_received_description": "{{badgeName}}",
|
||||||
|
"review_upvote_title": "Vaša recenzija za {{gameTitle}} je dobila glasove!",
|
||||||
|
"review_upvote_description": "Vaša recenzija je dobila {{count}} novih glasov",
|
||||||
|
"marked_all_as_read": "Vsa obvestila označena kot prebrana",
|
||||||
|
"failed_to_mark_as_read": "Neuspešno označevanje obvestil kot prebranih",
|
||||||
|
"cleared_all": "Vsa obvestila izbrisana",
|
||||||
|
"failed_to_clear": "Neuspešno brisanje obvestil",
|
||||||
|
"failed_to_load": "Neuspešno nalaganje obvestil",
|
||||||
|
"failed_to_dismiss": "Neuspešno opustitev obvestila",
|
||||||
|
"friend_request_accepted": "Zahteva za prijateljstvo sprejeta",
|
||||||
|
"friend_request_refused": "Zahteva za prijateljstvo zavrnjena"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,14 +15,7 @@ const deleteGameFolder = async (
|
|||||||
const downloadKey = levelKeys.game(shop, objectId);
|
const downloadKey = levelKeys.game(shop, objectId);
|
||||||
const download = await downloadsSublevel.get(downloadKey);
|
const download = await downloadsSublevel.get(downloadKey);
|
||||||
|
|
||||||
if (!download?.folderName) return;
|
if (!download) return;
|
||||||
|
|
||||||
const folderPath = path.join(
|
|
||||||
download.downloadPath ?? (await getDownloadsPath()),
|
|
||||||
download.folderName
|
|
||||||
);
|
|
||||||
|
|
||||||
const metaPath = `${folderPath}.meta`;
|
|
||||||
|
|
||||||
const deleteFile = async (filePath: string, isDirectory = false) => {
|
const deleteFile = async (filePath: string, isDirectory = false) => {
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
@@ -47,8 +40,18 @@ const deleteGameFolder = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await deleteFile(folderPath, true);
|
if (download.folderName) {
|
||||||
await deleteFile(metaPath);
|
const folderPath = path.join(
|
||||||
|
download.downloadPath ?? (await getDownloadsPath()),
|
||||||
|
download.folderName
|
||||||
|
);
|
||||||
|
|
||||||
|
const metaPath = `${folderPath}.meta`;
|
||||||
|
|
||||||
|
await deleteFile(folderPath, true);
|
||||||
|
await deleteFile(metaPath);
|
||||||
|
}
|
||||||
|
|
||||||
await downloadsSublevel.del(downloadKey);
|
await downloadsSublevel.del(downloadKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ const getGameInstallerActionType = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(gamePath)) {
|
if (!fs.existsSync(gamePath)) {
|
||||||
await downloadsSublevel.del(downloadKey);
|
|
||||||
return "open-folder";
|
return "open-folder";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ const openGameInstaller = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(gamePath)) {
|
if (!fs.existsSync(gamePath)) {
|
||||||
await downloadsSublevel.del(downloadKey);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
src/main/events/torrenting/add-game-to-queue.ts
Normal file
78
src/main/events/torrenting/add-game-to-queue.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import type { Download, StartGameDownloadPayload } from "@types";
|
||||||
|
import { DownloadManager, HydraApi, logger } from "@main/services";
|
||||||
|
import { createGame } from "@main/services/library-sync";
|
||||||
|
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
||||||
|
import { parseBytes } from "@shared";
|
||||||
|
import { handleDownloadError, prepareGameEntry } from "@main/helpers";
|
||||||
|
|
||||||
|
const addGameToQueue = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
payload: StartGameDownloadPayload
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
objectId,
|
||||||
|
title,
|
||||||
|
shop,
|
||||||
|
downloadPath,
|
||||||
|
downloader,
|
||||||
|
uri,
|
||||||
|
automaticallyExtract,
|
||||||
|
fileSize,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const gameKey = levelKeys.game(shop, objectId);
|
||||||
|
|
||||||
|
const download: Download = {
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
status: "paused",
|
||||||
|
progress: 0,
|
||||||
|
bytesDownloaded: 0,
|
||||||
|
downloadPath,
|
||||||
|
downloader,
|
||||||
|
uri,
|
||||||
|
folderName: null,
|
||||||
|
fileSize: parseBytes(fileSize ?? null),
|
||||||
|
shouldSeed: false,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
queued: true,
|
||||||
|
extracting: false,
|
||||||
|
automaticallyExtract,
|
||||||
|
extractionProgress: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await DownloadManager.validateDownloadUrl(download);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
logger.error("Failed to validate download URL for queue", err);
|
||||||
|
return handleDownloadError(err, downloader);
|
||||||
|
}
|
||||||
|
|
||||||
|
await prepareGameEntry({ gameKey, title, objectId, shop });
|
||||||
|
|
||||||
|
try {
|
||||||
|
await downloadsSublevel.put(gameKey, download);
|
||||||
|
|
||||||
|
const updatedGame = await gamesSublevel.get(gameKey);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
createGame(updatedGame!).catch(() => {}),
|
||||||
|
HydraApi.post(`/games/${shop}/${objectId}/download`, null, {
|
||||||
|
needsAuth: false,
|
||||||
|
}).catch(() => {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
} catch (err: unknown) {
|
||||||
|
logger.error("Failed to add game to queue", err);
|
||||||
|
|
||||||
|
if (err instanceof Error) {
|
||||||
|
return { ok: false, error: err.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: false };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("addGameToQueue", addGameToQueue);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import "./add-game-to-queue";
|
||||||
import "./cancel-game-download";
|
import "./cancel-game-download";
|
||||||
import "./check-debrid-availability";
|
import "./check-debrid-availability";
|
||||||
import "./pause-game-download";
|
import "./pause-game-download";
|
||||||
@@ -5,3 +6,4 @@ import "./pause-game-seed";
|
|||||||
import "./resume-game-download";
|
import "./resume-game-download";
|
||||||
import "./resume-game-seed";
|
import "./resume-game-seed";
|
||||||
import "./start-game-download";
|
import "./start-game-download";
|
||||||
|
import "./update-download-queue-position";
|
||||||
|
|||||||
@@ -2,14 +2,8 @@ import { registerEvent } from "../register-event";
|
|||||||
import type { Download, StartGameDownloadPayload } from "@types";
|
import type { Download, StartGameDownloadPayload } from "@types";
|
||||||
import { DownloadManager, HydraApi, logger } from "@main/services";
|
import { DownloadManager, HydraApi, logger } from "@main/services";
|
||||||
import { createGame } from "@main/services/library-sync";
|
import { createGame } from "@main/services/library-sync";
|
||||||
import { Downloader, DownloadError } from "@shared";
|
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import {
|
import { handleDownloadError, prepareGameEntry } from "@main/helpers";
|
||||||
downloadsSublevel,
|
|
||||||
gamesShopAssetsSublevel,
|
|
||||||
gamesSublevel,
|
|
||||||
levelKeys,
|
|
||||||
} from "@main/level";
|
|
||||||
import { AxiosError } from "axios";
|
|
||||||
|
|
||||||
const startGameDownload = async (
|
const startGameDownload = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@@ -38,30 +32,7 @@ const startGameDownload = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const game = await gamesSublevel.get(gameKey);
|
await prepareGameEntry({ gameKey, title, objectId, shop });
|
||||||
const gameAssets = await gamesShopAssetsSublevel.get(gameKey);
|
|
||||||
|
|
||||||
await downloadsSublevel.del(gameKey);
|
|
||||||
|
|
||||||
if (game) {
|
|
||||||
await gamesSublevel.put(gameKey, {
|
|
||||||
...game,
|
|
||||||
isDeleted: false,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await gamesSublevel.put(gameKey, {
|
|
||||||
title,
|
|
||||||
iconUrl: gameAssets?.iconUrl ?? null,
|
|
||||||
libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null,
|
|
||||||
logoImageUrl: gameAssets?.logoImageUrl ?? null,
|
|
||||||
objectId,
|
|
||||||
shop,
|
|
||||||
remoteId: null,
|
|
||||||
playTimeInMilliseconds: 0,
|
|
||||||
lastTimePlayed: null,
|
|
||||||
isDeleted: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await DownloadManager.cancelDownload(gameKey);
|
await DownloadManager.cancelDownload(gameKey);
|
||||||
|
|
||||||
@@ -101,68 +72,7 @@ const startGameDownload = async (
|
|||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
logger.error("Failed to start download", err);
|
logger.error("Failed to start download", err);
|
||||||
|
return handleDownloadError(err, downloader);
|
||||||
if (err instanceof AxiosError) {
|
|
||||||
if (err.response?.status === 429 && downloader === Downloader.Gofile) {
|
|
||||||
return { ok: false, error: DownloadError.GofileQuotaExceeded };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
err.response?.status === 403 &&
|
|
||||||
downloader === Downloader.RealDebrid
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error: DownloadError.RealDebridAccountNotAuthorized,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloader === Downloader.TorBox) {
|
|
||||||
return { ok: false, error: err.response?.data?.detail };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof Error) {
|
|
||||||
if (downloader === Downloader.Buzzheavier) {
|
|
||||||
if (err.message.includes("Rate limit")) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error: "Buzzheavier: Rate limit exceeded",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
err.message.includes("not found") ||
|
|
||||||
err.message.includes("deleted")
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error: "Buzzheavier: File not found",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloader === Downloader.FuckingFast) {
|
|
||||||
if (err.message.includes("Rate limit")) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error: "FuckingFast: Rate limit exceeded",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
err.message.includes("not found") ||
|
|
||||||
err.message.includes("deleted")
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error: "FuckingFast: File not found",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ok: false, error: err.message };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ok: false };
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
67
src/main/events/torrenting/update-download-queue-position.ts
Normal file
67
src/main/events/torrenting/update-download-queue-position.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { downloadsSublevel, levelKeys } from "@main/level";
|
||||||
|
import { GameShop } from "@types";
|
||||||
|
import { orderBy } from "lodash-es";
|
||||||
|
|
||||||
|
const updateDownloadQueuePosition = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
direction: "up" | "down"
|
||||||
|
) => {
|
||||||
|
const gameKey = levelKeys.game(shop, objectId);
|
||||||
|
|
||||||
|
const download = await downloadsSublevel.get(gameKey);
|
||||||
|
|
||||||
|
if (!download || !download.queued || download.status !== "paused") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allDownloads = await downloadsSublevel.values().all();
|
||||||
|
|
||||||
|
const queuedDownloads = orderBy(
|
||||||
|
allDownloads.filter((d) => d.status === "paused" && d.queued),
|
||||||
|
"timestamp",
|
||||||
|
"desc"
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentIndex = queuedDownloads.findIndex(
|
||||||
|
(d) => d.shop === shop && d.objectId === objectId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetIndex = direction === "up" ? currentIndex - 1 : currentIndex + 1;
|
||||||
|
|
||||||
|
if (targetIndex < 0 || targetIndex >= queuedDownloads.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDownload = queuedDownloads[currentIndex];
|
||||||
|
const adjacentDownload = queuedDownloads[targetIndex];
|
||||||
|
|
||||||
|
const currentKey = levelKeys.game(
|
||||||
|
currentDownload.shop,
|
||||||
|
currentDownload.objectId
|
||||||
|
);
|
||||||
|
const adjacentKey = levelKeys.game(
|
||||||
|
adjacentDownload.shop,
|
||||||
|
adjacentDownload.objectId
|
||||||
|
);
|
||||||
|
|
||||||
|
const tempTimestamp = currentDownload.timestamp;
|
||||||
|
await downloadsSublevel.put(currentKey, {
|
||||||
|
...currentDownload,
|
||||||
|
timestamp: adjacentDownload.timestamp,
|
||||||
|
});
|
||||||
|
await downloadsSublevel.put(adjacentKey, {
|
||||||
|
...adjacentDownload,
|
||||||
|
timestamp: tempTimestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("updateDownloadQueuePosition", updateDownloadQueuePosition);
|
||||||
51
src/main/helpers/download-error-handler.ts
Normal file
51
src/main/helpers/download-error-handler.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { AxiosError } from "axios";
|
||||||
|
import { Downloader, DownloadError } from "@shared";
|
||||||
|
|
||||||
|
export const handleDownloadError = (
|
||||||
|
err: unknown,
|
||||||
|
downloader: Downloader
|
||||||
|
): { ok: false; error?: string } => {
|
||||||
|
if (err instanceof AxiosError) {
|
||||||
|
if (err.response?.status === 429 && downloader === Downloader.Gofile) {
|
||||||
|
return { ok: false, error: DownloadError.GofileQuotaExceeded };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.response?.status === 403 && downloader === Downloader.RealDebrid) {
|
||||||
|
return { ok: false, error: DownloadError.RealDebridAccountNotAuthorized };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloader === Downloader.TorBox) {
|
||||||
|
return { ok: false, error: err.response?.data?.detail };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof Error) {
|
||||||
|
if (downloader === Downloader.Buzzheavier) {
|
||||||
|
if (err.message.includes("Rate limit")) {
|
||||||
|
return { ok: false, error: "Buzzheavier: Rate limit exceeded" };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
err.message.includes("not found") ||
|
||||||
|
err.message.includes("deleted")
|
||||||
|
) {
|
||||||
|
return { ok: false, error: "Buzzheavier: File not found" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloader === Downloader.FuckingFast) {
|
||||||
|
if (err.message.includes("Rate limit")) {
|
||||||
|
return { ok: false, error: "FuckingFast: Rate limit exceeded" };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
err.message.includes("not found") ||
|
||||||
|
err.message.includes("deleted")
|
||||||
|
) {
|
||||||
|
return { ok: false, error: "FuckingFast: File not found" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: false, error: err.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: false };
|
||||||
|
};
|
||||||
45
src/main/helpers/download-game-helper.ts
Normal file
45
src/main/helpers/download-game-helper.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
downloadsSublevel,
|
||||||
|
gamesShopAssetsSublevel,
|
||||||
|
gamesSublevel,
|
||||||
|
} from "@main/level";
|
||||||
|
import type { GameShop } from "@types";
|
||||||
|
|
||||||
|
interface PrepareGameEntryParams {
|
||||||
|
gameKey: string;
|
||||||
|
title: string;
|
||||||
|
objectId: string;
|
||||||
|
shop: GameShop;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const prepareGameEntry = async ({
|
||||||
|
gameKey,
|
||||||
|
title,
|
||||||
|
objectId,
|
||||||
|
shop,
|
||||||
|
}: PrepareGameEntryParams): Promise<void> => {
|
||||||
|
const game = await gamesSublevel.get(gameKey);
|
||||||
|
const gameAssets = await gamesShopAssetsSublevel.get(gameKey);
|
||||||
|
|
||||||
|
await downloadsSublevel.del(gameKey);
|
||||||
|
|
||||||
|
if (game) {
|
||||||
|
await gamesSublevel.put(gameKey, {
|
||||||
|
...game,
|
||||||
|
isDeleted: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await gamesSublevel.put(gameKey, {
|
||||||
|
title,
|
||||||
|
iconUrl: gameAssets?.iconUrl ?? null,
|
||||||
|
libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null,
|
||||||
|
logoImageUrl: gameAssets?.logoImageUrl ?? null,
|
||||||
|
objectId,
|
||||||
|
shop,
|
||||||
|
remoteId: null,
|
||||||
|
playTimeInMilliseconds: 0,
|
||||||
|
lastTimePlayed: null,
|
||||||
|
isDeleted: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -94,3 +94,5 @@ export const getThemeSoundPath = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export * from "./reg-parser";
|
export * from "./reg-parser";
|
||||||
|
export * from "./download-error-handler";
|
||||||
|
export * from "./download-game-helper";
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { RealDebridClient } from "./real-debrid";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import { sortBy } from "lodash-es";
|
import { orderBy } from "lodash-es";
|
||||||
import { TorBoxClient } from "./torbox";
|
import { TorBoxClient } from "./torbox";
|
||||||
import { GameFilesManager } from "../game-files-manager";
|
import { GameFilesManager } from "../game-files-manager";
|
||||||
import { HydraDebridClient } from "./hydra-debrid";
|
import { HydraDebridClient } from "./hydra-debrid";
|
||||||
@@ -323,7 +323,8 @@ export class DownloadManager {
|
|||||||
|
|
||||||
this.sendProgressUpdate(progress, status, game);
|
this.sendProgressUpdate(progress, status, game);
|
||||||
|
|
||||||
if (progress === 1) {
|
const isComplete = progress === 1 || download.status === "complete";
|
||||||
|
if (isComplete) {
|
||||||
await this.handleDownloadCompletion(download, game, gameId);
|
await this.handleDownloadCompletion(download, game, gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,10 +423,10 @@ export class DownloadManager {
|
|||||||
.values()
|
.values()
|
||||||
.all()
|
.all()
|
||||||
.then((games) =>
|
.then((games) =>
|
||||||
sortBy(
|
orderBy(
|
||||||
games.filter((game) => game.status === "paused" && game.queued),
|
games.filter((game) => game.status === "paused" && game.queued),
|
||||||
"timestamp",
|
["timestamp"],
|
||||||
"DESC"
|
["desc"]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -499,18 +500,20 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async cancelDownload(downloadKey = this.downloadingGameId) {
|
static async cancelDownload(downloadKey = this.downloadingGameId) {
|
||||||
if (this.usingJsDownloader && this.jsDownloader) {
|
const isActiveDownload = downloadKey === this.downloadingGameId;
|
||||||
logger.log("[DownloadManager] Cancelling JS download");
|
|
||||||
this.jsDownloader.cancelDownload();
|
if (isActiveDownload) {
|
||||||
this.jsDownloader = null;
|
if (this.usingJsDownloader && this.jsDownloader) {
|
||||||
this.usingJsDownloader = false;
|
logger.log("[DownloadManager] Cancelling JS download");
|
||||||
} else if (!this.isPreparingDownload) {
|
this.jsDownloader.cancelDownload();
|
||||||
await PythonRPC.rpc
|
this.jsDownloader = null;
|
||||||
.post("/action", { action: "cancel", game_id: downloadKey })
|
this.usingJsDownloader = false;
|
||||||
.catch((err) => logger.error("Failed to cancel game download", err));
|
} else if (!this.isPreparingDownload) {
|
||||||
}
|
await PythonRPC.rpc
|
||||||
|
.post("/action", { action: "cancel", game_id: downloadKey })
|
||||||
|
.catch((err) => logger.error("Failed to cancel game download", err));
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadKey === this.downloadingGameId) {
|
|
||||||
WindowManager.mainWindow?.setProgressBar(-1);
|
WindowManager.mainWindow?.setProgressBar(-1);
|
||||||
WindowManager.mainWindow?.webContents.send("on-download-progress", null);
|
WindowManager.mainWindow?.webContents.send("on-download-progress", null);
|
||||||
this.downloadingGameId = null;
|
this.downloadingGameId = null;
|
||||||
@@ -932,6 +935,20 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async validateDownloadUrl(download: Download): Promise<void> {
|
||||||
|
const useJsDownloader = await this.shouldUseJsDownloader();
|
||||||
|
const isHttp = this.isHttpDownloader(download.downloader);
|
||||||
|
|
||||||
|
if (useJsDownloader && isHttp) {
|
||||||
|
const options = await this.getJsDownloadOptions(download);
|
||||||
|
if (!options) {
|
||||||
|
throw new Error("Failed to validate download URL");
|
||||||
|
}
|
||||||
|
} else if (isHttp) {
|
||||||
|
await this.getDownloadPayload(download);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async startDownload(download: Download) {
|
static async startDownload(download: Download) {
|
||||||
const useJsDownloader = await this.shouldUseJsDownloader();
|
const useJsDownloader = await this.shouldUseJsDownloader();
|
||||||
const isHttp = this.isHttpDownloader(download.downloader);
|
const isHttp = this.isHttpDownloader(download.downloader);
|
||||||
|
|||||||
@@ -320,10 +320,17 @@ export class JsHttpDownloader {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
if (this.status === "complete") {
|
||||||
|
progress = 1;
|
||||||
|
} else if (this.fileSize > 0) {
|
||||||
|
progress = this.bytesDownloaded / this.fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
folderName: this.folderName,
|
folderName: this.folderName,
|
||||||
fileSize: this.fileSize,
|
fileSize: this.fileSize,
|
||||||
progress: this.fileSize > 0 ? this.bytesDownloaded / this.fileSize : 0,
|
progress,
|
||||||
downloadSpeed: this.downloadSpeed,
|
downloadSpeed: this.downloadSpeed,
|
||||||
numPeers: 0,
|
numPeers: 0,
|
||||||
numSeeds: 0,
|
numSeeds: 0,
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ import path from "node:path";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import type { GameShop } from "@types";
|
import type { GameShop } from "@types";
|
||||||
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
import { downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import { FILE_EXTENSIONS_TO_EXTRACT } from "@shared";
|
import { FILE_EXTENSIONS_TO_EXTRACT, removeSymbolsFromName } from "@shared";
|
||||||
import { SevenZip, ExtractionProgress } from "./7zip";
|
import { SevenZip, ExtractionProgress } from "./7zip";
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
import { publishExtractionCompleteNotification } from "./notifications";
|
import { publishExtractionCompleteNotification } from "./notifications";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { GameExecutables } from "./game-executables";
|
import { GameExecutables } from "./game-executables";
|
||||||
|
import createDesktopShortcut from "create-desktop-shortcuts";
|
||||||
|
import { app } from "electron";
|
||||||
|
import { SystemPath } from "./system-path";
|
||||||
|
|
||||||
const PROGRESS_THROTTLE_MS = 1000;
|
const PROGRESS_THROTTLE_MS = 1000;
|
||||||
|
|
||||||
@@ -204,6 +207,8 @@ export class GameFilesManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
WindowManager.mainWindow?.webContents.send("on-library-batch-complete");
|
WindowManager.mainWindow?.webContents.send("on-library-batch-complete");
|
||||||
|
|
||||||
|
await this.createDesktopShortcutForGame(game.title, foundExePath);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -213,6 +218,40 @@ export class GameFilesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createDesktopShortcutForGame(
|
||||||
|
gameTitle: string,
|
||||||
|
executablePath: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
const windowVbsPath = app.isPackaged
|
||||||
|
? path.join(process.resourcesPath, "windows.vbs")
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
filePath: executablePath,
|
||||||
|
name: removeSymbolsFromName(gameTitle),
|
||||||
|
outputPath: SystemPath.getPath("desktop"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const success = createDesktopShortcut({
|
||||||
|
windows: { ...options, VBScriptPath: windowVbsPath },
|
||||||
|
linux: options,
|
||||||
|
osx: options,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
logger.info(
|
||||||
|
`[GameFilesManager] Created desktop shortcut for ${this.objectId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
`[GameFilesManager] Error creating desktop shortcut: ${this.objectId}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async findExecutableInFolder(
|
private async findExecutableInFolder(
|
||||||
folderPath: string,
|
folderPath: string,
|
||||||
executableNames: string[]
|
executableNames: string[]
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
/* Torrenting */
|
/* Torrenting */
|
||||||
startGameDownload: (payload: StartGameDownloadPayload) =>
|
startGameDownload: (payload: StartGameDownloadPayload) =>
|
||||||
ipcRenderer.invoke("startGameDownload", payload),
|
ipcRenderer.invoke("startGameDownload", payload),
|
||||||
|
addGameToQueue: (payload: StartGameDownloadPayload) =>
|
||||||
|
ipcRenderer.invoke("addGameToQueue", payload),
|
||||||
cancelGameDownload: (shop: GameShop, objectId: string) =>
|
cancelGameDownload: (shop: GameShop, objectId: string) =>
|
||||||
ipcRenderer.invoke("cancelGameDownload", shop, objectId),
|
ipcRenderer.invoke("cancelGameDownload", shop, objectId),
|
||||||
pauseGameDownload: (shop: GameShop, objectId: string) =>
|
pauseGameDownload: (shop: GameShop, objectId: string) =>
|
||||||
@@ -37,6 +39,17 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("pauseGameSeed", shop, objectId),
|
ipcRenderer.invoke("pauseGameSeed", shop, objectId),
|
||||||
resumeGameSeed: (shop: GameShop, objectId: string) =>
|
resumeGameSeed: (shop: GameShop, objectId: string) =>
|
||||||
ipcRenderer.invoke("resumeGameSeed", shop, objectId),
|
ipcRenderer.invoke("resumeGameSeed", shop, objectId),
|
||||||
|
updateDownloadQueuePosition: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
direction: "up" | "down"
|
||||||
|
) =>
|
||||||
|
ipcRenderer.invoke(
|
||||||
|
"updateDownloadQueuePosition",
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
direction
|
||||||
|
),
|
||||||
onDownloadProgress: (cb: (value: DownloadProgress | null) => void) => {
|
onDownloadProgress: (cb: (value: DownloadProgress | null) => void) => {
|
||||||
const listener = (
|
const listener = (
|
||||||
_event: Electron.IpcRendererEvent,
|
_event: Electron.IpcRendererEvent,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
||||||
import { WorkWondersSdk } from "workwonders-sdk";
|
import { WorkWonders } from "workwonders-sdk";
|
||||||
import {
|
import {
|
||||||
useAppDispatch,
|
useAppDispatch,
|
||||||
useAppSelector,
|
useAppSelector,
|
||||||
@@ -52,7 +52,7 @@ export function App() {
|
|||||||
|
|
||||||
const { clearDownload, setLastPacket } = useDownload();
|
const { clearDownload, setLastPacket } = useDownload();
|
||||||
|
|
||||||
const workwondersRef = useRef<WorkWondersSdk | null>(null);
|
const workwondersRef = useRef<WorkWonders | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hasActiveSubscription,
|
hasActiveSubscription,
|
||||||
@@ -125,18 +125,18 @@ export function App() {
|
|||||||
const parsedLocale =
|
const parsedLocale =
|
||||||
possibleLocales.find((l) => l === locale?.slice(0, 2)) ?? "en";
|
possibleLocales.find((l) => l === locale?.slice(0, 2)) ?? "en";
|
||||||
|
|
||||||
workwondersRef.current = new WorkWondersSdk();
|
workwondersRef.current = new WorkWonders();
|
||||||
await workwondersRef.current.init({
|
await workwondersRef.current.init({
|
||||||
organization: "hydra",
|
organization: "hydra",
|
||||||
token,
|
token,
|
||||||
locale: parsedLocale,
|
locale: parsedLocale,
|
||||||
});
|
});
|
||||||
|
|
||||||
await workwondersRef.current.initChangelogWidget();
|
await workwondersRef.current.changelog.initChangelogWidget();
|
||||||
workwondersRef.current.initChangelogWidgetMini();
|
workwondersRef.current.changelog.initChangelogWidgetMini();
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
workwondersRef.current.initFeedbackWidget();
|
workwondersRef.current.feedback.initFeedbackWidget();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[workwondersRef]
|
[workwondersRef]
|
||||||
|
|||||||
8
src/renderer/src/declaration.d.ts
vendored
8
src/renderer/src/declaration.d.ts
vendored
@@ -47,11 +47,19 @@ declare global {
|
|||||||
startGameDownload: (
|
startGameDownload: (
|
||||||
payload: StartGameDownloadPayload
|
payload: StartGameDownloadPayload
|
||||||
) => Promise<{ ok: boolean; error?: string }>;
|
) => Promise<{ ok: boolean; error?: string }>;
|
||||||
|
addGameToQueue: (
|
||||||
|
payload: StartGameDownloadPayload
|
||||||
|
) => Promise<{ ok: boolean; error?: string }>;
|
||||||
cancelGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
cancelGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
||||||
pauseGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
pauseGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
||||||
resumeGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
resumeGameDownload: (shop: GameShop, objectId: string) => Promise<void>;
|
||||||
pauseGameSeed: (shop: GameShop, objectId: string) => Promise<void>;
|
pauseGameSeed: (shop: GameShop, objectId: string) => Promise<void>;
|
||||||
resumeGameSeed: (shop: GameShop, objectId: string) => Promise<void>;
|
resumeGameSeed: (shop: GameShop, objectId: string) => Promise<void>;
|
||||||
|
updateDownloadQueuePosition: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
direction: "up" | "down"
|
||||||
|
) => Promise<boolean>;
|
||||||
onDownloadProgress: (
|
onDownloadProgress: (
|
||||||
cb: (value: DownloadProgress | null) => void
|
cb: (value: DownloadProgress | null) => void
|
||||||
) => () => Electron.IpcRenderer;
|
) => () => Electron.IpcRenderer;
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ export function useDownload() {
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addGameToQueue = async (payload: StartGameDownloadPayload) => {
|
||||||
|
const response = await window.electron.addGameToQueue(payload);
|
||||||
|
|
||||||
|
if (response.ok) updateLibrary();
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
const pauseDownload = async (shop: GameShop, objectId: string) => {
|
const pauseDownload = async (shop: GameShop, objectId: string) => {
|
||||||
await window.electron.pauseGameDownload(shop, objectId);
|
await window.electron.pauseGameDownload(shop, objectId);
|
||||||
await updateLibrary();
|
await updateLibrary();
|
||||||
@@ -61,10 +69,16 @@ export function useDownload() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cancelDownload = async (shop: GameShop, objectId: string) => {
|
const cancelDownload = async (shop: GameShop, objectId: string) => {
|
||||||
await window.electron.cancelGameDownload(shop, objectId);
|
const gameId = `${shop}:${objectId}`;
|
||||||
dispatch(clearDownload());
|
const isActiveDownload = lastPacket?.gameId === gameId;
|
||||||
updateLibrary();
|
|
||||||
|
|
||||||
|
await window.electron.cancelGameDownload(shop, objectId);
|
||||||
|
|
||||||
|
if (isActiveDownload) {
|
||||||
|
dispatch(clearDownload());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLibrary();
|
||||||
removeGameInstaller(shop, objectId);
|
removeGameInstaller(shop, objectId);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,6 +127,7 @@ export function useDownload() {
|
|||||||
lastPacket,
|
lastPacket,
|
||||||
eta: calculateETA(),
|
eta: calculateETA(),
|
||||||
startDownload,
|
startDownload,
|
||||||
|
addGameToQueue,
|
||||||
pauseDownload,
|
pauseDownload,
|
||||||
resumeDownload,
|
resumeDownload,
|
||||||
cancelDownload,
|
cancelDownload,
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
} from "@renderer/components/dropdown-menu/dropdown-menu";
|
} from "@renderer/components/dropdown-menu/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
ClockIcon,
|
ClockIcon,
|
||||||
ColumnsIcon,
|
ColumnsIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
@@ -40,6 +42,44 @@ import {
|
|||||||
import { MoreVertical, Folder } from "lucide-react";
|
import { MoreVertical, Folder } from "lucide-react";
|
||||||
import { average } from "color.js";
|
import { average } from "color.js";
|
||||||
|
|
||||||
|
function hexToRgb(hex: string): [number, number, number] {
|
||||||
|
let h = hex.replace("#", "");
|
||||||
|
if (h.length === 3) {
|
||||||
|
h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
||||||
|
}
|
||||||
|
const r = Number.parseInt(h.substring(0, 2), 16) || 0;
|
||||||
|
const g = Number.parseInt(h.substring(2, 4), 16) || 0;
|
||||||
|
const b = Number.parseInt(h.substring(4, 6), 16) || 0;
|
||||||
|
return [r, g, b];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTooCloseRGB(a: string, b: string, threshold: number): boolean {
|
||||||
|
const [r1, g1, b1] = hexToRgb(a);
|
||||||
|
const [r2, g2, b2] = hexToRgb(b);
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2)
|
||||||
|
);
|
||||||
|
return distance < threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CHART_BACKGROUND_COLOR = "#1a1a1a";
|
||||||
|
const COLOR_DISTANCE_THRESHOLD = 28;
|
||||||
|
const FALLBACK_CHART_COLOR = "#fff";
|
||||||
|
|
||||||
|
function pickChartColor(dominant?: string): string {
|
||||||
|
if (!dominant || typeof dominant !== "string" || !dominant.startsWith("#")) {
|
||||||
|
return FALLBACK_CHART_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isTooCloseRGB(dominant, CHART_BACKGROUND_COLOR, COLOR_DISTANCE_THRESHOLD)
|
||||||
|
) {
|
||||||
|
return FALLBACK_CHART_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dominant;
|
||||||
|
}
|
||||||
|
|
||||||
interface AnimatedPercentageProps {
|
interface AnimatedPercentageProps {
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
@@ -442,6 +482,7 @@ export interface DownloadGroupProps {
|
|||||||
openDeleteGameModal: (shop: GameShop, objectId: string) => void;
|
openDeleteGameModal: (shop: GameShop, objectId: string) => void;
|
||||||
openGameInstaller: (shop: GameShop, objectId: string) => void;
|
openGameInstaller: (shop: GameShop, objectId: string) => void;
|
||||||
seedingStatus: SeedingStatus[];
|
seedingStatus: SeedingStatus[];
|
||||||
|
queuedGameIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DownloadGroup({
|
export function DownloadGroup({
|
||||||
@@ -450,6 +491,7 @@ export function DownloadGroup({
|
|||||||
openDeleteGameModal,
|
openDeleteGameModal,
|
||||||
openGameInstaller,
|
openGameInstaller,
|
||||||
seedingStatus,
|
seedingStatus,
|
||||||
|
queuedGameIds = [],
|
||||||
}: Readonly<DownloadGroupProps>) {
|
}: Readonly<DownloadGroupProps>) {
|
||||||
const { t } = useTranslation("downloads");
|
const { t } = useTranslation("downloads");
|
||||||
const { t: tGameDetails } = useTranslation("game_details");
|
const { t: tGameDetails } = useTranslation("game_details");
|
||||||
@@ -690,6 +732,18 @@ export function DownloadGroup({
|
|||||||
setGameToCancelObjectId(null);
|
setGameToCancelObjectId(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleMoveInQueue = useCallback(
|
||||||
|
async (shop: GameShop, objectId: string, direction: "up" | "down") => {
|
||||||
|
await window.electron.updateDownloadQueuePosition(
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
direction
|
||||||
|
);
|
||||||
|
updateLibrary();
|
||||||
|
},
|
||||||
|
[updateLibrary]
|
||||||
|
);
|
||||||
|
|
||||||
const getGameActions = (game: LibraryGame): DropdownMenuItem[] => {
|
const getGameActions = (game: LibraryGame): DropdownMenuItem[] => {
|
||||||
const download = lastPacket?.download;
|
const download = lastPacket?.download;
|
||||||
const isGameDownloading = isGameDownloadingMap[game.id];
|
const isGameDownloading = isGameDownloadingMap[game.id];
|
||||||
@@ -765,7 +819,12 @@ export function DownloadGroup({
|
|||||||
(download?.downloader === Downloader.TorBox &&
|
(download?.downloader === Downloader.TorBox &&
|
||||||
!userPreferences?.torBoxApiToken);
|
!userPreferences?.torBoxApiToken);
|
||||||
|
|
||||||
return [
|
const queueIndex = queuedGameIds.indexOf(game.id);
|
||||||
|
const isFirstInQueue = queueIndex === 0;
|
||||||
|
const isLastInQueue = queueIndex === queuedGameIds.length - 1;
|
||||||
|
const isInQueue = queueIndex !== -1;
|
||||||
|
|
||||||
|
const actions = [
|
||||||
{
|
{
|
||||||
label: t("resume"),
|
label: t("resume"),
|
||||||
disabled: isResumeDisabled,
|
disabled: isResumeDisabled,
|
||||||
@@ -774,6 +833,22 @@ export function DownloadGroup({
|
|||||||
},
|
},
|
||||||
icon: <PlayIcon />,
|
icon: <PlayIcon />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t("move_up"),
|
||||||
|
show: isInQueue && !isFirstInQueue,
|
||||||
|
onClick: () => {
|
||||||
|
handleMoveInQueue(game.shop, game.objectId, "up");
|
||||||
|
},
|
||||||
|
icon: <ArrowUpIcon />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("move_down"),
|
||||||
|
show: isInQueue && !isLastInQueue,
|
||||||
|
onClick: () => {
|
||||||
|
handleMoveInQueue(game.shop, game.objectId, "down");
|
||||||
|
},
|
||||||
|
icon: <ArrowDownIcon />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t("cancel"),
|
label: t("cancel"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -782,6 +857,8 @@ export function DownloadGroup({
|
|||||||
icon: <XCircleIcon />,
|
icon: <XCircleIcon />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return actions.filter((action) => action.show !== false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadInfo = useMemo(
|
const downloadInfo = useMemo(
|
||||||
@@ -863,7 +940,7 @@ export function DownloadGroup({
|
|||||||
currentProgress = lastPacket.progress;
|
currentProgress = lastPacket.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dominantColor = dominantColors[game.id] || "#fff";
|
const dominantColor = pickChartColor(dominantColors[game.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -103,18 +103,26 @@ export default function Downloads() {
|
|||||||
};
|
};
|
||||||
}, [library, lastPacket?.gameId, extraction?.visibleId]);
|
}, [library, lastPacket?.gameId, extraction?.visibleId]);
|
||||||
|
|
||||||
|
const queuedGameIds = useMemo(
|
||||||
|
() => libraryGroup.queued.map((game) => game.id),
|
||||||
|
[libraryGroup.queued]
|
||||||
|
);
|
||||||
|
|
||||||
const downloadGroups = [
|
const downloadGroups = [
|
||||||
{
|
{
|
||||||
title: t("download_in_progress"),
|
title: t("download_in_progress"),
|
||||||
library: libraryGroup.downloading,
|
library: libraryGroup.downloading,
|
||||||
|
queuedGameIds: [] as string[],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("queued_downloads"),
|
title: t("queued_downloads"),
|
||||||
library: libraryGroup.queued,
|
library: libraryGroup.queued,
|
||||||
|
queuedGameIds,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("downloads_completed"),
|
title: t("downloads_completed"),
|
||||||
library: libraryGroup.complete,
|
library: libraryGroup.complete,
|
||||||
|
queuedGameIds: [] as string[],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -142,10 +150,11 @@ export default function Downloads() {
|
|||||||
<DownloadGroup
|
<DownloadGroup
|
||||||
key={group.title}
|
key={group.title}
|
||||||
title={group.title}
|
title={group.title}
|
||||||
library={orderBy(group.library, ["updatedAt"], ["desc"])}
|
library={group.library}
|
||||||
openDeleteGameModal={handleOpenDeleteGameModal}
|
openDeleteGameModal={handleOpenDeleteGameModal}
|
||||||
openGameInstaller={handleOpenGameInstaller}
|
openGameInstaller={handleOpenGameInstaller}
|
||||||
seedingStatus={seedingStatus}
|
seedingStatus={seedingStatus}
|
||||||
|
queuedGameIds={group.queuedGameIds}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function GameDetails() {
|
|||||||
const fromRandomizer = searchParams.get("fromRandomizer");
|
const fromRandomizer = searchParams.get("fromRandomizer");
|
||||||
const gameTitle = searchParams.get("title");
|
const gameTitle = searchParams.get("title");
|
||||||
|
|
||||||
const { startDownload } = useDownload();
|
const { startDownload, addGameToQueue } = useDownload();
|
||||||
|
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
@@ -100,17 +100,30 @@ export default function GameDetails() {
|
|||||||
repack: GameRepack,
|
repack: GameRepack,
|
||||||
downloader: Downloader,
|
downloader: Downloader,
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
automaticallyExtract: boolean
|
automaticallyExtract: boolean,
|
||||||
|
addToQueueOnly = false
|
||||||
) => {
|
) => {
|
||||||
const response = await startDownload({
|
const response = addToQueueOnly
|
||||||
objectId: objectId!,
|
? await addGameToQueue({
|
||||||
title: gameTitle,
|
objectId: objectId!,
|
||||||
downloader,
|
title: gameTitle,
|
||||||
shop,
|
downloader,
|
||||||
downloadPath,
|
shop,
|
||||||
uri: selectRepackUri(repack, downloader),
|
downloadPath,
|
||||||
automaticallyExtract: automaticallyExtract,
|
uri: selectRepackUri(repack, downloader),
|
||||||
});
|
automaticallyExtract: automaticallyExtract,
|
||||||
|
fileSize: repack.fileSize,
|
||||||
|
})
|
||||||
|
: await startDownload({
|
||||||
|
objectId: objectId!,
|
||||||
|
title: gameTitle,
|
||||||
|
downloader,
|
||||||
|
shop,
|
||||||
|
downloadPath,
|
||||||
|
uri: selectRepackUri(repack, downloader),
|
||||||
|
automaticallyExtract: automaticallyExtract,
|
||||||
|
fileSize: repack.fileSize,
|
||||||
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
await updateGame();
|
await updateGame();
|
||||||
|
|||||||
@@ -12,11 +12,17 @@ import {
|
|||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
SyncIcon,
|
SyncIcon,
|
||||||
CheckCircleFillIcon,
|
CheckCircleFillIcon,
|
||||||
|
PlusIcon,
|
||||||
} from "@primer/octicons-react";
|
} from "@primer/octicons-react";
|
||||||
import { Downloader, formatBytes, getDownloadersForUri } from "@shared";
|
import { Downloader, formatBytes, getDownloadersForUri } from "@shared";
|
||||||
import type { GameRepack } from "@types";
|
import type { GameRepack } from "@types";
|
||||||
import { DOWNLOADER_NAME } from "@renderer/constants";
|
import { DOWNLOADER_NAME } from "@renderer/constants";
|
||||||
import { useAppSelector, useFeature, useToast } from "@renderer/hooks";
|
import {
|
||||||
|
useAppSelector,
|
||||||
|
useDownload,
|
||||||
|
useFeature,
|
||||||
|
useToast,
|
||||||
|
} from "@renderer/hooks";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { RealDebridInfoModal } from "./real-debrid-info-modal";
|
import { RealDebridInfoModal } from "./real-debrid-info-modal";
|
||||||
@@ -29,7 +35,8 @@ export interface DownloadSettingsModalProps {
|
|||||||
repack: GameRepack,
|
repack: GameRepack,
|
||||||
downloader: Downloader,
|
downloader: Downloader,
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
automaticallyExtract: boolean
|
automaticallyExtract: boolean,
|
||||||
|
addToQueueOnly?: boolean
|
||||||
) => Promise<{ ok: boolean; error?: string }>;
|
) => Promise<{ ok: boolean; error?: string }>;
|
||||||
repack: GameRepack | null;
|
repack: GameRepack | null;
|
||||||
}
|
}
|
||||||
@@ -46,8 +53,11 @@ export function DownloadSettingsModal({
|
|||||||
(state) => state.userPreferences.value
|
(state) => state.userPreferences.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { lastPacket } = useDownload();
|
||||||
const { showErrorToast } = useToast();
|
const { showErrorToast } = useToast();
|
||||||
|
|
||||||
|
const hasActiveDownload = lastPacket !== null;
|
||||||
|
|
||||||
const [diskFreeSpace, setDiskFreeSpace] = useState<number | null>(null);
|
const [diskFreeSpace, setDiskFreeSpace] = useState<number | null>(null);
|
||||||
const [selectedPath, setSelectedPath] = useState("");
|
const [selectedPath, setSelectedPath] = useState("");
|
||||||
const [downloadStarting, setDownloadStarting] = useState(false);
|
const [downloadStarting, setDownloadStarting] = useState(false);
|
||||||
@@ -220,7 +230,8 @@ export function DownloadSettingsModal({
|
|||||||
repack,
|
repack,
|
||||||
selectedDownloader!,
|
selectedDownloader!,
|
||||||
selectedPath,
|
selectedPath,
|
||||||
automaticExtractionEnabled
|
automaticExtractionEnabled,
|
||||||
|
hasActiveDownload
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@@ -456,6 +467,11 @@ export function DownloadSettingsModal({
|
|||||||
<SyncIcon className="download-settings-modal__loading-spinner" />
|
<SyncIcon className="download-settings-modal__loading-spinner" />
|
||||||
{t("loading")}
|
{t("loading")}
|
||||||
</>
|
</>
|
||||||
|
) : hasActiveDownload ? (
|
||||||
|
<>
|
||||||
|
<PlusIcon />
|
||||||
|
{t("add_to_queue")}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export interface RepacksModalProps {
|
|||||||
repack: GameRepack,
|
repack: GameRepack,
|
||||||
downloader: Downloader,
|
downloader: Downloader,
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
automaticallyExtract: boolean
|
automaticallyExtract: boolean,
|
||||||
|
addToQueueOnly?: boolean
|
||||||
) => Promise<{ ok: boolean; error?: string }>;
|
) => Promise<{ ok: boolean; error?: string }>;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,25 @@ export const formatBytes = (bytes: number): string => {
|
|||||||
return `${Math.trunc(formatedByte * 10) / 10} ${FORMAT[base]}`;
|
return `${Math.trunc(formatedByte * 10) / 10} ${FORMAT[base]}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const parseBytes = (sizeString: string | null): number | null => {
|
||||||
|
if (!sizeString) return null;
|
||||||
|
|
||||||
|
const regex = /^([\d.,]+)\s*([A-Za-z]+)$/;
|
||||||
|
const match = regex.exec(sizeString.trim());
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
const value = Number.parseFloat(match[1].replaceAll(",", "."));
|
||||||
|
const unit = match[2].toUpperCase();
|
||||||
|
|
||||||
|
if (Number.isNaN(value)) return null;
|
||||||
|
|
||||||
|
const unitIndex = FORMAT.indexOf(unit);
|
||||||
|
if (unitIndex === -1) return null;
|
||||||
|
|
||||||
|
const byteKBase = 1024;
|
||||||
|
return Math.round(value * Math.pow(byteKBase, unitIndex));
|
||||||
|
};
|
||||||
|
|
||||||
export const formatBytesToMbps = (bytesPerSecond: number): string => {
|
export const formatBytesToMbps = (bytesPerSecond: number): string => {
|
||||||
const bitsPerSecond = bytesPerSecond * 8;
|
const bitsPerSecond = bytesPerSecond * 8;
|
||||||
const mbps = bitsPerSecond / (1024 * 1024);
|
const mbps = bitsPerSecond / (1024 * 1024);
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ export interface StartGameDownloadPayload {
|
|||||||
downloadPath: string;
|
downloadPath: string;
|
||||||
downloader: Downloader;
|
downloader: Downloader;
|
||||||
automaticallyExtract: boolean;
|
automaticallyExtract: boolean;
|
||||||
|
fileSize?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserFriend {
|
export interface UserFriend {
|
||||||
|
|||||||
26
yarn.lock
26
yarn.lock
@@ -6464,10 +6464,10 @@ keyv@^4.0.0, keyv@^4.5.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
json-buffer "3.0.1"
|
json-buffer "3.0.1"
|
||||||
|
|
||||||
ky@^1.11.0:
|
ky@^1.14.2:
|
||||||
version "1.14.1"
|
version "1.14.2"
|
||||||
resolved "https://registry.yarnpkg.com/ky/-/ky-1.14.1.tgz#16f20b3bf3939abcc04e2a9613f47360fe5f64c9"
|
resolved "https://registry.yarnpkg.com/ky/-/ky-1.14.2.tgz#385d6d05d2825502e68898ace125124e6fe7357d"
|
||||||
integrity sha512-hYje4L9JCmpEQBtudo+v52X5X8tgWXUYyPcxKSuxQNboqufecl9VMWjGiucAFH060AwPXHZuH+WB2rrqfkmafw==
|
integrity sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==
|
||||||
|
|
||||||
language-subtag-registry@^0.3.20:
|
language-subtag-registry@^0.3.20:
|
||||||
version "0.3.23"
|
version "0.3.23"
|
||||||
@@ -8681,10 +8681,10 @@ tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.2.1:
|
|||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
tar@^7.5.2:
|
tar@^7.5.4:
|
||||||
version "7.5.2"
|
version "7.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.2.tgz#115c061495ec51ff3c6745ff8f6d0871c5b1dedc"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.4.tgz#18b53b44f939a7e03ed874f1fafe17d29e306c81"
|
||||||
integrity sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==
|
integrity sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@isaacs/fs-minipass" "^4.0.0"
|
"@isaacs/fs-minipass" "^4.0.0"
|
||||||
chownr "^3.0.0"
|
chownr "^3.0.0"
|
||||||
@@ -9238,12 +9238,12 @@ word-wrap@^1.2.5:
|
|||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||||
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
||||||
|
|
||||||
workwonders-sdk@0.0.10:
|
workwonders-sdk@0.1.1:
|
||||||
version "0.0.10"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/workwonders-sdk/-/workwonders-sdk-0.0.10.tgz#377167370a39c905c5228f8972c37c19004b7b21"
|
resolved "https://registry.yarnpkg.com/workwonders-sdk/-/workwonders-sdk-0.1.1.tgz#7ac0eb3d9ef0a5a8cc5ca4e6f5e387e29875faa9"
|
||||||
integrity sha512-bnswhlLRz1TCiqGV8l+VEOBej7u1SAkzLMEv6A60Sp0+S4j4pnmSve92KeOts/GYtUeNDuNM7fLPwZwMKY3sAg==
|
integrity sha512-PEsl33QCeiBlYed/MmnX1unnd4Kn7vzVIza00HQ/5Zsan89nqnwWx9vqgJnNipXkkmIWl8oDL9bGRNjtL4XZ4Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
ky "^1.11.0"
|
ky "^1.14.2"
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user