v1.9.9.4 - implement custom source mode (test)

This commit is contained in:
skymike03
2025-09-03 18:38:15 +02:00
parent 4ade358e1c
commit 2e9785e4e1
9 changed files with 190 additions and 64 deletions

View File

@@ -27,6 +27,7 @@ from utils import (
)
from history import load_history, save_history
from config import OTA_data_ZIP
from rgsx_settings import get_sources_mode, get_custom_sources_url, get_sources_zip_url
from accessibility import load_accessibility_settings
# Configuration du logging
@@ -64,6 +65,10 @@ for i, scale in enumerate(config.font_scale_options):
# Chargement et initialisation de la langue
from language import initialize_language
initialize_language()
# Initialiser le mode sources et URL personnalisée
config.sources_mode = get_sources_mode()
config.custom_sources_url = get_custom_sources_url()
logger.debug(f"Mode sources initial: {config.sources_mode}, URL custom: {config.custom_sources_url}")
# Détection du système non-PC
config.is_non_pc = detect_non_pc()
@@ -624,6 +629,10 @@ async def main():
config.needs_redraw = True
logger.error(f"État de menu non valide détecté: {config.menu_state}, retour à platform")
draw_controls(screen, config.menu_state, getattr(config, 'current_music_name', None), getattr(config, 'music_popup_start_time', 0))
# Popup générique (affiché dans n'importe quel état si timer actif), sauf si un état popup dédié déjà l'affiche
if config.popup_timer > 0 and config.popup_message and config.menu_state not in ["update_result", "restart_popup"]:
draw_popup(screen)
pygame.display.flip()
@@ -722,45 +731,60 @@ async def main():
try:
zip_path = os.path.join(config.SAVE_FOLDER, "data_download.zip")
headers = {'User-Agent': 'Mozilla/5.0'}
with requests.get(OTA_data_ZIP, stream=True, headers=headers, timeout=30) as response:
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
logger.debug(f"Taille totale du ZIP : {total_size} octets")
downloaded = 0
os.makedirs(os.path.dirname(zip_path), exist_ok=True)
with open(zip_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
config.download_progress[OTA_data_ZIP] = {
"downloaded_size": downloaded,
"total_size": total_size,
"status": "Téléchargement",
"progress_percent": (downloaded / total_size * 100) if total_size > 0 else 0
}
config.loading_progress = 15.0 + (35.0 * downloaded / total_size) if total_size > 0 else 15.0
config.needs_redraw = True
await asyncio.sleep(0)
logger.debug(f"ZIP téléchargé : {zip_path}")
config.current_loading_system = _("loading_extracting_data")
config.loading_progress = 60.0
config.needs_redraw = True
dest_dir = config.SAVE_FOLDER
success, message = extract_zip_data(zip_path, dest_dir, OTA_data_ZIP)
if success:
logger.debug(f"Extraction réussie : {message}")
config.loading_progress = 70.0
config.needs_redraw = True
# Déterminer l'URL à utiliser selon le mode (RGSX ou custom)
sources_zip_url = get_sources_zip_url(OTA_data_ZIP)
if sources_zip_url is None:
# Mode custom sans URL valide -> pas de téléchargement, jeux vides
logger.warning("Mode custom actif mais aucune URL valide fournie. Liste de jeux vide.")
config.popup_message = _("sources_mode_custom_missing_url").format(config.RGSX_SETTINGS_PATH)
config.popup_timer = 5000
else:
raise Exception(f"Échec de l'extraction : {message}")
try:
with requests.get(sources_zip_url, stream=True, headers=headers, timeout=30) as response:
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
logger.debug(f"Taille totale du ZIP : {total_size} octets")
downloaded = 0
os.makedirs(os.path.dirname(zip_path), exist_ok=True)
with open(zip_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
config.download_progress[sources_zip_url] = {
"downloaded_size": downloaded,
"total_size": total_size,
"status": "Téléchargement",
"progress_percent": (downloaded / total_size * 100) if total_size > 0 else 0
}
config.loading_progress = 15.0 + (35.0 * downloaded / total_size) if total_size > 0 else 15.0
config.needs_redraw = True
await asyncio.sleep(0)
logger.debug(f"ZIP téléchargé : {zip_path}")
config.current_loading_system = _("loading_extracting_data")
config.loading_progress = 60.0
config.needs_redraw = True
dest_dir = config.SAVE_FOLDER
success, message = extract_zip_data(zip_path, dest_dir, sources_zip_url)
if success:
logger.debug(f"Extraction réussie : {message}")
config.loading_progress = 70.0
config.needs_redraw = True
else:
raise Exception(f"Échec de l'extraction : {message}")
except Exception as de:
logger.error(f"Erreur téléchargement custom source: {de}")
config.popup_message = _("sources_mode_custom_download_error")
config.popup_timer = 5000
# Pas d'arrêt : continuer avec jeux vides
except Exception as e:
logger.error(f"Erreur lors du téléchargement/extraction du Dossier Data : {str(e)}")
config.menu_state = "error"
# Message UI générique (les détails restent dans les logs)
config.error_message = _("error_extract_data_failed")
config.needs_redraw = True
# En mode custom on ne bloque pas le chargement ; en mode RGSX (sources_zip_url non None et OTA) on affiche une erreur
if sources_zip_url is not None:
config.menu_state = "error"
config.error_message = _("error_extract_data_failed")
config.needs_redraw = True
loading_step = "load_sources"
if os.path.exists(zip_path):
os.remove(zip_path)
@@ -797,6 +821,17 @@ async def main():
config.needs_redraw = True
logger.debug("Transition terminée, passage à game")
# Mise à jour du timer popup générique (en dehors des états spéciaux) AVANT mise à jour last_frame_time
if config.popup_timer > 0 and config.popup_message and config.menu_state not in ["update_result", "restart_popup"]:
delta = current_time - config.last_frame_time
if delta > 0:
config.popup_timer -= delta
# Forcer redraw pour mettre à jour le compte à rebours
config.needs_redraw = True
if config.popup_timer <= 0:
config.popup_timer = 0
config.popup_message = ""
# Mettre à jour last_frame_time après tous les calculs dépendants
config.last_frame_time = current_time
clock.tick(60)
await asyncio.sleep(0.01)

View File

@@ -5,7 +5,7 @@ import platform
from rgsx_settings import load_rgsx_settings, save_rgsx_settings, migrate_old_settings
# Version actuelle de l'application
app_version = "1.9.9.3"
app_version = "1.9.9.4"
def get_operating_system():
"""Renvoie le nom du système d'exploitation."""
@@ -147,6 +147,8 @@ transition_duration = 18
games_count = {}
music_enabled = True # Par défaut la musique est activée
API_KEY_1FICHIER = "" # Initialisation de la variable globale pour la clé API
sources_mode = "rgsx" # Mode des sources de jeux (rgsx/custom)
custom_sources_url = "" # URL personnalisée si mode custom
# Variables pour la sélection de langue
selected_language_index = 0

View File

@@ -994,7 +994,9 @@ def handle_controls(event, sources, joystick, screen):
config.selected_option = max(0, config.selected_option - 1)
config.needs_redraw = True
elif is_input_matched(event, "down"):
config.selected_option = min(8, config.selected_option + 1) # 9 options maintenant (0-8)
# Nombre d'options dynamique (inclut éventuellement l'option source des jeux)
total = getattr(config, 'pause_menu_total_options', 9) # fallback 9
config.selected_option = min(total - 1, config.selected_option + 1)
config.needs_redraw = True
elif is_input_matched(event, "confirm"):
if config.selected_option == 0: # Controls
@@ -1034,18 +1036,32 @@ def handle_controls(event, sources, joystick, screen):
config.menu_state = "accessibility_menu"
config.needs_redraw = True
logger.debug("Passage au menu accessibilité")
elif config.selected_option == 5: # Redownload game cache
elif config.selected_option == 5: # Source toggle
try:
from rgsx_settings import get_sources_mode, set_sources_mode
current_mode = get_sources_mode()
new_mode = set_sources_mode('custom' if current_mode == 'rgsx' else 'rgsx')
config.sources_mode = new_mode
if new_mode == 'custom':
config.popup_message = _("sources_mode_custom_select_info").format(config.RGSX_SETTINGS_PATH)
config.popup_timer = 10000
else:
config.popup_message = _("sources_mode_rgsx_select_info")
config.popup_timer = 4000
config.needs_redraw = True
logger.info(f"Changement du mode des sources vers {new_mode}")
except Exception as e:
logger.error(f"Erreur changement mode sources: {e}")
elif config.selected_option == 6: # Redownload game cache
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "redownload_game_cache"
config.redownload_confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Passage à redownload_game_cache depuis pause_menu")
elif config.selected_option == 6: # Music toggle
elif config.selected_option == 7: # Music toggle
config.music_enabled = not config.music_enabled
save_music_config()
if config.music_enabled:
# Relancer la musique si activée
# Utilise les variables globales si elles existent
music_files = getattr(config, "music_files", None)
music_folder = getattr(config, "music_folder", None)
if music_files and music_folder:
@@ -1054,7 +1070,7 @@ def handle_controls(event, sources, joystick, screen):
pygame.mixer.music.stop()
config.needs_redraw = True
logger.info(f"Musique {'activée' if config.music_enabled else 'désactivée'} via menu pause")
elif config.selected_option == 7: # Symlink option
elif config.selected_option == 8: # Symlink option
from rgsx_settings import set_symlink_option, get_symlink_option
current_status = get_symlink_option()
success, message = set_symlink_option(not current_status)
@@ -1062,7 +1078,7 @@ def handle_controls(event, sources, joystick, screen):
config.popup_timer = 3000 if success else 5000
config.needs_redraw = True
logger.info(f"Symlink option {'activée' if not current_status else 'désactivée'} via menu pause")
elif config.selected_option == 8: # Quit
elif config.selected_option == 9: # Quit
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "confirm_exit"
config.confirm_selection = 0

View File

@@ -1187,33 +1187,27 @@ def draw_language_menu(screen):
def draw_pause_menu(screen, selected_option):
"""Dessine le menu pause avec un style moderne."""
screen.blit(OVERLAY, (0, 0))
# Option musique dynamique
from rgsx_settings import get_symlink_option, get_sources_mode
mode = get_sources_mode()
source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom")
if config.music_enabled:
music_name = config.current_music_name or ""
music_option = _("menu_music_enabled").format(music_name)
else:
music_option = _("menu_music_disabled")
# Option symlink dynamique
from rgsx_settings import get_symlink_option
if get_symlink_option():
symlink_option = _("symlink_option_enabled")
else:
symlink_option = _("symlink_option_disabled")
symlink_option = _("symlink_option_enabled") if get_symlink_option() else _("symlink_option_disabled")
options = [
_("menu_controls"),
_("menu_remap_controls"),
_("menu_history"),
_("menu_language"),
_("menu_accessibility"),
_("menu_redownload_cache"),
music_option, # Ici l'option dynamique
symlink_option,
_("menu_quit")
_("menu_controls"), # 0
_("menu_remap_controls"), # 1
_("menu_history"), # 2
_("menu_language"), # 3
_("menu_accessibility"), # 4
f"{_('menu_games_source_prefix')}: {source_label}", # 5
_("menu_redownload_cache"), # 6
music_option, # 7
symlink_option, # 8
_("menu_quit") # 9
]
menu_width = int(config.screen_width * 0.8)
line_height = config.font.get_height() + 10
button_height = int(config.screen_height * 0.0463)
@@ -1235,6 +1229,8 @@ def draw_pause_menu(screen, selected_option):
button_height,
selected=i == selected_option
)
# Stocker le nombre total d'options pour la navigation dynamique
config.pause_menu_total_options = len(options)
# Menu aide contrôles
def draw_controls_help(screen, previous_state):
@@ -1313,6 +1309,9 @@ def draw_controls_help(screen, previous_state):
else:
if cur:
line_surf = font.render(cur, True, THEME_COLORS["text"])
wrapped.append((False, line_surf))
total_height += line_surf.get_height() + line_spacing
max_width = max(max_width, line_surf.get_width())

View File

@@ -187,4 +187,13 @@
"symlink_option_disabled": "Symlink-Option deaktiviert",
"symlink_settings_saved_successfully": "Symlink-Einstellungen erfolgreich gespeichert",
"symlink_settings_save_error": "Fehler beim Speichern der Symlink-Einstellungen"
,
"menu_games_source_prefix": "Spielquelle",
"games_source_rgsx": "RGSX",
"sources_mode_rgsx_select_info": "RGSX: Spieleliste aktualisieren",
"games_source_custom": "Benutzerdefiniert"
,
"sources_mode_custom_select_info": "Benutzerdefiniert: URL in {0} setzen und Spieleliste aktualisieren",
"sources_mode_custom_missing_url": "Keine benutzerdefinierte URL gesetzt (bearbeite {0})",
"sources_mode_custom_download_error": "Download der benutzerdefinierten Quelle fehlgeschlagen"
}

