11 Commits

Author SHA1 Message Date
e57770f796 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:58:30 +05:00
49cd77ee38 chore(localization): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:56:57 +05:00
d26b9774a0 feat(add_game): download cover if link is provided
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:54:53 +05:00
9a27d67dc0 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:14:29 +05:00
b0fff5af0c ci(pre-commit): exclude QSS themes from pyright and target them in qss check
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:11:35 +05:00
e54fac8aa4 feat: exclude custom_data from package
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:08:40 +05:00
f111674260 feat: rename launchers custom_data
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 17:05:07 +05:00
a5df7f0477 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 13:19:13 +05:00
f2954497d9 chore(readme): update todo
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 13:18:04 +05:00
80bbab692d chore(documentation): mention localization in custom data
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 13:17:07 +05:00
731e919884 feat: added translate support to custom data
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-06 13:10:37 +05:00
49 changed files with 154 additions and 68 deletions

View File

@@ -1,6 +1,6 @@
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks # 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: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v5.0.0
@@ -27,8 +27,9 @@ repos:
name: pyright name: pyright
entry: pyright entry: pyright
language: system language: system
'types_or': [python, pyi] types_or: [python, pyi]
require_serial: true require_serial: true
exclude: '^portprotonqt/themes/[^/]+/styles\.py$'
- repo: local - repo: local
hooks: hooks:
@@ -37,5 +38,5 @@ repos:
entry: ./dev-scripts/check_qss_properties.py entry: ./dev-scripts/check_qss_properties.py
language: system language: system
types: [file] types: [file]
files: \.py$ files: ^portprotonqt/themes/[^/]+/styles\.py$
pass_filenames: false pass_filenames: false

View File

@@ -6,15 +6,16 @@
## [Unreleased] ## [Unreleased]
### Added ### Added
- Переводы в переопределениях (за подробностями в документацию)
- Обложки и описания для всех автоинсталлов
- Возможность указать ссылку для скачивания обложки в диалоге добавления игры
### Changed ### Changed
- Оптимизированны обложки автоинсталлов - Оптимизированны обложки автоинсталлов
- Папка custom_data исключена из сборки модуля для уменьшение его размера
### Fixed ### Fixed
### Contributors ### Contributors
- @Vector_null - @Vector_null

View File

@@ -34,7 +34,7 @@
- [ ] Достигнуть паритета функциональности с PortProton - [ ] Достигнуть паритета функциональности с PortProton
- [X] Добавить возможность изменения названия, описания и обложки через файлы `.local/share/PortProtonQT/custom_data/exe_name/{desc,name,cover}` - [X] Добавить возможность изменения названия, описания и обложки через файлы `.local/share/PortProtonQT/custom_data/exe_name/{desc,name,cover}`
- [X] Добавить встроенное переопределение названия, описания и обложки, например, по пути `portprotonqt/custom_data` [Документация](documentation/metadata_override/) - [X] Добавить встроенное переопределение названия, описания и обложки, например, по пути `portprotonqt/custom_data` [Документация](documentation/metadata_override/)
- [ ] Добавить переводы в переопределения - [X] Добавить переводы в переопределения
- [ ] Придумать как переопределять launcher.exe - [ ] Придумать как переопределять launcher.exe
- [X] Добавить в карточку игры сведения о поддержке геймпада - [X] Добавить в карточку игры сведения о поддержке геймпада
- [X] Добавить в карточки данные с ProtonDB - [X] Добавить в карточки данные с ProtonDB

View File

@@ -20,9 +20,9 @@ Current translation status:
| Locale | Progress | Translated | | Locale | Progress | Translated |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 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 192 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 194 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 192 of 192 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 194 of 194 |
--- ---

View File

@@ -20,9 +20,9 @@
| Локаль | Прогресс | Переведено | | Локаль | Прогресс | Переведено |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 192 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 194 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 192 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 194 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 192 из 192 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 194 из 194 |
--- ---

View File

@@ -50,7 +50,9 @@ Each `<exe_name>` folder can include:
- `metadata.txt` — contains name and description: - `metadata.txt` — contains name and description:
```txt ```txt
name=My Game Title name=My Game Title
name_ru=My Game Title (in russian language)
description=My Game Description description=My Game Description
description_ru=My Game Description (in russian language)
``` ```
- `cover.<extension>` — image file (`.png`, `.jpg`, `.jpeg`, `.bmp`) - `cover.<extension>` — image file (`.png`, `.jpg`, `.jpeg`, `.bmp`)

