Compare commits

...

2 Commits

Author SHA1 Message Date
skymike03
08f3e64d2a v2.4.0.2 (2026.01.14)
- correct some bugs/errors on display and logging functionality; update language files with new options
2026-01-14 20:25:47 +01:00
RGS
4968af2da9 Update image source in README.md 2026-01-07 14:48:30 +01:00
9 changed files with 63 additions and 92 deletions

View File

@@ -5,7 +5,7 @@
A free, user-friendly ROM downloader for Batocera, Knulli, and RetroBat with multi-source support.
<p align="center">
<img width="69%" alt="platform menu" src="https://github.com/user-attachments/assets/4464b57b-06a8-45e9-a411-cc12b421545a" />
<img width="69%" alt="main" src="https://github.com/user-attachments/assets/a98f1189-9a50-4cc3-b588-3f85245640d8" />
<img width="30%" alt="controls help" src="https://github.com/user-attachments/assets/38cac7e6-14f2-4e83-91da-0679669822ee" />
</p>
<p align="center">

View File

@@ -176,7 +176,6 @@ config.init_footer_font()
# Mise à jour de la résolution dans config
config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
logger.debug(f"Resolution d'ecran : {config.screen_width}x{config.screen_height}")
print(f"Resolution ecran validee: {config.screen_width}x{config.screen_height}")
# Afficher un premier écran de chargement immédiatement pour éviter un écran noir
@@ -276,7 +275,6 @@ logger.debug(f"Historique de téléchargement : {len(config.history)} entrées")
# Chargement des jeux téléchargés
config.downloaded_games = load_downloaded_games()
logger.debug(f"Jeux téléchargés : {sum(len(v) for v in config.downloaded_games.values())} jeux")
# Vérification et chargement de la configuration des contrôles (après mises à jour et détection manette)
config.controls_config = load_controls_config()
@@ -302,6 +300,9 @@ try:
if config.controls_config:
summary = {}
for action, mapping in config.controls_config.items():
# Vérifier que mapping est bien un dictionnaire
if not isinstance(mapping, dict):
continue
mtype = mapping.get("type")
val = None
if mtype == "key":
@@ -341,9 +342,6 @@ def start_web_server():
global web_server_process
try:
web_server_script = os.path.join(config.APP_FOLDER, "rgsx_web.py")
logger.info(f"Tentative de démarrage du serveur web...")
logger.info(f"Script: {web_server_script}")
logger.info(f"Fichier existe: {os.path.exists(web_server_script)}")
if not os.path.exists(web_server_script):
logger.warning(f"Script serveur web introuvable: {web_server_script}")
@@ -384,7 +382,6 @@ def start_web_server():
logger.info(f"✅ Serveur web démarré (PID: {web_server_process.pid})")
logger.info(f"🌐 Serveur accessible sur http://localhost:5000")
logger.info(f"📝 Logs de démarrage: {web_server_log}")
# Attendre un peu pour voir si le processus crash immédiatement
import time
@@ -447,7 +444,6 @@ async def main():
# Démarrer le worker de la queue de téléchargement
queue_worker_thread = threading.Thread(target=download_queue_worker, daemon=True)
queue_worker_thread.start()
logger.info("Worker de la queue de téléchargement démarré")
running = True
loading_step = "none"
@@ -1235,6 +1231,7 @@ async def main():
config.loading_progress = 20.0
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
continue # Passer immédiatement à check_ota
else:
config.menu_state = "error"
config.error_message = _("error_no_internet")
@@ -1264,6 +1261,7 @@ async def main():
config.loading_progress = 50.0
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
continue # Passer immédiatement à check_data
elif loading_step == "check_data":
is_data_empty = not os.path.exists(config.GAMES_FOLDER) or not any(os.scandir(config.GAMES_FOLDER))
if is_data_empty:
@@ -1271,6 +1269,7 @@ async def main():
config.loading_progress = 30.0
config.needs_redraw = True
logger.debug("Dossier Data vide, début du téléchargement du ZIP")
sources_zip_url = None # Initialiser pour éviter les erreurs
try:
zip_path = os.path.join(config.SAVE_FOLDER, "data_download.zip")
headers = {'User-Agent': 'Mozilla/5.0'}
@@ -1376,7 +1375,9 @@ async def main():
config.loading_progress = 80.0
config.needs_redraw = True
logger.debug(f"Dossier Data non vide, passage à {loading_step}")
continue # Passer immédiatement à load_sources
elif loading_step == "load_sources":
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
sources = load_sources()
config.menu_state = "platform"
config.loading_progress = 100.0