View File

@@ -187,4 +187,13 @@
"symlink_option_disabled": "Symlink option disabled",
"symlink_settings_saved_successfully": "Symlink settings saved successfully",
"symlink_settings_save_error": "Error saving symlink settings"
,
"menu_games_source_prefix": "Game source",
"games_source_rgsx": "RGSX",
"sources_mode_rgsx_select_info": "RGSX: update the games list",
"games_source_custom": "Custom"
,
"sources_mode_custom_select_info": "Custom mode: set URL in {0} then update games list",
"sources_mode_custom_missing_url": "No custom URL set (edit {0})",
"sources_mode_custom_download_error": "Custom source download failed"
}

View File

@@ -188,4 +188,13 @@
"symlink_option_disabled": "Opción symlink deshabilitada",
"symlink_settings_saved_successfully": "Configuración symlink guardada con éxito",
"symlink_settings_save_error": "Error al guardar la configuración symlink"
,
"menu_games_source_prefix": "Origen de juegos",
"games_source_rgsx": "RGSX",
"sources_mode_rgsx_select_info": "RGSX: actualizar la lista de juegos",
"games_source_custom": "Personalizado"
,
"sources_mode_custom_select_info": "Modo personalizado: define la URL en {0} y actualiza la lista de juegos",
"sources_mode_custom_missing_url": "No se ha establecido URL personalizada (editar {0})",
"sources_mode_custom_download_error": "Fallo en la descarga de la fuente personalizada"
}