View File

@@ -50,7 +50,9 @@
- `metadata.txt` — имя и описание в формате: - `metadata.txt` — имя и описание в формате:
```txt ```txt
name=Моё название игры name=Моё название игры
description=Описание моей игры name_en=Моё название игры (на английском)
description=Описание моей игры (на английском)
description_en=Описание моей игры
``` ```
- `cover.<расширение>` — обложка (`.png`, `.jpg`, `.jpeg`, `.bmp`) - `cover.<расширение>` — обложка (`.png`, `.jpg`, `.jpeg`, `.bmp`)

View File

Before

Width:  |  Height:  |  Size: 720 KiB

After

Width:  |  Height:  |  Size: 720 KiB

View File

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 655 KiB

View File

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 315 KiB

View File

Before

Width:  |  Height:  |  Size: 978 KiB

After

Width:  |  Height:  |  Size: 978 KiB

View File

Before

Width:  |  Height:  |  Size: 650 KiB

After

Width:  |  Height:  |  Size: 650 KiB

View File

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 391 KiB

View File

Before

Width:  |  Height:  |  Size: 710 KiB

After

Width:  |  Height:  |  Size: 710 KiB

View File

Before

Width:  |  Height:  |  Size: 670 KiB

After

Width:  |  Height:  |  Size: 670 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 814 KiB

After

Width:  |  Height:  |  Size: 814 KiB

View File

Before

Width:  |  Height:  |  Size: 566 KiB

After

Width:  |  Height:  |  Size: 566 KiB

View File

Before

Width:  |  Height:  |  Size: 895 KiB

After

Width:  |  Height:  |  Size: 895 KiB

View File

Before

Width:  |  Height:  |  Size: 627 KiB

After

Width:  |  Height:  |  Size: 627 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 722 KiB

After

Width:  |  Height:  |  Size: 722 KiB

View File

