Compare commits
	
		
			11 Commits
		
	
	
		
			0efc3a8701
			...
			e57770f796
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e57770f796 | |||
| 49cd77ee38 | |||
| d26b9774a0 | |||
| 9a27d67dc0 | |||
| b0fff5af0c | |||
| e54fac8aa4 | |||
| f111674260 | |||
| a5df7f0477 | |||
| f2954497d9 | |||
| 80bbab692d | |||
| 731e919884 | 
| @@ -1,6 +1,6 @@ | ||||
| # See https://pre-commit.com for more information | ||||
| # See https://pre-commit.com/hooks.html for more hooks | ||||
| exclude: '(data/|documentation/|portprotonqt/locales/|dev-scripts/|\.venv/|venv/|.*\.svg$)' | ||||
| exclude: '(data/|documentation/|portprotonqt/locales/|portprotonqt/custom_data/|dev-scripts/|\.venv/|venv/|.*\.svg$)' | ||||
| repos: | ||||
|   - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: v5.0.0 | ||||
| @@ -27,8 +27,9 @@ repos: | ||||
|         name: pyright | ||||
|         entry: pyright | ||||
|         language: system | ||||
|         'types_or': [python, pyi] | ||||
|         types_or: [python, pyi] | ||||
|         require_serial: true | ||||
|         exclude: '^portprotonqt/themes/[^/]+/styles\.py$' | ||||
|  | ||||
|   - repo: local | ||||
|     hooks: | ||||
| @@ -37,5 +38,5 @@ repos: | ||||
|         entry: ./dev-scripts/check_qss_properties.py | ||||
|         language: system | ||||
|         types: [file] | ||||
|         files: \.py$ | ||||
|         files: ^portprotonqt/themes/[^/]+/styles\.py$ | ||||
|         pass_filenames: false | ||||
|   | ||||
| @@ -6,15 +6,16 @@ | ||||
| ## [Unreleased] | ||||
|  | ||||
| ### Added | ||||
|  | ||||
| - Переводы в переопределениях (за подробностями в документацию) | ||||
| - Обложки и описания для всех автоинсталлов | ||||
| - Возможность указать ссылку для скачивания обложки в диалоге добавления игры | ||||
|  | ||||
| ### Changed | ||||
| - Оптимизированны обложки автоинсталлов | ||||
|  | ||||
| - Папка custom_data исключена из сборки модуля для уменьшение его размера | ||||
|  | ||||
| ### Fixed | ||||
|  | ||||
|  | ||||
| ### Contributors | ||||
| - @Vector_null | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ | ||||
| - [ ] Достигнуть паритета функциональности с PortProton | ||||
| - [X] Добавить возможность изменения названия, описания и обложки через файлы `.local/share/PortProtonQT/custom_data/exe_name/{desc,name,cover}` | ||||
| - [X] Добавить встроенное переопределение названия, описания и обложки, например, по пути `portprotonqt/custom_data` [Документация](documentation/metadata_override/) | ||||
| - [ ] Добавить переводы в переопределения | ||||
| - [X] Добавить переводы в переопределения | ||||
| - [ ] Придумать как переопределять launcher.exe | ||||
| - [X] Добавить в карточку игры сведения о поддержке геймпада | ||||
| - [X] Добавить в карточки данные с ProtonDB | ||||
|   | ||||
| @@ -20,9 +20,9 @@ Current translation status: | ||||
|  | ||||
| | Locale | Progress | Translated | | ||||
| | :----- | -------: | ---------: | | ||||
| | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 192 | | ||||
| | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 192 | | ||||
| | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 192 of 192 | | ||||
| | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 194 | | ||||
| | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 194 | | ||||
| | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 194 of 194 | | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
| @@ -20,9 +20,9 @@ | ||||
|  | ||||
| | Локаль | Прогресс | Переведено | | ||||
| | :----- | -------: | ---------: | | ||||
| | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 192 | | ||||
| | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 192 | | ||||
| | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 192 из 192 | | ||||
| | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 194 | | ||||
| | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 194 | | ||||
| | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 194 из 194 | | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
| @@ -50,7 +50,9 @@ Each `<exe_name>` folder can include: | ||||
| - `metadata.txt` — contains name and description: | ||||
|   ```txt | ||||
|   name=My Game Title | ||||
|   name_ru=My Game Title (in russian language) | ||||
|   description=My Game Description | ||||
|   description_ru=My Game Description (in russian language) | ||||
|   ``` | ||||
| - `cover.<extension>` — image file (`.png`, `.jpg`, `.jpeg`, `.bmp`) | ||||
|  | ||||
|   | ||||
| @@ -50,7 +50,9 @@ | ||||
| - `metadata.txt` — имя и описание в формате: | ||||
|   ```txt | ||||
|   name=Моё название игры | ||||
|   description=Описание моей игры | ||||
|   name_en=Моё название игры (на английском) | ||||
|   description=Описание моей игры  (на английском) | ||||
|   description_en=Описание моей игры | ||||
|   ``` | ||||
| - `cover.<расширение>` — обложка (`.png`, `.jpg`, `.jpeg`, `.bmp`) | ||||
|  | ||||
|   | ||||
| Before Width: | Height: | Size: 720 KiB After Width: | Height: | Size: 720 KiB | 
| Before Width: | Height: | Size: 655 KiB After Width: | Height: | Size: 655 KiB | 
| Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 315 KiB | 
| Before Width: | Height: | Size: 978 KiB After Width: | Height: | Size: 978 KiB | 
| Before Width: | Height: | Size: 650 KiB After Width: | Height: | Size: 650 KiB | 
| Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 391 KiB | 
| Before Width: | Height: | Size: 710 KiB After Width: | Height: | Size: 710 KiB | 
| Before Width: | Height: | Size: 670 KiB After Width: | Height: | Size: 670 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 814 KiB After Width: | Height: | Size: 814 KiB | 
| Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 566 KiB | 
| Before Width: | Height: | Size: 895 KiB After Width: | Height: | Size: 895 KiB | 
| Before Width: | Height: | Size: 627 KiB After Width: | Height: | Size: 627 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB | 
| Before Width: | Height: | Size: 722 KiB After Width: | Height: | Size: 722 KiB | 
| @@ -1,5 +1,6 @@ | ||||
| import os | ||||
| import tempfile | ||||
| import re | ||||
| from typing import cast, TYPE_CHECKING | ||||
| from PySide6.QtGui import QPixmap, QIcon | ||||
| from PySide6.QtWidgets import ( | ||||
| @@ -14,6 +15,7 @@ from portprotonqt.logger import get_logger | ||||
| import portprotonqt.themes.standart.styles as default_styles | ||||
| from portprotonqt.theme_manager import ThemeManager | ||||
| from portprotonqt.custom_widgets import AutoSizeButton | ||||
| from portprotonqt.downloader import Downloader | ||||
|  | ||||
| if TYPE_CHECKING: | ||||
|     from portprotonqt.main_window import MainWindow | ||||
| @@ -449,6 +451,7 @@ class AddGameDialog(QDialog): | ||||
|         self.original_name = game_name | ||||
|         self.last_exe_path = exe_path  # Store last selected exe path | ||||
|         self.last_cover_path = cover_path  # Store last selected cover path | ||||
|         self.downloader = Downloader(max_workers=4)  # Initialize Downloader | ||||
|  | ||||
|         self.setWindowTitle(_("Edit Game") if edit_mode else _("Add Game")) | ||||
|         self.setModal(True) | ||||
| @@ -472,8 +475,7 @@ class AddGameDialog(QDialog): | ||||
|  | ||||
|         # Exe path | ||||
|         exe_label = QLabel(_("Path to Executable:")) | ||||
|         exe_label.setStyleSheet( | ||||
|             self.theme.PARAMS_TITLE_STYLE) | ||||
|         exe_label.setStyleSheet(self.theme.PARAMS_TITLE_STYLE) | ||||
|  | ||||
|         self.exeEdit = CustomLineEdit(self, theme=self.theme) | ||||
|         self.exeEdit.setStyleSheet(self.theme.ADDGAME_INPUT_STYLE) | ||||
| @@ -550,7 +552,7 @@ class AddGameDialog(QDialog): | ||||
|             exeBrowseButton.setFixedWidth(self.exeEdit.width()) | ||||
|             coverBrowseButton.setFixedWidth(self.coverEdit.width()) | ||||
|  | ||||
|         # Вызываем после отображения окна, когда размеры установлены, чтобы реально дождаться, когда всё сформируется | ||||
|         # Вызываем после отображения окна, когда размеры установлены | ||||
|         QTimer.singleShot(0, update_button_widths) | ||||
|  | ||||
|         # Обновляем превью, если в режиме редактирования | ||||
| @@ -615,15 +617,46 @@ class AddGameDialog(QDialog): | ||||
|         """Обработчик выбора файла обложки в FileExplorer""" | ||||
|         if file_path and os.path.splitext(file_path)[1].lower() in ('.png', '.jpg', '.jpeg', '.bmp'): | ||||
|             self.coverEdit.setText(file_path) | ||||
|             self.last_cover_path = file_path  # Update last selected cover path | ||||
|             self.last_cover_path = file_path | ||||
|             self.updatePreview() | ||||
|         else: | ||||
|             logger.warning(f"Selected file is not a valid image: {file_path}") | ||||
|  | ||||
|     def handleDownloadedCover(self, file_path): | ||||
|         """Handle the downloaded cover image and update the preview.""" | ||||
|         if file_path and os.path.isfile(file_path): | ||||
|             self.last_cover_path = file_path | ||||
|             pixmap = QPixmap(file_path) | ||||
|             if not pixmap.isNull(): | ||||
|                 self.coverPreview.setPixmap(pixmap.scaled(250, 250, Qt.AspectRatioMode.KeepAspectRatio)) | ||||
|             else: | ||||
|                 self.coverPreview.setText(_("Invalid image")) | ||||
|         else: | ||||
|             self.coverPreview.setText(_("Failed to download cover")) | ||||
|             logger.warning(f"Failed to download cover to {file_path}") | ||||
|  | ||||
|     def updatePreview(self): | ||||
|         """Update the cover preview image.""" | ||||
|         cover_path = self.coverEdit.text().strip() | ||||
|         exe_path = self.exeEdit.text().strip() | ||||
|         if cover_path and os.path.isfile(cover_path): | ||||
|  | ||||
|         # Check if cover_path is a URL | ||||
|         url_pattern = r'^https?://[^\s/$.?#].[^\s]*$' | ||||
|         if re.match(url_pattern, cover_path): | ||||
|             # Create a temporary file for the downloaded image | ||||
|             fd, local_path = tempfile.mkstemp(suffix=".png") | ||||
|             os.close(fd) | ||||
|             os.unlink(local_path) | ||||
|  | ||||
|             # Start asynchronous download | ||||
|             self.downloader.download_async( | ||||
|                 url=cover_path, | ||||
|                 local_path=local_path, | ||||
|                 timeout=10, | ||||
|                 callback=self.handleDownloadedCover | ||||
|             ) | ||||
|             self.coverPreview.setText(_("Downloading cover...")) | ||||
|         elif cover_path and os.path.isfile(cover_path): | ||||
|             pixmap = QPixmap(cover_path) | ||||
|             if not pixmap.isNull(): | ||||
|                 self.coverPreview.setPixmap(pixmap.scaled(250, 250, Qt.AspectRatioMode.KeepAspectRatio)) | ||||
| @@ -666,8 +699,8 @@ class AddGameDialog(QDialog): | ||||
|  | ||||
|         os.makedirs(os.path.dirname(icon_path), exist_ok=True) | ||||
|  | ||||
|         # Generate thumbnail (128x128) from exe | ||||
|         if not generate_thumbnail(exe_path, icon_path, size=128): | ||||
|         # Generate thumbnail (128x128) from exe if no cover is provided | ||||
|         if not self.last_cover_path and not generate_thumbnail(exe_path, icon_path, size=128): | ||||
|             logger.error(f"Failed to generate thumbnail from exe: {exe_path}") | ||||
|             icon_path = ""  # Set empty icon if generation fails | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: PROJECT VERSION\n" | ||||
| "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
| "POT-Creation-Date: 2025-07-03 19:29+0700\n" | ||||
| "POT-Creation-Date: 2025-07-06 17:56+0500\n" | ||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
| "Language: de_DE\n" | ||||
| @@ -296,6 +296,12 @@ msgstr "" | ||||
| msgid "Invalid image" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Failed to download cover" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Downloading cover..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "No cover selected" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -338,6 +344,9 @@ msgstr "" | ||||
| msgid "Pending" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Library" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -362,9 +371,6 @@ msgstr "" | ||||
| msgid "Loading PortProton games..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Game Library" | ||||
| msgstr "" | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: PROJECT VERSION\n" | ||||
| "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
| "POT-Creation-Date: 2025-07-03 19:29+0700\n" | ||||
| "POT-Creation-Date: 2025-07-06 17:56+0500\n" | ||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
| "Language: es_ES\n" | ||||
| @@ -296,6 +296,12 @@ msgstr "" | ||||
| msgid "Invalid image" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Failed to download cover" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Downloading cover..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "No cover selected" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -338,6 +344,9 @@ msgstr "" | ||||
| msgid "Pending" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Library" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -362,9 +371,6 @@ msgstr "" | ||||
| msgid "Loading PortProton games..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Game Library" | ||||
| msgstr "" | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: PortProtonQt 0.1.1\n" | ||||
| "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
| "POT-Creation-Date: 2025-07-03 19:29+0700\n" | ||||
| "POT-Creation-Date: 2025-07-06 17:56+0500\n" | ||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
| @@ -294,6 +294,12 @@ msgstr "" | ||||
| msgid "Invalid image" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Failed to download cover" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Downloading cover..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "No cover selected" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -336,6 +342,9 @@ msgstr "" | ||||
| msgid "Pending" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Library" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -360,9 +369,6 @@ msgstr "" | ||||
| msgid "Loading PortProton games..." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Game Library" | ||||
| msgstr "" | ||||
|  | ||||
|   | ||||
| @@ -9,8 +9,8 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: PROJECT VERSION\n" | ||||
| "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
| "POT-Creation-Date: 2025-07-03 19:29+0700\n" | ||||
| "PO-Revision-Date: 2025-07-03 19:28+0700\n" | ||||
| "POT-Creation-Date: 2025-07-06 17:56+0500\n" | ||||
| "PO-Revision-Date: 2025-07-06 17:56+0500\n" | ||||
| "Last-Translator: \n" | ||||
| "Language: ru_RU\n" | ||||
| "Language-Team: ru_RU <LL@li.org>\n" | ||||
| @@ -303,6 +303,12 @@ msgstr "Применить" | ||||
| msgid "Invalid image" | ||||
| msgstr "Недопустимое изображение" | ||||
|  | ||||
| msgid "Failed to download cover" | ||||
| msgstr "Не удалось скачать обложку" | ||||
|  | ||||
| msgid "Downloading cover..." | ||||
| msgstr "Скачивание обложки..." | ||||
|  | ||||
| msgid "No cover selected" | ||||
| msgstr "Обложка не выбрана" | ||||
|  | ||||
| @@ -345,6 +351,9 @@ msgstr "Бронза" | ||||
| msgid "Pending" | ||||
| msgstr "В ожидании" | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "Неизвестная игра" | ||||
|  | ||||
| msgid "Library" | ||||
| msgstr "Библиотека" | ||||
|  | ||||
| @@ -369,9 +378,6 @@ msgstr "Загрузка игр из Steam..." | ||||
| msgid "Loading PortProton games..." | ||||
| msgstr "Загрузка игр из PortProton..." | ||||
|  | ||||
| msgid "Unknown Game" | ||||
| msgstr "Неизвестная игра" | ||||
|  | ||||
| msgid "Game Library" | ||||
| msgstr "Игровая библиотека" | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import gettext | ||||
| from pathlib import Path | ||||
| import locale | ||||
| import os | ||||
| from babel import Locale | ||||
|  | ||||
| LOCALE_MAP = { | ||||
| @@ -72,3 +73,32 @@ def get_egs_language(): | ||||
|  | ||||
|     # Если что-то пошло не так — используем английский по умолчанию | ||||
|     return 'en' | ||||
|  | ||||
| def read_metadata_translations(metadata_file, language_code): | ||||
|     """ | ||||
|     Читает переводы из metadata.txt для указанного языка. | ||||
|     Возвращает словарь с полями name и description. | ||||
|     Для name: использует name_<language_code>, затем name_en, затем name, и наконец _('Unknown Game'). | ||||
|     Для description: использует description_<language_code>, затем description_en, затем description. | ||||
|     """ | ||||
|     translations = {'name': _('Unknown Game'), 'description': ''} | ||||
|     if not os.path.exists(metadata_file): | ||||
|         return translations | ||||
|  | ||||
|     with open(metadata_file, encoding='utf-8') as f: | ||||
|         for line in f: | ||||
|             line = line.strip() | ||||
|             if line.startswith(f'name_{language_code}='): | ||||
|                 translations['name'] = line[len(f'name_{language_code}='):].strip() | ||||
|             elif line.startswith('name_en=') and translations['name'] == _('Unknown Game'): | ||||
|                 translations['name'] = line[len('name_en='):].strip() | ||||
|             elif line.startswith('name=') and translations['name'] == _('Unknown Game'): | ||||
|                 translations['name'] = line[len('name='):].strip() | ||||
|             elif line.startswith(f'description_{language_code}='): | ||||
|                 translations['description'] = line[len(f'description_{language_code}='):].strip() | ||||
|             elif line.startswith('description_en=') and not translations['description']: | ||||
|                 translations['description'] = line[len('description_en='):].strip() | ||||
|             elif line.startswith('description=') and not translations['description']: | ||||
|                 translations['description'] = line[len('description='):].strip() | ||||
|  | ||||
|     return translations | ||||
|   | ||||
| @@ -28,7 +28,7 @@ from portprotonqt.config_utils import ( | ||||
|     save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config, | ||||
|     clear_cache, read_auto_fullscreen_gamepad, save_auto_fullscreen_gamepad, read_rumble_config, save_rumble_config | ||||
| ) | ||||
| from portprotonqt.localization import _ | ||||
| from portprotonqt.localization import _, get_egs_language, read_metadata_translations | ||||
| from portprotonqt.logger import get_logger | ||||
| from portprotonqt.downloader import Downloader | ||||
|  | ||||
| @@ -465,11 +465,9 @@ class MainWindow(QMainWindow): | ||||
|         os.makedirs(user_custom_folder, exist_ok=True) | ||||
|  | ||||
|         builtin_cover = "" | ||||
|         builtin_name = None | ||||
|         builtin_desc = None | ||||
|         user_cover = "" | ||||
|         user_name = None | ||||
|         user_desc = None | ||||
|         user_game_folder="" | ||||
|         builtin_game_folder="" | ||||
|  | ||||
|         if game_exe: | ||||
|             exe_name = os.path.splitext(os.path.basename(game_exe))[0] | ||||
| @@ -477,6 +475,7 @@ class MainWindow(QMainWindow): | ||||
|             user_game_folder = os.path.join(user_custom_folder, exe_name) | ||||
|             os.makedirs(user_game_folder, exist_ok=True) | ||||
|  | ||||
|             # Чтение обложки | ||||
|             builtin_files = set(os.listdir(builtin_game_folder)) if os.path.exists(builtin_game_folder) else set() | ||||
|             for ext in [".jpg", ".png", ".jpeg", ".bmp"]: | ||||
|                 candidate = f"cover{ext}" | ||||
| @@ -484,16 +483,6 @@ class MainWindow(QMainWindow): | ||||
|                     builtin_cover = os.path.join(builtin_game_folder, candidate) | ||||
|                     break | ||||
|  | ||||
|             builtin_metadata_file = os.path.join(builtin_game_folder, "metadata.txt") | ||||
|             if os.path.exists(builtin_metadata_file): | ||||
|                 with open(builtin_metadata_file, encoding="utf-8") as f: | ||||
|                     for line in f: | ||||
|                         line = line.strip() | ||||
|                         if line.startswith("name="): | ||||
|                             builtin_name = line[len("name="):].strip() | ||||
|                         elif line.startswith("description="): | ||||
|                             builtin_desc = line[len("description="):].strip() | ||||
|  | ||||
|             user_files = set(os.listdir(user_game_folder)) if os.path.exists(user_game_folder) else set() | ||||
|             for ext in [".jpg", ".png", ".jpeg", ".bmp"]: | ||||
|                 candidate = f"cover{ext}" | ||||
| @@ -501,16 +490,7 @@ class MainWindow(QMainWindow): | ||||
|                     user_cover = os.path.join(user_game_folder, candidate) | ||||
|                     break | ||||
|  | ||||
|             user_metadata_file = os.path.join(user_game_folder, "metadata.txt") | ||||
|             if os.path.exists(user_metadata_file): | ||||
|                 with open(user_metadata_file, encoding="utf-8") as f: | ||||
|                     for line in f: | ||||
|                         line = line.strip() | ||||
|                         if line.startswith("name="): | ||||
|                             user_name = line[len("name="):].strip() | ||||
|                         elif line.startswith("description="): | ||||
|                             user_desc = line[len("description="):].strip() | ||||
|  | ||||
|             # Чтение статистики | ||||
|             if self.portproton_location: | ||||
|                 statistics_file = os.path.join(self.portproton_location, "data", "tmp", "statistics") | ||||
|                 try: | ||||
| @@ -526,13 +506,26 @@ class MainWindow(QMainWindow): | ||||
|                     print(f"Failed to parse playtime data: {e}") | ||||
|  | ||||
|         def on_steam_info(steam_info: dict): | ||||
|             final_name = user_name or builtin_name or desktop_name | ||||
|             final_desc = (user_desc if user_desc is not None else | ||||
|                         builtin_desc if builtin_desc is not None else | ||||
|                         steam_info.get("description", "")) | ||||
|             # Определяем текущий язык | ||||
|             language_code = get_egs_language() | ||||
|  | ||||
|             # Чтение переводов из metadata.txt | ||||
|             user_metadata_file = os.path.join(user_game_folder, "metadata.txt") | ||||
|             builtin_metadata_file = os.path.join(builtin_game_folder, "metadata.txt") | ||||
|  | ||||
|             # Сначала пытаемся загрузить пользовательские переводы | ||||
|             translations = {'name': desktop_name, 'description': ''} | ||||
|             if os.path.exists(user_metadata_file): | ||||
|                 translations = read_metadata_translations(user_metadata_file, language_code) | ||||
|             elif os.path.exists(builtin_metadata_file): | ||||
|                 translations = read_metadata_translations(builtin_metadata_file, language_code) | ||||
|  | ||||
|             final_name = translations['name'] | ||||
|             final_desc = translations['description'] or steam_info.get("description", "") | ||||
|             final_cover = (user_cover if user_cover else | ||||
|                         builtin_cover if builtin_cover else | ||||
|                         steam_info.get("cover", "") or entry.get("Icon", "")) | ||||
|  | ||||
|             callback(( | ||||
|                 final_name, | ||||
|                 final_desc, | ||||
|   | ||||
| @@ -44,7 +44,7 @@ dependencies = [ | ||||
| portprotonqt = "portprotonqt.app:main" | ||||
|  | ||||
| [tool.setuptools.package-data] | ||||
| "portprotonqt" = ["themes/**/*", "locales/**/*", "custom_data/**/*"] | ||||
| "portprotonqt" = ["themes/**/*", "locales/**/*"] | ||||
|  | ||||
| [tool.setuptools.packages.find] | ||||
| exclude = ["build-aux", "dev-scripts", "documentation", "data"] | ||||
|   | ||||