View File

@@ -13,7 +13,7 @@ except Exception:
pygame = None # type: ignore
# Version actuelle de l'application
app_version = "2.4.0.1"
app_version = "2.4.0.2"
def get_application_root():

View File

@@ -3,12 +3,21 @@
import pygame # type: ignore
import os
import io
import platform
import random
import config
from utils import truncate_text_middle, wrap_text, load_system_image, truncate_text_end
from utils import (truncate_text_middle, wrap_text, load_system_image, truncate_text_end,
check_web_service_status, check_custom_dns_status, load_api_keys,
_get_dest_folder_name, find_file_with_or_without_extension)
import logging
import math
from history import load_history, is_game_downloaded
from language import _, get_size_units, get_speed_unit
from language import _, get_size_units, get_speed_unit, get_available_languages, get_language_name
from rgsx_settings import (load_rgsx_settings, get_light_mode, get_show_unsupported_platforms,
get_allow_unknown_extensions, get_display_monitor, get_display_fullscreen,
get_available_monitors, get_font_family, get_sources_mode,
get_hide_premium_systems, get_symlink_option)
from game_filters import GameFilters
logger = logging.getLogger(__name__)
@@ -226,38 +235,27 @@ THEME_COLORS = {
# Général, résolution, overlay
def init_display():
"""Initialise l'écran et les ressources globales.
Supporte la sélection de moniteur et le mode fenêtré/plein écran.
Supporte la sélection de moniteur en plein écran.
Compatible Windows et Linux (Batocera).
"""
global OVERLAY
import platform
import os
from rgsx_settings import get_display_monitor, get_display_fullscreen, load_rgsx_settings
logger.debug("Initialisation de l'écran")
# Charger les paramètres d'affichage avec debug
# Charger les paramètres d'affichage
settings = load_rgsx_settings()
logger.debug(f"Settings chargés: display={settings.get('display', {})}")
target_monitor = settings.get("display", {}).get("monitor", 0)
fullscreen = settings.get("display", {}).get("fullscreen", True)
logger.debug(f"Paramètres lus: monitor={target_monitor}, fullscreen={fullscreen}")
# Vérifier les variables d'environnement (priorité sur les settings)
env_display = os.environ.get("RGSX_DISPLAY")
env_windowed = os.environ.get("RGSX_WINDOWED")
if env_display is not None:
try:
target_monitor = int(env_display)
logger.debug(f"Override par RGSX_DISPLAY: monitor={target_monitor}")
except ValueError:
pass
if env_windowed == "1":
fullscreen = False
logger.debug("Override par RGSX_WINDOWED: fullscreen=False")
logger.debug(f"Configuration finale: monitor={target_monitor}, fullscreen={fullscreen}")
# Configurer SDL pour utiliser le bon moniteur
# Cette variable d'environnement doit être définie AVANT la création de la fenêtre
@@ -295,13 +293,14 @@ def init_display():
screen_width = display_info.current_w
screen_height = display_info.current_h
# Créer la fenêtre
flags = 0
if fullscreen:
flags = pygame.FULLSCREEN
# Sur certains systèmes, NOFRAME aide pour le multi-écran
if platform.system() == "Windows":
flags |= pygame.NOFRAME
# Créer la fenêtre en plein écran
flags = pygame.FULLSCREEN
# Sur Linux/Batocera, utiliser SCALED pour respecter la résolution forcée d'EmulationStation
if platform.system() == "Linux":
flags |= pygame.SCALED
# Sur certains systèmes Windows, NOFRAME aide pour le multi-écran
elif platform.system() == "Windows":
flags |= pygame.NOFRAME
try:
screen = pygame.display.set_mode((screen_width, screen_height), flags, display=target_monitor)
@@ -315,19 +314,17 @@ def init_display():
config.screen_width = screen_width
config.screen_height = screen_height
config.current_monitor = target_monitor
config.is_fullscreen = fullscreen
# Initialisation de OVERLAY avec effet glassmorphism
OVERLAY = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
OVERLAY.fill((5, 10, 20, 160)) # Bleu très foncé semi-transparent pour effet verre
logger.debug(f"Écran initialisé: {screen_width}x{screen_height} sur moniteur {target_monitor} (fullscreen={fullscreen})")
logger.debug(f"Écran initialisé: {screen_width}x{screen_height} sur moniteur {target_monitor}")
return screen
# Fond d'écran dégradé
def draw_gradient(screen, top_color, bottom_color, light_mode=None):
"""Dessine un fond dégradé vertical avec des couleurs vibrantes et texture de grain.
En mode light, utilise une couleur unie pour de meilleures performances."""
from rgsx_settings import get_light_mode
if light_mode is None:
light_mode = get_light_mode()
@@ -355,7 +352,6 @@ def draw_gradient(screen, top_color, bottom_color, light_mode=None):
# Ajouter une texture de grain subtile pour plus de profondeur
grain_surface = pygame.Surface((width, height), pygame.SRCALPHA)
import random
random.seed(42) # Seed fixe pour cohérence
for _ in range(width * height // 200): # Réduire la densité pour performance
x = random.randint(0, width - 1)
@@ -367,7 +363,6 @@ def draw_gradient(screen, top_color, bottom_color, light_mode=None):
def draw_shadow(surface, rect, offset=6, alpha=120, light_mode=None):
"""Dessine une ombre portée pour un rectangle. Désactivé en mode light."""
from rgsx_settings import get_light_mode
if light_mode is None:
light_mode = get_light_mode()
if light_mode:
@@ -379,7 +374,6 @@ def draw_shadow(surface, rect, offset=6, alpha=120, light_mode=None):
def draw_glow_effect(screen, rect, color, intensity=80, size=10, light_mode=None):
"""Dessine un effet de glow autour d'un rectangle. Désactivé en mode light."""
from rgsx_settings import get_light_mode
if light_mode is None:
light_mode = get_light_mode()
if light_mode:
@@ -396,7 +390,6 @@ def draw_glow_effect(screen, rect, color, intensity=80, size=10, light_mode=None
def draw_stylized_button(screen, text, x, y, width, height, selected=False, light_mode=None):
"""Dessine un bouton moderne avec effet de survol, ombre et bordure arrondie.
En mode light, utilise un style simplifié pour de meilleures performances."""
from rgsx_settings import get_light_mode
if light_mode is None:
light_mode = get_light_mode()
@@ -1843,49 +1836,46 @@ def draw_extension_warning(screen):
def draw_controls(screen, menu_state, current_music_name=None, music_popup_start_time=0):
"""Affiche les contrôles contextuels en bas de l'écran selon le menu_state."""
# Import local de la fonction de traduction pour éviter les conflits de scope
from language import _ as i18n
# Mapping des contrôles par menu_state
controls_map = {
"platform": [
("history", i18n("controls_action_history")),
("confirm", i18n("controls_confirm_select")),
("start", i18n("controls_action_start")),
("history", _("controls_action_history")),
("confirm", _("controls_confirm_select")),
("start", _("controls_action_start")),
],
"game": [
("confirm", i18n("controls_confirm_select")),
("clear_history", i18n("controls_action_queue")),
(("page_up", "page_down"), i18n("controls_pages")),
("filter", i18n("controls_filter_search")),
("history", i18n("controls_action_history")),
("confirm", _("controls_confirm_select")),
("clear_history", _("controls_action_queue")),
(("page_up", "page_down"), _("controls_pages")),
("filter", _("controls_filter_search")),
("history", _("controls_action_history")),
],
"history": [
("confirm", i18n("history_game_options_title")),
("clear_history", i18n("controls_action_clear_history")),
("history", i18n("controls_action_close_history")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("history_game_options_title")),
("clear_history", _("controls_action_clear_history")),
("history", _("controls_action_close_history")),
("cancel", _("controls_cancel_back")),
],
"scraper": [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
],
"error": [
("confirm", i18n("controls_confirm_select")),
("confirm", _("controls_confirm_select")),
],
"confirm_exit": [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
],
"extension_warning": [
("confirm", i18n("controls_confirm_select")),
("confirm", _("controls_confirm_select")),
],
}
# Récupérer les contrôles pour ce menu, sinon affichage par défaut
controls_list = controls_map.get(menu_state, [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
])
# Construire les lignes avec icônes
@@ -1899,7 +1889,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
# Si aucun joystick, afficher la touche entre crochets
if not getattr(config, 'joystick', True):
start_button = f"[{start_button}]"
start_text = i18n("controls_action_start")
start_text = _("controls_action_start")
control_parts.append(f"RGSX v{config.app_version} - {start_button} : {start_text}")
# Afficher le nom du joystick s'il est détecté
@@ -1907,7 +1897,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
device_name = getattr(config, 'controller_device_name', '') or ''
if device_name:
try:
joy_label = i18n("footer_joystick")
joy_label = _("footer_joystick")
except Exception:
joy_label = "Joystick: {0}"
if isinstance(joy_label, str) and "{0}" in joy_label:
@@ -1962,7 +1952,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
combined_surf = pygame.Surface((max_width, 50), pygame.SRCALPHA)
x_pos = 10
for action_tuple in all_controls:
_, actions, label = action_tuple
ignored, actions, label = action_tuple
try:
surf = _render_icons_line(actions, label, max_width - x_pos - 10, config.tiny_font, THEME_COLORS["text"], icon_size=scaled_icon_size, icon_gap=scaled_icon_gap, icon_text_gap=scaled_icon_text_gap)
if x_pos + surf.get_width() > max_width - 10:
@@ -1977,7 +1967,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
final_surf.blit(combined_surf, (0, 0), (0, 0, x_pos - 10, 50))
icon_surfs.append(final_surf)
elif line_data[0] == "icons" and len(line_data) == 3:
_, actions, label = line_data
ignored, actions, label = line_data
try:
surf = _render_icons_line(actions, label, max_width, config.tiny_font, THEME_COLORS["text"], icon_size=scaled_icon_size, icon_gap=scaled_icon_gap, icon_text_gap=scaled_icon_text_gap)
icon_surfs.append(surf)
@@ -2015,7 +2005,6 @@ def draw_language_menu(screen):
- Bloc (titre + liste de langues) centré verticalement.
- Gestion d'overflow: réduit légèrement la hauteur/espacement si nécessaire.
"""
from language import get_available_languages, get_language_name
screen.blit(OVERLAY, (0, 0))
@@ -2161,8 +2150,6 @@ def draw_display_menu(screen):
# États actuels
layout_str = f"{getattr(config, 'GRID_COLS', 3)}x{getattr(config, 'GRID_ROWS', 4)}"
font_scale = config.accessibility_settings.get("font_scale", 1.0)
from rgsx_settings import (get_show_unsupported_platforms, get_allow_unknown_extensions,
get_display_monitor, get_display_fullscreen, get_available_monitors)
show_unsupported = get_show_unsupported_platforms()
allow_unknown = get_allow_unknown_extensions()
@@ -2418,14 +2405,6 @@ def draw_pause_controls_menu(screen, selected_index):
draw_menu_instruction(screen, text, last_button_bottom)
def draw_pause_display_menu(screen, selected_index):
from rgsx_settings import (
get_allow_unknown_extensions,
get_font_family,
get_display_monitor,
get_display_fullscreen,
get_available_monitors,
get_light_mode
)
# Layout label
layouts = [(3,3),(3,4),(4,3),(4,4)]
try:
@@ -2509,7 +2488,6 @@ def draw_pause_display_menu(screen, selected_index):
draw_menu_instruction(screen, _(key), last_button_bottom)
def draw_pause_games_menu(screen, selected_index):
from rgsx_settings import get_sources_mode, get_show_unsupported_platforms, get_hide_premium_systems
mode = get_sources_mode()
source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom")
source_txt = f"{_('menu_games_source_prefix')}: < {source_label} >"
@@ -2582,8 +2560,6 @@ def draw_pause_games_menu(screen, selected_index):
draw_menu_instruction(screen, text, last_button_bottom)
def draw_pause_settings_menu(screen, selected_index):
from rgsx_settings import get_symlink_option
from utils import check_web_service_status, check_custom_dns_status
# Music
if config.music_enabled:
music_name = config.current_music_name or ""
@@ -2653,7 +2629,6 @@ def draw_pause_settings_menu(screen, selected_index):
def draw_pause_api_keys_status(screen):
screen.blit(OVERLAY, (0,0))
from utils import load_api_keys
keys = load_api_keys()
title = _("api_keys_status_title") if _ else "API Keys Status"
# Préparer données avec masquage partiel des clés (afficher 4 premiers et 2 derniers caractères si longueur > 10)
@@ -2757,7 +2732,6 @@ def draw_pause_api_keys_status(screen):
def draw_filter_platforms_menu(screen):
"""Affiche le menu de filtrage des plateformes (afficher/masquer)."""
from rgsx_settings import load_rgsx_settings
screen.blit(OVERLAY, (0, 0))
settings = load_rgsx_settings()
hidden = set(settings.get("hidden_platforms", [])) if isinstance(settings, dict) else set()
@@ -3302,8 +3276,6 @@ def show_toast(message, duration=2000):
config.toast_start_time = pygame.time.get_ticks()
def draw_history_game_options(screen):
"""Affiche le menu d'options pour un jeu de l'historique."""
import os
from utils import _get_dest_folder_name, find_file_with_or_without_extension
screen.blit(OVERLAY, (0, 0))
@@ -3410,8 +3382,6 @@ def draw_history_game_options(screen):
def draw_history_show_folder(screen):
"""Affiche le chemin complet du fichier téléchargé."""
import os
from utils import _get_dest_folder_name
screen.blit(OVERLAY, (0, 0))
@@ -3956,7 +3926,6 @@ def draw_filter_menu_choice(screen):
def draw_filter_advanced(screen):
"""Affiche l'écran de filtrage avancé"""
from game_filters import GameFilters
screen.blit(OVERLAY, (0, 0))
@@ -4236,7 +4205,6 @@ def draw_filter_advanced(screen):
def draw_filter_priority_config(screen):
"""Affiche l'écran de configuration de la priorité des régions pour One ROM per game"""
from game_filters import GameFilters
screen.blit(OVERLAY, (0, 0))
@@ -4254,7 +4222,6 @@ def draw_filter_priority_config(screen):
# Initialiser le filtre si nécessaire
if not hasattr(config, 'game_filter_obj'):
from game_filters import GameFilters
from rgsx_settings import load_game_filters
config.game_filter_obj = GameFilters()
filter_dict = load_game_filters()

View File

@@ -264,6 +264,8 @@
"history_option_extract_archive": "Extraire l'archive",
"history_option_open_file": "Ouvrir le fichier",
"history_option_scraper": "Récupérer métadonnées",
"history_option_remove_from_queue": "Retirer de la file d'attente",
"history_option_cancel_download": "Annuler le téléchargement",
"history_option_delete_game": "Supprimer le jeu",
"history_option_error_info": "Détails de l'erreur",
"history_option_retry": "Retenter le téléchargement",

View File

@@ -259,6 +259,8 @@
"history_option_extract_archive": "Estrai archivio",
"history_option_open_file": "Apri file",
"history_option_scraper": "Scraper metadati",
"history_option_remove_from_queue": "Rimuovi dalla coda",
"history_option_cancel_download": "Annulla download",
"history_option_delete_game": "Elimina gioco",
"history_option_error_info": "Dettagli errore",
"history_option_retry": "Riprova download",

View File

@@ -404,7 +404,6 @@ def test_internet():
]
for test_url in test_urls:
logger.debug(f"Test connexion HTTP vers {test_url}")
try:
response = requests.get(test_url, timeout=5, allow_redirects=True)
if response.status_code == 200:

View File

@@ -49,7 +49,7 @@ def load_rgsx_settings():
"""Charge tous les paramètres depuis rgsx_settings.json."""
from config import RGSX_SETTINGS_PATH
logger.debug(f"Chargement des settings depuis: {RGSX_SETTINGS_PATH}")
#logger.debug(f"Chargement des settings depuis: {RGSX_SETTINGS_PATH}")
default_settings = {
"language": "en",
@@ -83,7 +83,7 @@ def load_rgsx_settings():
if os.path.exists(RGSX_SETTINGS_PATH):
with open(RGSX_SETTINGS_PATH, 'r', encoding='utf-8') as f:
settings = json.load(f)
logger.debug(f"Settings JSON chargé: display={settings.get('display', {})}")
#logger.debug(f"Settings JSON chargé: display={settings.get('display', {})}")
# Fusionner avec les valeurs par défaut pour assurer la compatibilité
for key, value in default_settings.items():
if key not in settings:

View File

@@ -1,3 +1,3 @@
{
"version": "2.4.0.1"
"version": "2.4.0.2"
}