@@ -1,5 +1,6 @@
import os import os
import tempfile import tempfile
import re
from typing import cast, TYPE_CHECKING from typing import cast, TYPE_CHECKING
from PySide6.QtGui import QPixmap, QIcon from PySide6.QtGui import QPixmap, QIcon
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
@@ -14,6 +15,7 @@ from portprotonqt.logger import get_logger
import portprotonqt.themes.standart.styles as default_styles import portprotonqt.themes.standart.styles as default_styles
from portprotonqt.theme_manager import ThemeManager from portprotonqt.theme_manager import ThemeManager
from portprotonqt.custom_widgets import AutoSizeButton from portprotonqt.custom_widgets import AutoSizeButton
from portprotonqt.downloader import Downloader
if TYPE_CHECKING: if TYPE_CHECKING:
from portprotonqt.main_window import MainWindow from portprotonqt.main_window import MainWindow
@@ -449,6 +451,7 @@ class AddGameDialog(QDialog):
self.original_name = game_name self.original_name = game_name
self.last_exe_path = exe_path # Store last selected exe path self.last_exe_path = exe_path # Store last selected exe path
self.last_cover_path = cover_path # Store last selected cover 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.setWindowTitle(_("Edit Game") if edit_mode else _("Add Game"))
self.setModal(True) self.setModal(True)
@@ -472,8 +475,7 @@ class AddGameDialog(QDialog):
# Exe path # Exe path
exe_label = QLabel(_("Path to Executable:")) exe_label = QLabel(_("Path to Executable:"))
exe_label.setStyleSheet( exe_label.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
self.theme.PARAMS_TITLE_STYLE)
self.exeEdit = CustomLineEdit(self, theme=self.theme) self.exeEdit = CustomLineEdit(self, theme=self.theme)
self.exeEdit.setStyleSheet(self.theme.ADDGAME_INPUT_STYLE) self.exeEdit.setStyleSheet(self.theme.ADDGAME_INPUT_STYLE)
@@ -550,7 +552,7 @@ class AddGameDialog(QDialog):
exeBrowseButton.setFixedWidth(self.exeEdit.width()) exeBrowseButton.setFixedWidth(self.exeEdit.width())
coverBrowseButton.setFixedWidth(self.coverEdit.width()) coverBrowseButton.setFixedWidth(self.coverEdit.width())
# Вызываем после отображения окна, когда размеры установлены, чтобы реально дождаться, когда всё сформируется # Вызываем после отображения окна, когда размеры установлены
QTimer.singleShot(0, update_button_widths) QTimer.singleShot(0, update_button_widths)
# Обновляем превью, если в режиме редактирования # Обновляем превью, если в режиме редактирования
@@ -615,15 +617,46 @@ class AddGameDialog(QDialog):
"""Обработчик выбора файла обложки в FileExplorer""" """Обработчик выбора файла обложки в FileExplorer"""
if file_path and os.path.splitext(file_path)[1].lower() in ('.png', '.jpg', '.jpeg', '.bmp'): if file_path and os.path.splitext(file_path)[1].lower() in ('.png', '.jpg', '.jpeg', '.bmp'):
self.coverEdit.setText(file_path) 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: else:
logger.warning(f"Selected file is not a valid image: {file_path}") 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): def updatePreview(self):
"""Update the cover preview image.""" """Update the cover preview image."""
cover_path = self.coverEdit.text().strip() cover_path = self.coverEdit.text().strip()
exe_path = self.exeEdit.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) pixmap = QPixmap(cover_path)
if not pixmap.isNull(): if not pixmap.isNull():
self.coverPreview.setPixmap(pixmap.scaled(250, 250, Qt.AspectRatioMode.KeepAspectRatio)) 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) os.makedirs(os.path.dirname(icon_path), exist_ok=True)
# Generate thumbnail (128x128) from exe # Generate thumbnail (128x128) from exe if no cover is provided
if not generate_thumbnail(exe_path, icon_path, size=128): 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}") logger.error(f"Failed to generate thumbnail from exe: {exe_path}")
icon_path = "" # Set empty icon if generation fails icon_path = "" # Set empty icon if generation fails

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -296,6 +296,12 @@ msgstr ""
msgid "Invalid image" msgid "Invalid image"
msgstr "" msgstr ""
msgid "Failed to download cover"
msgstr ""
msgid "Downloading cover..."
msgstr ""
msgid "No cover selected" msgid "No cover selected"
msgstr "" msgstr ""
@@ -338,6 +344,9 @@ msgstr ""
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Library" msgid "Library"
msgstr "" msgstr ""
@@ -362,9 +371,6 @@ msgstr ""
msgid "Loading PortProton games..." msgid "Loading PortProton games..."
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Game Library" msgid "Game Library"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es_ES\n" "Language: es_ES\n"
@@ -296,6 +296,12 @@ msgstr ""
msgid "Invalid image" msgid "Invalid image"
msgstr "" msgstr ""
msgid "Failed to download cover"
msgstr ""
msgid "Downloading cover..."
msgstr ""
msgid "No cover selected" msgid "No cover selected"
msgstr "" msgstr ""
@@ -338,6 +344,9 @@ msgstr ""
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Library" msgid "Library"
msgstr "" msgstr ""
@@ -362,9 +371,6 @@ msgstr ""
msgid "Loading PortProton games..." msgid "Loading PortProton games..."
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Game Library" msgid "Game Library"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PortProtonQt 0.1.1\n" "Project-Id-Version: PortProtonQt 0.1.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -294,6 +294,12 @@ msgstr ""
msgid "Invalid image" msgid "Invalid image"
msgstr "" msgstr ""
msgid "Failed to download cover"
msgstr ""
msgid "Downloading cover..."
msgstr ""
msgid "No cover selected" msgid "No cover selected"
msgstr "" msgstr ""
@@ -336,6 +342,9 @@ msgstr ""
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Library" msgid "Library"
msgstr "" msgstr ""
@@ -360,9 +369,6 @@ msgstr ""
msgid "Loading PortProton games..." msgid "Loading PortProton games..."
msgstr "" msgstr ""
msgid "Unknown Game"
msgstr ""
msgid "Game Library" msgid "Game Library"
msgstr "" msgstr ""

View File

@@ -9,8 +9,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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: 2025-07-03 19:28+0700\n" "PO-Revision-Date: 2025-07-06 17:56+0500\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language: ru_RU\n" "Language: ru_RU\n"
"Language-Team: ru_RU <LL@li.org>\n" "Language-Team: ru_RU <LL@li.org>\n"
@@ -303,6 +303,12 @@ msgstr "Применить"
msgid "Invalid image" msgid "Invalid image"
msgstr "Недопустимое изображение" msgstr "Недопустимое изображение"
msgid "Failed to download cover"
msgstr "Не удалось скачать обложку"
msgid "Downloading cover..."
msgstr "Скачивание обложки..."
msgid "No cover selected" msgid "No cover selected"
msgstr "Обложка не выбрана" msgstr "Обложка не выбрана"
@@ -345,6 +351,9 @@ msgstr "Бронза"
msgid "Pending" msgid "Pending"
msgstr "В ожидании" msgstr "В ожидании"
msgid "Unknown Game"
msgstr "Неизвестная игра"
msgid "Library" msgid "Library"
msgstr "Библиотека" msgstr "Библиотека"
@@ -369,9 +378,6 @@ msgstr "Загрузка игр из Steam..."
msgid "Loading PortProton games..." msgid "Loading PortProton games..."
msgstr "Загрузка игр из PortProton..." msgstr "Загрузка игр из PortProton..."
msgid "Unknown Game"
msgstr "Неизвестная игра"
msgid "Game Library" msgid "Game Library"
msgstr "Игровая библиотека" msgstr "Игровая библиотека"