View File

@@ -188,4 +188,12 @@
"symlink_option_disabled": "Option symlink désactivée",
"symlink_settings_saved_successfully": "Paramètres symlink sauvegardés avec succès",
"symlink_settings_save_error": "Erreur lors de la sauvegarde des paramètres symlink"
,
"menu_games_source_prefix": "Source des jeux",
"games_source_rgsx": "RGSX",
"sources_mode_rgsx_select_info": "RGSX : mettre à jour la liste des jeux",
"games_source_custom": "Personnalisée",
"sources_mode_custom_select_info": "Mode personnalisé : définir l'URL dans {0} puis mettre à jour la liste des jeux",
"sources_mode_custom_missing_url": "Aucune URL personnalisée définie (modifier {0})",
"sources_mode_custom_download_error": "Échec du téléchargement de la source personnalisée"
}

View File

@@ -28,6 +28,10 @@ def load_rgsx_settings():
"symlink": {
"enabled": False,
"target_directory": ""
},
"sources": { # Nouvelle section pour la source des jeux
"mode": "rgsx", # "rgsx" ou "custom"
"custom_url": "" # URL personnalisée pour le ZIP des sources
}
}
@@ -217,3 +221,38 @@ def apply_symlink_path(base_path, platform_folder):
else:
# Return original path
return os.path.join(base_path, platform_folder)
# ----------------------- Sources (RGSX / Custom) ----------------------- #
def get_sources_mode(settings=None):
"""Retourne le mode des sources: 'rgsx' (par défaut) ou 'custom'."""
if settings is None:
settings = load_rgsx_settings()
return settings.get("sources", {}).get("mode", "rgsx")
def set_sources_mode(mode):
"""Définit le mode des sources et sauvegarde le fichier."""
if mode not in ("rgsx", "custom"):
mode = "rgsx"
settings = load_rgsx_settings()
sources = settings.setdefault("sources", {})
sources["mode"] = mode
save_rgsx_settings(settings)
return mode
def get_custom_sources_url(settings=None):
"""Retourne l'URL personnalisée configurée (ou chaîne vide)."""
if settings is None:
settings = load_rgsx_settings()
return settings.get("sources", {}).get("custom_url", "").strip()
def get_sources_zip_url(fallback_url):
"""Retourne l'URL ZIP à utiliser selon le mode. Fallback sur l'URL standard si custom invalide."""
settings = load_rgsx_settings()
if get_sources_mode(settings) == "custom":
custom = get_custom_sources_url(settings)
if custom.startswith("http://") or custom.startswith("https://"):
return custom
# Pas de fallback : retourner None pour signaler une source vide
return None
return fallback_url