View File

@@ -1,6 +1,7 @@
import gettext import gettext
from pathlib import Path from pathlib import Path
import locale import locale
import os
from babel import Locale from babel import Locale
LOCALE_MAP = { LOCALE_MAP = {
@@ -72,3 +73,32 @@ def get_egs_language():
# Если что-то пошло не так — используем английский по умолчанию # Если что-то пошло не так — используем английский по умолчанию
return 'en' 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

View File

@@ -28,7 +28,7 @@ from portprotonqt.config_utils import (
save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config, 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 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.logger import get_logger
from portprotonqt.downloader import Downloader from portprotonqt.downloader import Downloader
@@ -465,11 +465,9 @@ class MainWindow(QMainWindow):
os.makedirs(user_custom_folder, exist_ok=True) os.makedirs(user_custom_folder, exist_ok=True)
builtin_cover = "" builtin_cover = ""
builtin_name = None
builtin_desc = None
user_cover = "" user_cover = ""
user_name = None user_game_folder=""
user_desc = None builtin_game_folder=""
if game_exe: if game_exe:
exe_name = os.path.splitext(os.path.basename(game_exe))[0] 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) user_game_folder = os.path.join(user_custom_folder, exe_name)
os.makedirs(user_game_folder, exist_ok=True) 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() builtin_files = set(os.listdir(builtin_game_folder)) if os.path.exists(builtin_game_folder) else set()
for ext in [".jpg", ".png", ".jpeg", ".bmp"]: for ext in [".jpg", ".png", ".jpeg", ".bmp"]:
candidate = f"cover{ext}" candidate = f"cover{ext}"
@@ -484,16 +483,6 @@ class MainWindow(QMainWindow):
builtin_cover = os.path.join(builtin_game_folder, candidate) builtin_cover = os.path.join(builtin_game_folder, candidate)
break 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() user_files = set(os.listdir(user_game_folder)) if os.path.exists(user_game_folder) else set()
for ext in [".jpg", ".png", ".jpeg", ".bmp"]: for ext in [".jpg", ".png", ".jpeg", ".bmp"]:
candidate = f"cover{ext}" candidate = f"cover{ext}"
@@ -501,16 +490,7 @@ class MainWindow(QMainWindow):
user_cover = os.path.join(user_game_folder, candidate) user_cover = os.path.join(user_game_folder, candidate)
break 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: if self.portproton_location:
statistics_file = os.path.join(self.portproton_location, "data", "tmp", "statistics") statistics_file = os.path.join(self.portproton_location, "data", "tmp", "statistics")
try: try:
@@ -526,13 +506,26 @@ class MainWindow(QMainWindow):
print(f"Failed to parse playtime data: {e}") print(f"Failed to parse playtime data: {e}")
def on_steam_info(steam_info: dict): 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 language_code = get_egs_language()
builtin_desc if builtin_desc is not None else
steam_info.get("description", "")) # Чтение переводов из 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 final_cover = (user_cover if user_cover else
builtin_cover if builtin_cover else builtin_cover if builtin_cover else
steam_info.get("cover", "") or entry.get("Icon", "")) steam_info.get("cover", "") or entry.get("Icon", ""))
callback(( callback((
final_name, final_name,
final_desc, final_desc,

View File

@@ -44,7 +44,7 @@ dependencies = [
portprotonqt = "portprotonqt.app:main" portprotonqt = "portprotonqt.app:main"
[tool.setuptools.package-data] [tool.setuptools.package-data]
"portprotonqt" = ["themes/**/*", "locales/**/*", "custom_data/**/*"] "portprotonqt" = ["themes/**/*", "locales/**/*"]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
exclude = ["build-aux", "dev-scripts", "documentation", "data"] exclude = ["build-aux", "dev-scripts", "documentation", "data"]