v1.9.7.7 - Corrrection de bugs de téléchargements, bug detection clé 1fichier, ajout options d'accessibilité pour agrandir/reduire la taille des polices

This commit is contained in:
skymike03
2025-07-27 18:58:10 +02:00
parent 6f2bf5eea8
commit ec321dfc7e
9 changed files with 509 additions and 256 deletions

View File

@@ -7,7 +7,7 @@ import logging
import requests import requests
import queue import queue
import datetime import datetime
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_virtual_keyboard, draw_popup_result_download, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup, draw_gradient, draw_language_menu, THEME_COLORS from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_virtual_keyboard, draw_popup_result_download, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_cancel_download_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup, draw_gradient, THEME_COLORS
from language import handle_language_menu_events, _ from language import handle_language_menu_events, _
from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates
from controls import handle_controls, validate_menu_state, process_key_repeats from controls import handle_controls, validate_menu_state, process_key_repeats
@@ -38,8 +38,19 @@ logger = logging.getLogger(__name__)
pygame.init() pygame.init()
config.init_font() config.init_font()
pygame.joystick.init() pygame.joystick.init()
logger.debug("--------------------------------------------------------------------")
logger.debug("---------------------------DEBUT LOG--------------------------------")
logger.debug("--------------------------------------------------------------------")
# Chargement des paramètres d'accessibilité
from utils import load_accessibility_settings
config.accessibility_settings = load_accessibility_settings()
for i, scale in enumerate(config.font_scale_options):
if scale == config.accessibility_settings.get("font_scale", 1.0):
config.current_font_scale_index = i
break
# Chargement et initialisation de la langue # Chargement et initialisation de la langue
from language import initialize_language from language import initialize_language
initialize_language() initialize_language()
@@ -53,22 +64,8 @@ clock = pygame.time.Clock()
pygame.display.set_caption("RGSX") pygame.display.set_caption("RGSX")
# Initialisation des polices # Initialisation des polices via config
try: config.init_font()
font_path = os.path.join(config.APP_FOLDER, "assets", "Pixel-UniCode.ttf")
config.font = pygame.font.Font(font_path, 36) # Police principale
config.title_font = pygame.font.Font(font_path, 48) # Police pour les titres
config.search_font = pygame.font.Font(font_path, 48) # Police pour la recherche
config.progress_font = pygame.font.Font(font_path, 36) # Police pour l'affichage de la progression
config.small_font = pygame.font.Font(font_path, 28) # Police pour les petits textes
#logger.debug("Police Pixel-UniCode chargée")
except:
config.font = pygame.font.SysFont("arial", 48) # Police fallback
config.title_font = pygame.font.SysFont("arial", 60) # Police fallback pour les titres
config.search_font = pygame.font.SysFont("arial", 60) # Police fallback pour la recherche
config.progress_font = pygame.font.SysFont("arial", 36) # Police fallback pour l'affichage de la progression
config.small_font = pygame.font.SysFont("arial", 28) # Police fallback pour les petits textes
#logger.debug("Police Arial chargée")
# Mise à jour de la résolution dans config # Mise à jour de la résolution dans config
config.screen_width, config.screen_height = pygame.display.get_surface().get_size() config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
@@ -190,8 +187,10 @@ async def main():
events = pygame.event.get() events = pygame.event.get()
for event in events: for event in events:
# Gestion directe des événements pour le menu de langue # Gestion directe des événements pour le menu de langue
if config.menu_state == "language_select" and event.type == pygame.KEYDOWN: if config.menu_state == "language_select":
handle_language_menu_events(event, screen) from language import handle_language_menu_events
if handle_language_menu_events(event, screen):
config.needs_redraw = True
continue continue
if event.type == pygame.USEREVENT + 1: # Événement de fin de musique if event.type == pygame.USEREVENT + 1: # Événement de fin de musique
@@ -228,6 +227,12 @@ async def main():
#logger.debug(f"Événement transmis à handle_controls dans pause_menu: {event.type}") #logger.debug(f"Événement transmis à handle_controls dans pause_menu: {event.type}")
continue continue
if config.menu_state == "accessibility_menu":
from accessibility import handle_accessibility_events
if handle_accessibility_events(event):
config.needs_redraw = True
continue
if config.menu_state == "controls_help": if config.menu_state == "controls_help":
action = handle_controls(event, sources, joystick, screen) action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True config.needs_redraw = True
@@ -237,7 +242,11 @@ async def main():
if config.menu_state == "confirm_clear_history": if config.menu_state == "confirm_clear_history":
action = handle_controls(event, sources, joystick, screen) action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True config.needs_redraw = True
#logger.debug(f"Événement transmis à handle_controls dans confirm_clear_history: {event.type}") continue
if config.menu_state == "confirm_cancel_download":
action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True
continue continue
if config.menu_state == "redownload_game_cache": if config.menu_state == "redownload_game_cache":
@@ -298,6 +307,7 @@ async def main():
"game_name": game_name, "game_name": game_name,
"status": "downloading", "status": "downloading",
"progress": 0, "progress": 0,
"message": _("download_initializing"),
"url": url, "url": url,
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}) })
@@ -559,10 +569,18 @@ async def main():
# logger.debug("Screen updated with draw_history_list") # logger.debug("Screen updated with draw_history_list")
elif config.menu_state == "confirm_clear_history": elif config.menu_state == "confirm_clear_history":
draw_clear_history_dialog(screen) draw_clear_history_dialog(screen)
elif config.menu_state == "confirm_cancel_download":
draw_cancel_download_dialog(screen)
elif config.menu_state == "redownload_game_cache": elif config.menu_state == "redownload_game_cache":
draw_redownload_game_cache_dialog(screen) draw_redownload_game_cache_dialog(screen)
elif config.menu_state == "restart_popup": elif config.menu_state == "restart_popup":
draw_popup(screen) draw_popup(screen)
elif config.menu_state == "accessibility_menu":
from accessibility import draw_accessibility_menu
draw_accessibility_menu(screen)
elif config.menu_state == "language_select":
from display import draw_language_menu
draw_language_menu(screen)
else: else:
config.menu_state = "platform" config.menu_state = "platform"
draw_platform_grid(screen) draw_platform_grid(screen)

129
accessibility.py Normal file
View File

@@ -0,0 +1,129 @@
import pygame #type:ignore
import config
from utils import save_accessibility_settings
from language import _
def draw_accessibility_menu(screen):
"""Affiche le menu d'accessibilité avec curseur pour la taille de police."""
from display import OVERLAY, THEME_COLORS, draw_stylized_button
screen.blit(OVERLAY, (0, 0))
# Titre
title_text = _("menu_accessibility")
title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, config.screen_height // 4))
# Fond du titre
title_bg_rect = title_rect.inflate(40, 20)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_bg_rect, border_radius=10)
pygame.draw.rect(screen, THEME_COLORS["border"], title_bg_rect, 2, border_radius=10)
screen.blit(title_surface, title_rect)
# Curseur de taille de police
current_scale = config.font_scale_options[config.current_font_scale_index]
font_text = _("accessibility_font_size").format(f"{current_scale:.1f}")
# Position du curseur
cursor_y = config.screen_height // 2
cursor_width = 400
cursor_height = 60
cursor_x = (config.screen_width - cursor_width) // 2
# Fond du curseur
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (cursor_x, cursor_y, cursor_width, cursor_height), border_radius=10)
pygame.draw.rect(screen, THEME_COLORS["border"], (cursor_x, cursor_y, cursor_width, cursor_height), 2, border_radius=10)
# Flèches gauche/droite
arrow_size = 30
left_arrow_x = cursor_x + 20
right_arrow_x = cursor_x + cursor_width - arrow_size - 20
arrow_y = cursor_y + (cursor_height - arrow_size) // 2
# Flèche gauche
left_color = THEME_COLORS["fond_lignes"] if config.current_font_scale_index > 0 else THEME_COLORS["border"]
pygame.draw.polygon(screen, left_color, [
(left_arrow_x + arrow_size, arrow_y),
(left_arrow_x, arrow_y + arrow_size // 2),
(left_arrow_x + arrow_size, arrow_y + arrow_size)
])
# Flèche droite
right_color = THEME_COLORS["fond_lignes"] if config.current_font_scale_index < len(config.font_scale_options) - 1 else THEME_COLORS["border"]
pygame.draw.polygon(screen, right_color, [
(right_arrow_x, arrow_y),
(right_arrow_x + arrow_size, arrow_y + arrow_size // 2),
(right_arrow_x, arrow_y + arrow_size)
])
# Texte au centre
text_surface = config.font.render(font_text, True, THEME_COLORS["text"])
text_rect = text_surface.get_rect(center=(cursor_x + cursor_width // 2, cursor_y + cursor_height // 2))
screen.blit(text_surface, text_rect)
# Instructions
instruction_text = _("language_select_instruction")
instruction_surface = config.small_font.render(instruction_text, True, THEME_COLORS["text"])
instruction_rect = instruction_surface.get_rect(center=(config.screen_width // 2, config.screen_height - 100))
screen.blit(instruction_surface, instruction_rect)
def handle_accessibility_events(event):
"""Gère les événements du menu d'accessibilité avec support clavier et manette."""
# Gestion des touches du clavier
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and config.current_font_scale_index > 0:
config.current_font_scale_index -= 1
update_font_scale()
return True
elif event.key == pygame.K_RIGHT and config.current_font_scale_index < len(config.font_scale_options) - 1:
config.current_font_scale_index += 1
update_font_scale()
return True
elif event.key == pygame.K_RETURN or event.key == pygame.K_ESCAPE:
config.menu_state = "pause_menu"
return True
# Gestion des boutons de la manette
elif event.type == pygame.JOYBUTTONDOWN:
if event.button == 0: # Bouton A (valider)
config.menu_state = "pause_menu"
return True
elif event.button == 1: # Bouton B (annuler)
config.menu_state = "pause_menu"
return True
# Gestion du D-pad
elif event.type == pygame.JOYHATMOTION:
if event.value == (-1, 0): # Gauche
if config.current_font_scale_index > 0:
config.current_font_scale_index -= 1
update_font_scale()
return True
elif event.value == (1, 0): # Droite
if config.current_font_scale_index < len(config.font_scale_options) - 1:
config.current_font_scale_index += 1
update_font_scale()
return True
# Gestion du joystick analogique (axe horizontal)
elif event.type == pygame.JOYAXISMOTION:
if event.axis == 0 and abs(event.value) > 0.5: # Joystick gauche horizontal
if event.value < -0.5 and config.current_font_scale_index > 0: # Gauche
config.current_font_scale_index -= 1
update_font_scale()
return True
elif event.value > 0.5 and config.current_font_scale_index < len(config.font_scale_options) - 1: # Droite
config.current_font_scale_index += 1
update_font_scale()
return True
return False
def update_font_scale():
"""Met à jour l'échelle de police et sauvegarde."""
new_scale = config.font_scale_options[config.current_font_scale_index]
config.accessibility_settings["font_scale"] = new_scale
save_accessibility_settings(config.accessibility_settings)
# Réinitialiser les polices
config.init_font()
config.needs_redraw = True

View File

@@ -3,7 +3,7 @@ import os
import logging import logging
# Version actuelle de l'application # Version actuelle de l'application
app_version = "1.9.7.6" app_version = "1.9.7.7"
@@ -43,6 +43,9 @@ REPEAT_ACTION_DEBOUNCE = 150 # Délai anti-rebond pour répétitions (ms) - aug
platforms = [] platforms = []
current_platform = 0 current_platform = 0
accessibility_mode = False # Mode accessibilité pour les polices agrandies accessibility_mode = False # Mode accessibilité pour les polices agrandies
accessibility_settings = {"font_scale": 1.0}
font_scale_options = [0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
current_font_scale_index = 3 # Index pour 1.0
platform_names = {} # {platform_id: platform_name} platform_names = {} # {platform_id: platform_name}
games = [] games = []
current_game = 0 current_game = 0
@@ -89,6 +92,7 @@ current_history_item = 0
history_scroll_offset = 0 # Offset pour le défilement de l'historique history_scroll_offset = 0 # Offset pour le défilement de l'historique
visible_history_items = 15 # Nombre d'éléments d'historique visibles (ajusté dynamiquement) visible_history_items = 15 # Nombre d'éléments d'historique visibles (ajusté dynamiquement)
confirm_clear_selection = 0 # confirmation clear historique confirm_clear_selection = 0 # confirmation clear historique
confirm_cancel_selection = 0 # confirmation annulation téléchargement
last_state_change_time = 0 # Temps du dernier changement d'état pour debounce last_state_change_time = 0 # Temps du dernier changement d'état pour debounce
debounce_delay = 200 # Délai de debounce en millisecondes debounce_delay = 200 # Délai de debounce en millisecondes
platform_dicts = [] # Liste des dictionnaires de plateformes platform_dicts = [] # Liste des dictionnaires de plateformes
@@ -126,27 +130,32 @@ small_font = None
def init_font(): def init_font():
"""Initialise les polices après pygame.init().""" """Initialise les polices après pygame.init()."""
logger.debug("--------------------------------------------------------------------")
logger.debug("---------------------------DEBUT LOG--------------------------------")
logger.debug("--------------------------------------------------------------------")
global FONT, progress_font, title_font, search_font, small_font global font, progress_font, title_font, search_font, small_font
multiplier = 1.5 if accessibility_mode else 1.0 font_scale = accessibility_settings.get("font_scale", 1.0)
try: try:
FONT = pygame.font.Font(None, int(36 * multiplier)) font_path = os.path.join(APP_FOLDER, "assets", "Pixel-UniCode.ttf")
progress_font = pygame.font.Font(None, int(28 * multiplier)) font = pygame.font.Font(font_path, int(36 * font_scale))
title_font = pygame.font.Font(None, int(48 * multiplier)) title_font = pygame.font.Font(font_path, int(48 * font_scale))
search_font = pygame.font.Font(None, int(36 * multiplier)) search_font = pygame.font.Font(font_path, int(48 * font_scale))
small_font = pygame.font.Font(None, int(24 * multiplier)) progress_font = pygame.font.Font(font_path, int(36 * font_scale))
logger.debug(f"Polices initialisées avec succès (mode accessibilité: {accessibility_mode})") small_font = pygame.font.Font(font_path, int(28 * font_scale))
# amazonq-ignore-next-line logger.debug(f"Polices Pixel-UniCode initialisées (font_scale: {font_scale})")
except pygame.error as e: except Exception as e:
logger.error(f"Erreur lors de l'initialisation des polices : {e}") try:
FONT = None font = pygame.font.SysFont("arial", int(48 * font_scale))
progress_font = None title_font = pygame.font.SysFont("arial", int(60 * font_scale))
title_font = None search_font = pygame.font.SysFont("arial", int(60 * font_scale))
search_font = None progress_font = pygame.font.SysFont("arial", int(36 * font_scale))
small_font = None small_font = pygame.font.SysFont("arial", int(28 * font_scale))
logger.debug(f"Polices Arial initialisées (font_scale: {font_scale})")
except Exception as e2:
logger.error(f"Erreur lors de l'initialisation des polices : {e2}")
font = None
progress_font = None
title_font = None
search_font = None
small_font = None
def validate_resolution(): def validate_resolution():
"""Valide la résolution de l'écran par rapport aux capacités de l'écran.""" """Valide la résolution de l'écran par rapport aux capacités de l'écran."""

View File

@@ -9,7 +9,7 @@ import json
import os import os
from display import draw_validation_transition from display import draw_validation_transition
from network import download_rom, download_from_1fichier, is_1fichier_url from network import download_rom, download_from_1fichier, is_1fichier_url
from utils import load_games, check_extension_before_download, is_extension_supported, load_extensions_json, sanitize_filename from utils import load_games, check_extension_before_download, is_extension_supported, load_extensions_json, sanitize_filename, load_api_key_1fichier
from history import load_history, clear_history, add_to_history, save_history from history import load_history, clear_history, add_to_history, save_history
import logging import logging
from language import _ # Import de la fonction de traduction from language import _ # Import de la fonction de traduction
@@ -504,6 +504,7 @@ def handle_controls(event, sources, joystick, screen):
config.current_history_item = len(config.history) - 1 config.current_history_item = len(config.history) - 1
# Vérifier d'abord si c'est un lien 1fichier # Vérifier d'abord si c'est un lien 1fichier
if is_1fichier_url(url): if is_1fichier_url(url):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER: if not config.API_KEY_1FICHIER:
config.previous_menu_state = config.menu_state config.previous_menu_state = config.menu_state
config.menu_state = "error" config.menu_state = "error"
@@ -739,13 +740,50 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True config.needs_redraw = True
logger.error(f"config.pending_download est None pour {game_name}") logger.error(f"config.pending_download est None pour {game_name}")
break break
elif is_input_matched(event, "cancel"): elif is_input_matched(event, "cancel") or is_input_matched(event, "history"):
if config.history and config.current_history_item < len(config.history):
entry = config.history[config.current_history_item]
if entry.get("status") in ["downloading", "Téléchargement", "Extracting"] and is_input_matched(event, "cancel"):
config.menu_state = "confirm_cancel_download"
config.confirm_cancel_selection = 0
config.needs_redraw = True
logger.debug("Demande d'annulation de téléchargement")
return action
config.menu_state = validate_menu_state(config.previous_menu_state) config.menu_state = validate_menu_state(config.previous_menu_state)
config.current_history_item = 0 config.current_history_item = 0
config.history_scroll_offset = 0 config.history_scroll_offset = 0
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis history") logger.debug(f"Retour à {config.menu_state} depuis history")
# Confirmation annulation téléchargement
elif config.menu_state == "confirm_cancel_download":
if is_input_matched(event, "confirm"):
if config.confirm_cancel_selection == 1: # Oui
entry = config.history[config.current_history_item]
url = entry.get("url")
# Annuler la tâche correspondante
for task_id, (task, task_url, game_name, platform) in list(config.download_tasks.items()):
if task_url == url:
task.cancel()
del config.download_tasks[task_id]
entry["status"] = "Canceled"
entry["progress"] = 0
entry["message"] = "Téléchargement annulé"
save_history(config.history)
logger.debug(f"Téléchargement annulé: {game_name}")
break
config.menu_state = "history"
config.needs_redraw = True
else: # Non
config.menu_state = "history"
config.needs_redraw = True
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
config.confirm_cancel_selection = 1 - config.confirm_cancel_selection
config.needs_redraw = True
elif is_input_matched(event, "cancel"):
config.menu_state = "history"
config.needs_redraw = True
# Confirmation vider l'historique" # Confirmation vider l'historique"
elif config.menu_state == "confirm_clear_history": elif config.menu_state == "confirm_clear_history":
logger.debug(f"État confirm_clear_history, confirm_clear_selection={config.confirm_clear_selection}, événement={event.type}, valeur={getattr(event, 'value', None)}") logger.debug(f"État confirm_clear_history, confirm_clear_selection={config.confirm_clear_selection}, événement={event.type}, valeur={getattr(event, 'value', None)}")
@@ -866,26 +904,10 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Passage à language_select depuis pause_menu") logger.debug(f"Passage à language_select depuis pause_menu")
elif config.selected_option == 4: # Accessibility elif config.selected_option == 4: # Accessibility
config.accessibility_mode = not config.accessibility_mode config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.init_font() config.menu_state = "accessibility_menu"
# Reinitialiser les polices dans le main
font_path = os.path.join(config.APP_FOLDER, "assets", "Pixel-UniCode.ttf")
multiplier = 1.5 if config.accessibility_mode else 1.0
try:
config.font = pygame.font.Font(font_path, int(36 * multiplier))
config.title_font = pygame.font.Font(font_path, int(48 * multiplier))
config.search_font = pygame.font.Font(font_path, int(48 * multiplier))
config.progress_font = pygame.font.Font(font_path, int(36 * multiplier))
config.small_font = pygame.font.Font(font_path, int(28 * multiplier))
except:
config.font = pygame.font.SysFont("arial", int(48 * multiplier))
config.title_font = pygame.font.SysFont("arial", int(60 * multiplier))
config.search_font = pygame.font.SysFont("arial", int(60 * multiplier))
config.progress_font = pygame.font.SysFont("arial", int(36 * multiplier))
config.small_font = pygame.font.SysFont("arial", int(28 * multiplier))
config.menu_state = config.previous_menu_state
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Mode accessibilité {'activé' if config.accessibility_mode else 'désactivé'}") logger.debug("Passage au menu accessibilité")
elif config.selected_option == 5: # Redownload game cache elif config.selected_option == 5: # Redownload game cache
config.previous_menu_state = validate_menu_state(config.previous_menu_state) config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "redownload_game_cache" config.menu_state = "redownload_game_cache"

View File

@@ -530,7 +530,7 @@ def draw_game_list(screen):
for i in range(config.scroll_offset, min(config.scroll_offset + items_per_page, len(games))): for i in range(config.scroll_offset, min(config.scroll_offset + items_per_page, len(games))):
game_name = games[i][0] if isinstance(games[i], (list, tuple)) else games[i] game_name = games[i][0] if isinstance(games[i], (list, tuple)) else games[i]
color = THEME_COLORS["fond_lignes"] if i == config.current_game else THEME_COLORS["text"] color = THEME_COLORS["fond_lignes"] if i == config.current_game else THEME_COLORS["text"]
game_text = truncate_text_middle(game_name, config.small_font, rect_width - 40) game_text = truncate_text_middle(game_name, config.small_font, rect_width - 40, is_filename=False)
text_surface = config.small_font.render(game_text, True, color) text_surface = config.small_font.render(game_text, True, color)
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + (i - config.scroll_offset) * line_height + line_height // 2)) text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + (i - config.scroll_offset) * line_height + line_height // 2))
if i == config.current_game: if i == config.current_game:
@@ -680,7 +680,8 @@ def draw_history_list(screen):
# logger.debug(f"Affichage terminé: {game_name}, status={status_text}") # logger.debug(f"Affichage terminé: {game_name}, status={status_text}")
elif status == "Erreur": elif status == "Erreur":
status_text = _("history_status_error").format(entry.get('message', 'Échec')) status_text = _("history_status_error").format(entry.get('message', 'Échec'))
#logger.debug(f"Affichage erreur: {game_name}, status={status_text}") elif status == "Canceled":
status_text = _("history_status_canceled")
else: else:
status_text = status status_text = status
#logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}") #logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}")
@@ -757,8 +758,37 @@ def draw_clear_history_dialog(screen):
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect) screen.blit(text, text_rect)
draw_stylized_button(screen, _("button_yes"), rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_clear_selection == 1) button_width = min(160, (rect_width - 60) // 2)
draw_stylized_button(screen, _("button_no"), rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_clear_selection == 0) draw_stylized_button(screen, _("button_yes"), rect_x + rect_width // 2 - button_width - 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_clear_selection == 1)
draw_stylized_button(screen, _("button_no"), rect_x + rect_width // 2 + 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_clear_selection == 0)
def draw_cancel_download_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour annuler un téléchargement."""
screen.blit(OVERLAY, (0, 0))
message = _("confirm_cancel_download")
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height
button_height = int(config.screen_height * 0.0463)
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 150
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, THEME_COLORS["text"])
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
button_width = min(160, (rect_width - 60) // 2)
draw_stylized_button(screen, _("button_yes"), rect_x + rect_width // 2 - button_width - 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_cancel_selection == 1)
draw_stylized_button(screen, _("button_no"), rect_x + rect_width // 2 + 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_cancel_selection == 0)
# Affichage du clavier virtuel sur non-PC # Affichage du clavier virtuel sur non-PC
def draw_virtual_keyboard(screen): def draw_virtual_keyboard(screen):
@@ -960,7 +990,6 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
current_time = pygame.time.get_ticks() / 1000 current_time = pygame.time.get_ticks() / 1000
if current_time - config.music_popup_start_time < 3.0: # Afficher pendant 3 secondes if current_time - config.music_popup_start_time < 3.0: # Afficher pendant 3 secondes
control_text += f" | {config.current_music_name}" control_text += f" | {config.current_music_name}"
logger.debug(f"config.current_music_name")
max_width = config.screen_width - 40 max_width = config.screen_width - 40
wrapped_controls = wrap_text(control_text, config.small_font, max_width) wrapped_controls = wrap_text(control_text, config.small_font, max_width)
line_height = config.small_font.get_height() + 5 line_height = config.small_font.get_height() + 5
@@ -1124,22 +1153,30 @@ def draw_controls_help(screen, previous_state):
} }
# Catégories de contrôles # Catégories de contrôles
nav_text = _("controls_navigation")
pages_text = _("controls_pages")
confirm_select_text = _("controls_confirm_select")
cancel_back_text = _("controls_cancel_back")
history_text = _("controls_history")
clear_history_text = _("controls_clear_history")
filter_search_text = _("controls_filter_search")
control_categories = { control_categories = {
"Navigation": [ _("controls_category_navigation"): [
f"{get_control_display('up', '')} {get_control_display('down', '')} {get_control_display('left', '')} {get_control_display('right', '')} : Navigation", f"{get_control_display('up', '')} {get_control_display('down', '')} {get_control_display('left', '')} {get_control_display('right', '')} : {nav_text}",
f"{get_control_display('page_up', 'LB')} {get_control_display('page_down', 'RB')} : Pages" f"{get_control_display('page_up', 'LB')} {get_control_display('page_down', 'RB')} : {pages_text}"
], ],
"Actions principales": [ _("controls_category_main_actions"): [
f"{get_control_display('confirm', 'A')} : Confirmer/Sélectionner", f"{get_control_display('confirm', 'A')} : {confirm_select_text}",
f"{get_control_display('cancel', 'B')} : Annuler/Retour", f"{get_control_display('cancel', 'B')} : {cancel_back_text}",
f"{get_control_display('start', 'Start')} : {start_text}" f"{get_control_display('start', 'Start')} : {start_text}"
], ],
"Téléchargements": [ _("controls_category_downloads"): [
f"{get_control_display('history', 'Y')} : Historique", f"{get_control_display('history', 'Y')} : {history_text}",
f"{get_control_display('progress', 'X')} : Effacer historique" f"{get_control_display('progress', 'X')} : {clear_history_text}"
], ],
"Recherche": [ _("controls_category_search"): [
f"{get_control_display('filter', 'Select')} : Filtrer/Rechercher", f"{get_control_display('filter', 'Select')} : {filter_search_text}",
f"{get_control_display('delete', 'Suppr')} : {delete_text}", f"{get_control_display('delete', 'Suppr')} : {delete_text}",
f"{get_control_display('space', 'Espace')} : {space_text}" f"{get_control_display('space', 'Espace')} : {space_text}"
] ]
@@ -1185,7 +1222,7 @@ def draw_controls_help(screen, previous_state):
pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12) pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12)
# Titre # Titre
title_text = "Aide des contrôles" title_text = _("controls_help_title")
title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"]) title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, popup_y + 25)) title_rect = title_surface.get_rect(center=(config.screen_width // 2, popup_y + 25))
screen.blit(title_surface, title_rect) screen.blit(title_surface, title_rect)
@@ -1257,8 +1294,9 @@ def draw_confirm_dialog(screen):
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect) screen.blit(text, text_rect)
draw_stylized_button(screen, _("button_yes"), rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_selection == 1) button_width = min(160, (rect_width - 60) // 2)
draw_stylized_button(screen, _("button_no"), rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_selection == 0) draw_stylized_button(screen, _("button_yes"), rect_x + rect_width // 2 - button_width - 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_selection == 1)
draw_stylized_button(screen, _("button_no"), rect_x + rect_width // 2 + 10, rect_y + text_height + margin_top_bottom, button_width, button_height, selected=config.confirm_selection == 0)
# draw_redownload_game_cache_dialog # draw_redownload_game_cache_dialog
def draw_redownload_game_cache_dialog(screen): def draw_redownload_game_cache_dialog(screen):

View File

@@ -42,6 +42,7 @@
"history_status_extracting": "Extracting: {0}%", "history_status_extracting": "Extracting: {0}%",
"history_status_completed": "Completed", "history_status_completed": "Completed",
"history_status_error": "Error: {0}", "history_status_error": "Error: {0}",
"history_status_canceled": "Canceled",
"download_status": "{0}: {1}", "download_status": "{0}: {1}",
"download_progress": "{0}% {1} MB / {2} MB", "download_progress": "{0}% {1} MB / {2} MB",
@@ -66,6 +67,7 @@
"menu_remap_controls": "Remap controls", "menu_remap_controls": "Remap controls",
"menu_history": "History", "menu_history": "History",
"menu_language": "Language", "menu_language": "Language",
"menu_accessibility": "Accessibility",
"menu_redownload_cache": "Redownload Games cache", "menu_redownload_cache": "Redownload Games cache",
"menu_quit": "Quit", "menu_quit": "Quit",
@@ -155,5 +157,20 @@
"utils_permission_denied": "Permission denied during extraction: {0}", "utils_permission_denied": "Permission denied during extraction: {0}",
"utils_extraction_failed": "Extraction failed: {0}", "utils_extraction_failed": "Extraction failed: {0}",
"utils_unrar_unavailable": "Unrar command not available", "utils_unrar_unavailable": "Unrar command not available",
"utils_rar_list_failed": "Failed to list RAR files: {0}" "utils_rar_list_failed": "Failed to list RAR files: {0}",
"download_initializing": "Initializing...",
"accessibility_font_size": "Font size: {0}",
"confirm_cancel_download": "Cancel current download?",
"controls_help_title": "Controls Help",
"controls_category_navigation": "Navigation",
"controls_category_main_actions": "Main Actions",
"controls_category_downloads": "Downloads",
"controls_category_search": "Search",
"controls_navigation": "Navigation",
"controls_pages": "Pages",
"controls_confirm_select": "Confirm/Select",
"controls_cancel_back": "Cancel/Back",
"controls_history": "History",
"controls_clear_history": "Clear History",
"controls_filter_search": "Filter/Search"
} }

View File

@@ -42,6 +42,7 @@
"history_status_extracting": "Extraction : {0}%", "history_status_extracting": "Extraction : {0}%",
"history_status_completed": "Terminé", "history_status_completed": "Terminé",
"history_status_error": "Erreur : {0}", "history_status_error": "Erreur : {0}",
"history_status_canceled": "Annulé",
"download_status": "{0} : {1}", "download_status": "{0} : {1}",
"download_progress": "{0}% {1} Mo / {2} Mo", "download_progress": "{0}% {1} Mo / {2} Mo",
@@ -66,6 +67,7 @@
"menu_remap_controls": "Remapper les contrôles", "menu_remap_controls": "Remapper les contrôles",
"menu_history": "Historique", "menu_history": "Historique",
"menu_language": "Langue", "menu_language": "Langue",
"menu_accessibility": "Accessibilité",
"menu_redownload_cache": "Retélécharger le cache des jeux", "menu_redownload_cache": "Retélécharger le cache des jeux",
"menu_quit": "Quitter", "menu_quit": "Quitter",
@@ -144,7 +146,22 @@
"network_permission_error": "Pas de permission d'écriture dans {0}", "network_permission_error": "Pas de permission d'écriture dans {0}",
"network_file_not_found": "Le fichier {0} n'existe pas", "network_file_not_found": "Le fichier {0} n'existe pas",
"network_cannot_get_filename": "Impossible de récupérer le nom du fichier", "network_cannot_get_filename": "Impossible de récupérer le nom du fichier",
"network_cannot_get_download_url": "Impossible de récupérer l'URL de téléchargement", "network_cannot_get_download_url": "Impossible de récupérer l'URL de téléchargement",
"download_initializing": "Initialisation en cours...",
"accessibility_font_size": "Taille de police : {0}",
"confirm_cancel_download": "Annuler le téléchargement en cours ?",
"controls_help_title": "Aide des contrôles",
"controls_category_navigation": "Navigation",
"controls_category_main_actions": "Actions principales",
"controls_category_downloads": "Téléchargements",
"controls_category_search": "Recherche",
"controls_navigation": "Navigation",
"controls_pages": "Pages",
"controls_confirm_select": "Confirmer/Sélectionner",
"controls_cancel_back": "Annuler/Retour",
"controls_history": "Historique",
"controls_clear_history": "Effacer historique",
"controls_filter_search": "Filtrer/Rechercher",
"network_download_failed": "Échec du téléchargement après {0} tentatives", "network_download_failed": "Échec du téléchargement après {0} tentatives",
"network_api_error": "Erreur lors de la requête API, la clé est peut-être incorrecte: {0}", "network_api_error": "Erreur lors de la requête API, la clé est peut-être incorrecte: {0}",
"network_download_error": "Erreur téléchargement {0}: {1}", "network_download_error": "Erreur téléchargement {0}: {1}",

View File

@@ -10,6 +10,7 @@ from config import OTA_VERSION_ENDPOINT,APP_FOLDER, UPDATE_FOLDER, OTA_UPDATE_ZI
from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier
from history import save_history from history import save_history
import logging import logging
import datetime
import queue import queue
import time import time
import os import os
@@ -152,9 +153,8 @@ def extract_update(zip_path, dest_dir, source_url):
logger.error(f"Erreur critique lors de l'extraction du ZIP {source_url}: {str(e)}") logger.error(f"Erreur critique lors de l'extraction du ZIP {source_url}: {str(e)}")
return False, _("network_zip_extraction_error").format(source_url, str(e)) return False, _("network_zip_extraction_error").format(source_url, str(e))
# File d'attente pour la progression # File d'attente pour la progression - une par tâche
import queue progress_queues = {}
progress_queue = queue.Queue()
@@ -162,13 +162,9 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
logger.debug(f"Début téléchargement: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}") logger.debug(f"Début téléchargement: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}")
result = [None, None] result = [None, None]
# Vider la file d'attente avant de commencer # Créer une queue spécifique pour cette tâche
while not progress_queue.empty(): if task_id not in progress_queues:
try: progress_queues[task_id] = queue.Queue()
progress_queue.get_nowait()
logger.debug(f"File progress_queue vidée pour {game_name}")
except queue.Empty:
break
def download_thread(): def download_thread():
logger.debug(f"Thread téléchargement démarré pour {url}, task_id={task_id}") logger.debug(f"Thread téléchargement démarré pour {url}, task_id={task_id}")
@@ -178,7 +174,6 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
if platform_dict["platform"] == platform: if platform_dict["platform"] == platform:
dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", ""))) dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", "")))
logger.debug(f"Répertoire de destination trouvé pour {platform}: {dest_dir}") logger.debug(f"Répertoire de destination trouvé pour {platform}: {dest_dir}")
#dest_dir = platform_dict.get("folder")
break break
if not dest_dir: if not dest_dir:
dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform.lower().replace(" ", "")) dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform.lower().replace(" ", ""))
@@ -201,47 +196,27 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
'Upgrade-Insecure-Requests': '1' 'Upgrade-Insecure-Requests': '1'
} }
# Utiliser une session pour gérer les cookies
session = requests.Session() session = requests.Session()
session.headers.update(headers) session.headers.update(headers)
# Première requête HEAD pour obtenir la vraie URL
#logger.debug(f"Première requête HEAD vers {url}")
head_response = session.head(url, timeout=30, allow_redirects=False)
#logger.debug(f"HEAD Status: {head_response.status_code}, Headers: {dict(head_response.headers)}")
# Suivre la redirection manuellement si nécessaire
final_url = url
if head_response.status_code in [301, 302, 303, 307, 308]:
final_url = head_response.headers.get('Location', url)
#logger.debug(f"Redirection détectée vers: {final_url}")
# Requête GET vers l'URL finale avec en-têtes spécifiques
download_headers = headers.copy() download_headers = headers.copy()
download_headers['Accept'] = 'application/octet-stream, */*' download_headers['Accept'] = 'application/octet-stream, */*'
download_headers['Referer'] = 'https://myrient.erista.me/' download_headers['Referer'] = 'https://myrient.erista.me/'
response = session.get(final_url, stream=True, timeout=30, allow_redirects=False, headers=download_headers) response = session.get(url, stream=True, timeout=30, allow_redirects=True, headers=download_headers)
logger.debug(f"Status code: {response.status_code}") logger.debug(f"Status code: {response.status_code}")
logger.debug(f"Headers: {dict(response.headers)}")
response.raise_for_status() response.raise_for_status()
total_size = int(response.headers.get('content-length', 0)) total_size = int(response.headers.get('content-length', 0))
logger.debug(f"Taille totale: {total_size} octets") logger.debug(f"Taille totale: {total_size} octets")
if total_size == 0:
logger.warning(f"Taille de fichier 0, possible redirection ou erreur. URL finale: {response.url}")
# Vérifier si c'est une redirection
if response.url != url:
logger.debug(f"Redirection détectée: {url} -> {response.url}")
# Initialiser la progression avec task_id # Initialiser la progression avec task_id
progress_queue.put((task_id, 0, total_size)) progress_queues[task_id].put((task_id, 0, total_size))
logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}") logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}")
downloaded = 0 downloaded = 0
chunk_size = 4096 chunk_size = 4096
last_update_time = time.time() last_update_time = time.time()
update_interval = 0.5 # Mettre à jour toutes les 0,5 secondes update_interval = 0.1 # Mettre à jour toutes les 0,1 secondes
with open(dest_path, 'wb') as f: with open(dest_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=chunk_size): for chunk in response.iter_content(chunk_size=chunk_size):
if chunk: if chunk:
@@ -250,24 +225,17 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
downloaded += size_received downloaded += size_received
current_time = time.time() current_time = time.time()
if current_time - last_update_time >= update_interval: if current_time - last_update_time >= update_interval:
# Calculer le pourcentage correctement et le limiter entre 0 et 100 progress_queues[task_id].put((task_id, downloaded, total_size))
progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0
progress_percent = max(0, min(100, progress_percent))
progress_queue.put((task_id, downloaded, total_size))
last_update_time = current_time last_update_time = current_time
else:
logger.debug("Chunk vide reçu")
os.chmod(dest_path, 0o644) os.chmod(dest_path, 0o644)
logger.debug(f"Téléchargement terminé: {dest_path}") logger.debug(f"Téléchargement terminé: {dest_path}")
# Vérifier si l'extraction est nécessaire pour les archives non supportées
if is_zip_non_supported: if is_zip_non_supported:
logger.debug(f"Extraction automatique nécessaire pour {dest_path}") logger.debug(f"Extraction automatique nécessaire pour {dest_path}")
extension = os.path.splitext(dest_path)[1].lower() extension = os.path.splitext(dest_path)[1].lower()
if extension == ".zip": if extension == ".zip":
try: try:
# Mettre à jour le statut avant l'extraction
if isinstance(config.history, list): if isinstance(config.history, list):
for entry in config.history: for entry in config.history:
if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]: if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
@@ -319,7 +287,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
result[1] = _("network_download_error").format(game_name, str(e)) result[1] = _("network_download_error").format(game_name, str(e))
finally: finally:
logger.debug(f"Thread téléchargement terminé pour {url}, task_id={task_id}") logger.debug(f"Thread téléchargement terminé pour {url}, task_id={task_id}")
progress_queue.put((task_id, result[0], result[1])) progress_queues[task_id].put((task_id, result[0], result[1]))
logger.debug(f"Final result sent to queue: success={result[0]}, message={result[1]}, task_id={task_id}") logger.debug(f"Final result sent to queue: success={result[0]}, message={result[1]}, task_id={task_id}")
thread = threading.Thread(target=download_thread) thread = threading.Thread(target=download_thread)
@@ -328,68 +296,63 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
# Boucle principale pour mettre à jour la progression # Boucle principale pour mettre à jour la progression
while thread.is_alive(): while thread.is_alive():
try: try:
while not progress_queue.empty(): task_queue = progress_queues.get(task_id)
data = progress_queue.get() if task_queue:
logger.debug(f"Progress queue data received: {data}") while not task_queue.empty():
if len(data) != 3 or data[0] != task_id: # Ignorer les données d'une autre tâche data = task_queue.get()
logger.debug(f"Ignoring queue data for task_id={data[0]}, expected={task_id}") #logger.debug(f"Progress queue data received: {data}")
continue if isinstance(data[1], bool): # Fin du téléchargement
if isinstance(data[1], bool): # Fin du téléchargement success, message = data[1], data[2]
success, message = data[1], data[2] if isinstance(config.history, list):
# Vérifier si config.history est une liste avant d'itérer for entry in config.history:
if isinstance(config.history, list): if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement", "Extracting"]:
for entry in config.history: entry["status"] = "Download_OK" if success else "Erreur"
if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement", "Extracting"]: entry["progress"] = 100 if success else 0
entry["status"] = "Download_OK" if success else "Erreur" entry["message"] = message
entry["progress"] = 100 if success else 0 save_history(config.history)
# Utiliser une variable intermédiaire pour stocker le message config.needs_redraw = True
message_text = message logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
entry["message"] = message_text break
save_history(config.history) else:
config.needs_redraw = True downloaded, total_size = data[1], data[2]
logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}") progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0
break progress_percent = max(0, min(100, progress_percent))
else:
downloaded, total_size = data[1], data[2] if isinstance(config.history, list):
# Calculer le pourcentage correctement et le limiter entre 0 et 100 for entry in config.history:
progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0 if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
progress_percent = max(0, min(100, progress_percent)) entry["progress"] = progress_percent
entry["status"] = "Téléchargement"
# Vérifier si config.history est une liste avant d'itérer entry["downloaded_size"] = downloaded
if isinstance(config.history, list): entry["total_size"] = total_size
for entry in config.history: config.needs_redraw = True
if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]: break
entry["progress"] = progress_percent await asyncio.sleep(0.1)
entry["status"] = "Téléchargement"
entry["downloaded_size"] = downloaded
entry["total_size"] = total_size
config.needs_redraw = True
break
await asyncio.sleep(0.2)
except Exception as e: except Exception as e:
logger.error(f"Erreur mise à jour progression: {str(e)}") logger.error(f"Erreur mise à jour progression: {str(e)}")
thread.join() thread.join()
#logger.debug(f"Thread joined for {url}, task_id={task_id}") # Nettoyer la queue
if task_id in progress_queues:
del progress_queues[task_id]
return result[0], result[1] return result[0], result[1]
async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None): async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None):
load_api_key_1fichier() config.API_KEY_1FICHIER = load_api_key_1fichier()
logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}") logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}")
logger.debug(f"Clé API 1fichier: {'présente' if config.API_KEY_1FICHIER else 'absente'}")
result = [None, None] result = [None, None]
# Vider la file d'attente avant de commencer # Créer une queue spécifique pour cette tâche
while not progress_queue.empty(): logger.debug(f"Création queue pour task_id={task_id}")
try: if task_id not in progress_queues:
progress_queue.get_nowait() progress_queues[task_id] = queue.Queue()
logger.debug(f"File progress_queue vidée pour {game_name}")
except queue.Empty:
break
def download_thread(): def download_thread():
logger.debug(f"Thread téléchargement 1fichier démarré pour {url}, task_id={task_id}") logger.debug(f"Thread téléchargement 1fichier démarré pour {url}, task_id={task_id}")
try: try:
link = url.split('&af=')[0] link = url.split('&af=')[0]
logger.debug(f"URL nettoyée: {link}")
dest_dir = None dest_dir = None
for platform_dict in config.platform_dicts: for platform_dict in config.platform_dicts:
if platform_dict["platform"] == platform: if platform_dict["platform"] == platform:
@@ -398,10 +361,13 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
if not dest_dir: if not dest_dir:
logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}") logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}")
dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform) dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform)
logger.debug(f"Répertoire destination déterminé: {dest_dir}")
logger.debug(f"Vérification répertoire destination: {dest_dir}") logger.debug(f"Vérification répertoire destination: {dest_dir}")
os.makedirs(dest_dir, exist_ok=True) os.makedirs(dest_dir, exist_ok=True)
logger.debug(f"Répertoire créé ou existant: {dest_dir}")
if not os.access(dest_dir, os.W_OK): if not os.access(dest_dir, os.W_OK):
logger.error(f"Pas de permission d'écriture dans {dest_dir}")
raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}") raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}")
headers = { headers = {
@@ -412,10 +378,9 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
"url": link, "url": link,
"pretty": 1 "pretty": 1
} }
logger.debug(f"Préparation requête file/info pour {link}")
#logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/file/info.cgi pour {url}")
response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30) response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30)
#logger.debug(f"Réponse reçue, status: {response.status_code}") logger.debug(f"Réponse file/info reçue, code: {response.status_code}")
response.raise_for_status() response.raise_for_status()
file_info = response.json() file_info = response.json()
@@ -427,7 +392,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
filename = file_info.get("filename", "").strip() filename = file_info.get("filename", "").strip()
if not filename: if not filename:
logger.error("Impossible de récupérer le nom du fichier") logger.error(f"Impossible de récupérer le nom du fichier")
result[0] = False result[0] = False
result[1] = _("network_cannot_get_filename") result[1] = _("network_cannot_get_filename")
return return
@@ -435,50 +400,48 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
sanitized_filename = sanitize_filename(filename) sanitized_filename = sanitize_filename(filename)
dest_path = os.path.join(dest_dir, sanitized_filename) dest_path = os.path.join(dest_dir, sanitized_filename)
logger.debug(f"Chemin destination: {dest_path}") logger.debug(f"Chemin destination: {dest_path}")
logger.debug(f"Envoi requête get_token pour {link}")
#logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/download/get_token.cgi pour {url}")
response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30) response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30)
#logger.debug(f"Réponse reçue, status: {response.status_code}") logger.debug(f"Réponse get_token reçue, code: {response.status_code}")
response.raise_for_status() response.raise_for_status()
download_info = response.json() download_info = response.json()
final_url = download_info.get("url") final_url = download_info.get("url")
if not final_url: if not final_url:
logger.error("Impossible de récupérer l'URL de téléchargement") logger.error(f"Impossible de récupérer l'URL de téléchargement")
result[0] = False result[0] = False
result[1] = _("network_cannot_get_download_url") result[1] = _("network_cannot_get_download_url")
return return
logger.debug(f"URL de téléchargement obtenue: {final_url}")
lock = threading.Lock() lock = threading.Lock()
retries = 10 retries = 10
retry_delay = 10 retry_delay = 10
# Initialiser la progression avec task_id logger.debug(f"Initialisation progression avec taille inconnue pour task_id={task_id}")
progress_queue.put((task_id, 0, 0)) # Taille initiale inconnue progress_queues[task_id].put((task_id, 0, 0)) # Taille initiale inconnue
#logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}")
for attempt in range(retries): for attempt in range(retries):
logger.debug(f"Début tentative {attempt + 1} pour télécharger {final_url}")
try: try:
#logger.debug(f"Tentative {attempt + 1} : Envoi requête GET à {final_url}")
with requests.get(final_url, stream=True, headers={'User-Agent': 'Mozilla/5.0'}, timeout=30) as response: with requests.get(final_url, stream=True, headers={'User-Agent': 'Mozilla/5.0'}, timeout=30) as response:
#logger.debug(f"Réponse reçue, status: {response.status_code}") logger.debug(f"Réponse GET reçue, code: {response.status_code}")
response.raise_for_status() response.raise_for_status()
total_size = int(response.headers.get('content-length', 0)) total_size = int(response.headers.get('content-length', 0))
logger.debug(f"Taille totale: {total_size} octets") logger.debug(f"Taille totale: {total_size} octets")
with lock: with lock:
# Vérifier si config.history est une liste avant d'itérer
if isinstance(config.history, list): if isinstance(config.history, list):
for entry in config.history: for entry in config.history:
if "url" in entry and entry["url"] == url and entry["status"] == "downloading": if "url" in entry and entry["url"] == url and entry["status"] == "downloading":
entry["total_size"] = total_size entry["total_size"] = total_size
config.needs_redraw = True config.needs_redraw = True
break break
progress_queue.put((task_id, 0, total_size)) # Mettre à jour la taille totale progress_queues[task_id].put((task_id, 0, total_size)) # Mettre à jour la taille totale
downloaded = 0 downloaded = 0
chunk_size = 8192 chunk_size = 8192
last_update_time = time.time() last_update_time = time.time()
update_interval = 0.5 # Mettre à jour toutes les 0,5 secondes update_interval = 0.1 # Mettre à jour toutes les 0,1 secondes
logger.debug(f"Ouverture fichier: {dest_path}")
with open(dest_path, 'wb') as f: with open(dest_path, 'wb') as f:
logger.debug(f"Ouverture fichier: {dest_path}")
for chunk in response.iter_content(chunk_size=chunk_size): for chunk in response.iter_content(chunk_size=chunk_size):
if chunk: if chunk:
f.write(chunk) f.write(chunk)
@@ -486,11 +449,9 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
current_time = time.time() current_time = time.time()
if current_time - last_update_time >= update_interval: if current_time - last_update_time >= update_interval:
with lock: with lock:
# Vérifier si config.history est une liste avant d'itérer
if isinstance(config.history, list): if isinstance(config.history, list):
for entry in config.history: for entry in config.history:
if "url" in entry and entry["url"] == url and entry["status"] == "downloading": if "url" in entry and entry["url"] == url and entry["status"] == "downloading":
# Calculer le pourcentage correctement et le limiter entre 0 et 100
progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0 progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0
progress_percent = max(0, min(100, progress_percent)) progress_percent = max(0, min(100, progress_percent))
entry["progress"] = progress_percent entry["progress"] = progress_percent
@@ -498,14 +459,12 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
entry["downloaded_size"] = downloaded entry["downloaded_size"] = downloaded
entry["total_size"] = total_size entry["total_size"] = total_size
config.needs_redraw = True config.needs_redraw = True
#logger.debug(f"Progression mise à jour: {entry['progress']:.1f}% pour {game_name}")
break break
progress_queue.put((task_id, downloaded, total_size)) progress_queues[task_id].put((task_id, downloaded, total_size))
last_update_time = current_time last_update_time = current_time
if is_zip_non_supported: if is_zip_non_supported:
with lock: with lock:
# Vérifier si config.history est une liste avant d'itérer
if isinstance(config.history, list): if isinstance(config.history, list):
for entry in config.history: for entry in config.history:
if "url" in entry and entry["url"] == url and entry["status"] == "Téléchargement": if "url" in entry and entry["url"] == url and entry["status"] == "Téléchargement":
@@ -514,11 +473,12 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
config.needs_redraw = True config.needs_redraw = True
break break
extension = os.path.splitext(dest_path)[1].lower() extension = os.path.splitext(dest_path)[1].lower()
logger.debug(f"Début extraction, type d'archive: {extension}")
if extension == ".zip": if extension == ".zip":
try: try:
success, msg = extract_zip(dest_path, dest_dir, url) success, msg = extract_zip(dest_path, dest_dir, url)
logger.debug(f"Extraction ZIP terminée: {msg}")
if success: if success:
logger.debug(f"Extraction ZIP réussie: {msg}")
result[0] = True result[0] = True
result[1] = _("network_download_extract_ok").format(game_name) result[1] = _("network_download_extract_ok").format(game_name)
else: else:
@@ -526,14 +486,14 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
result[0] = False result[0] = False
result[1] = _("network_extraction_failed").format(msg) result[1] = _("network_extraction_failed").format(msg)
except Exception as e: except Exception as e:
logger.error(f"Exception lors de l'extraction: {str(e)}") logger.error(f"Exception lors de l'extraction ZIP: {str(e)}")
result[0] = False result[0] = False
result[1] = f"Erreur téléchargement {game_name}: {str(e)}" result[1] = f"Erreur téléchargement {game_name}: {str(e)}"
elif extension == ".rar": elif extension == ".rar":
try: try:
success, msg = extract_rar(dest_path, dest_dir, url) success, msg = extract_rar(dest_path, dest_dir, url)
logger.debug(f"Extraction RAR terminée: {msg}")
if success: if success:
logger.debug(f"Extraction RAR réussie: {msg}")
result[0] = True result[0] = True
result[1] = _("network_download_extract_ok").format(game_name) result[1] = _("network_download_extract_ok").format(game_name)
else: else:
@@ -549,6 +509,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
result[0] = True result[0] = True
result[1] = _("network_download_ok").format(game_name) result[1] = _("network_download_ok").format(game_name)
else: else:
logger.debug(f"Application des permissions sur {dest_path}")
os.chmod(dest_path, 0o644) os.chmod(dest_path, 0o644)
logger.debug(f"Téléchargement terminé: {dest_path}") logger.debug(f"Téléchargement terminé: {dest_path}")
result[0] = True result[0] = True
@@ -556,78 +517,77 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
return return
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"Tentative {attempt + 1} échouée : {e}") logger.error(f"Tentative {attempt + 1} échouée: {e}")
if attempt < retries - 1: if attempt < retries - 1:
logger.debug(f"Attente de {retry_delay} secondes avant nouvelle tentative")
time.sleep(retry_delay) time.sleep(retry_delay)
else: else:
logger.error("Nombre maximum de tentatives atteint") logger.error(f"Nombre maximum de tentatives atteint")
result[0] = False result[0] = False
result[1] = _("network_download_failed").format(retries) result[1] = _("network_download_failed").format(retries)
return return
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"Erreur API 1fichier : {e}") logger.error(f"Erreur API 1fichier: {e}")
result[0] = False result[0] = False
result[1] = _("network_api_error").format(str(e)) result[1] = _("network_api_error").format(str(e))
finally: finally:
logger.debug(f"Thread téléchargement 1fichier terminé pour {url}, task_id={task_id}") logger.debug(f"Thread téléchargement 1fichier terminé pour {url}, task_id={task_id}")
progress_queue.put((task_id, result[0], result[1])) progress_queues[task_id].put((task_id, result[0], result[1]))
logger.debug(f"Final result sent to queue: success={result[0]}, message={result[1]}, task_id={task_id}") logger.debug(f"Résultat final envoyé à la queue: success={result[0]}, message={result[1]}, task_id={task_id}")
thread = threading.Thread(target=download_thread)
logger.debug(f"Démarrage thread pour {url}, task_id={task_id}") logger.debug(f"Démarrage thread pour {url}, task_id={task_id}")
thread = threading.Thread(target=download_thread)
thread.start() thread.start()
# Boucle principale pour mettre à jour la progression # Boucle principale pour mettre à jour la progression
logger.debug(f"Début boucle de progression pour task_id={task_id}")
while thread.is_alive(): while thread.is_alive():
try: try:
while not progress_queue.empty(): task_queue = progress_queues.get(task_id)
data = progress_queue.get() if task_queue:
logger.debug(f"Progress queue data received: {data}") while not task_queue.empty():
if len(data) != 3 or data[0] != task_id: # Ignorer les données d'une autre tâche data = task_queue.get()
logger.debug(f"Ignoring queue data for task_id={data[0]}, expected={task_id}") logger.debug(f"Données queue progression reçues: {data}")
continue if isinstance(data[1], bool): # Fin du téléchargement
if isinstance(data[1], bool): # Fin du téléchargement success, message = data[1], data[2]
success, message = data[1], data[2] if isinstance(config.history, list):
# Vérifier si config.history est une liste avant d'itérer for entry in config.history:
if isinstance(config.history, list): if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement", "Extracting"]:
for entry in config.history: entry["status"] = "Download_OK" if success else "Erreur"
if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement", "Extracting"]: entry["progress"] = 100 if success else 0
entry["status"] = "Download_OK" if success else "Erreur" entry["message"] = message
entry["progress"] = 100 if success else 0 save_history(config.history)
# Utiliser une variable intermédiaire pour stocker le message config.needs_redraw = True
message_text = message logger.debug(f"Mise à jour finale historique: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
entry["message"] = message_text break
save_history(config.history) else:
config.needs_redraw = True downloaded, total_size = data[1], data[2]
logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}") progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0
break progress_percent = max(0, min(100, progress_percent))
else:
downloaded, total_size = data[1], data[2] if isinstance(config.history, list):
# Calculer le pourcentage correctement et le limiter entre 0 et 100 for entry in config.history:
progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0 if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
progress_percent = max(0, min(100, progress_percent)) entry["progress"] = progress_percent
entry["status"] = "Téléchargement"
# Vérifier si config.history est une liste avant d'itérer entry["downloaded_size"] = downloaded
if isinstance(config.history, list): entry["total_size"] = total_size
for entry in config.history: config.needs_redraw = True
if "url" in entry and entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]: break
entry["progress"] = progress_percent await asyncio.sleep(0.1)
entry["status"] = "Téléchargement"
entry["downloaded_size"] = downloaded
entry["total_size"] = total_size
config.needs_redraw = True
break
await asyncio.sleep(0.2)
except Exception as e: except Exception as e:
logger.error(f"Erreur mise à jour progression: {str(e)}") logger.error(f"Erreur mise à jour progression: {str(e)}")
logger.debug(f"Fin boucle de progression, attente fin thread pour task_id={task_id}")
thread.join() thread.join()
logger.debug(f"Thread joined for {url}, task_id={task_id}") logger.debug(f"Thread terminé, nettoyage queue pour task_id={task_id}")
# Nettoyer la queue
if task_id in progress_queues:
del progress_queues[task_id]
logger.debug(f"Fin download_from_1fichier, résultat: success={result[0]}, message={result[1]}")
return result[0], result[1] return result[0], result[1]
def is_1fichier_url(url): def is_1fichier_url(url):
"""Détecte si l'URL est un lien 1fichier.""" """Détecte si l'URL est un lien 1fichier."""
return "1fichier.com" in url return "1fichier.com" in url

View File

@@ -13,6 +13,28 @@ import time
import random import random
import random import random
from config import JSON_EXTENSIONS, SAVE_FOLDER from config import JSON_EXTENSIONS, SAVE_FOLDER
def load_accessibility_settings():
"""Charge les paramètres d'accessibilité depuis accessibility.json."""
accessibility_path = os.path.join(SAVE_FOLDER, "accessibility.json")
try:
if os.path.exists(accessibility_path):
with open(accessibility_path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
logger.error(f"Erreur lors du chargement de accessibility.json: {str(e)}")
return {"font_scale": 1.0}
def save_accessibility_settings(settings):
"""Sauvegarde les paramètres d'accessibilité dans accessibility.json."""
accessibility_path = os.path.join(SAVE_FOLDER, "accessibility.json")
try:
os.makedirs(SAVE_FOLDER, exist_ok=True)
with open(accessibility_path, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=2)
logger.debug(f"Paramètres d'accessibilité sauvegardés: {settings}")
except Exception as e:
logger.error(f"Erreur lors de la sauvegarde de accessibility.json: {str(e)}")
from history import save_history from history import save_history
from language import _ # Import de la fonction de traduction from language import _ # Import de la fonction de traduction
from datetime import datetime from datetime import datetime
@@ -85,17 +107,23 @@ def check_extension_before_download(url, platform, game_name):
def is_extension_supported(filename, platform, extensions_data): def is_extension_supported(filename, platform, extensions_data):
"""Vérifie si l'extension du fichier est supportée pour la plateforme donnée.""" """Vérifie si l'extension du fichier est supportée pour la plateforme donnée."""
extension = os.path.splitext(filename)[1].lower() extension = os.path.splitext(filename)[1].lower()
dest_dir = None dest_dir = None
for platform_dict in config.platform_dicts: for platform_dict in config.platform_dicts:
if platform_dict["platform"] == platform: if platform_dict["platform"] == platform:
dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", ""))) dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder"))
break break
if not dest_dir: if not dest_dir:
logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}") logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}")
dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform) dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform)
for system in extensions_data:
if system["folder"] == dest_dir: dest_folder_name = os.path.basename(dest_dir)
return extension in system["extensions"] for i, system in enumerate(extensions_data):
if system["folder"] == dest_folder_name:
result = extension in system["extensions"]
return result
logger.warning(f"Aucun système trouvé pour le dossier {dest_dir}") logger.warning(f"Aucun système trouvé pour le dossier {dest_dir}")
return False return False
@@ -388,7 +416,7 @@ def extract_zip(zip_path, dest_dir, url):
if current_time - last_save_time >= save_interval: if current_time - last_save_time >= save_interval:
save_history(config.history) save_history(config.history)
last_save_time = current_time last_save_time = current_time
logger.debug(f"Extraction en cours: {info.filename}, file_extracted={file_extracted}/{file_size}, total_extracted={extracted_size}/{total_size}, progression={progress_percent:.1f}%") # logger.debug(f"Extraction en cours: {info.filename}, file_extracted={file_extracted}/{file_size}, total_extracted={extracted_size}/{total_size}, progression={progress_percent:.1f}%")
config.needs_redraw = True config.needs_redraw = True
break break
@@ -574,7 +602,7 @@ def extract_rar(rar_path, dest_dir, url):
logger.error(f"Erreur lors de la suppression de {rar_path}: {str(e)}") logger.error(f"Erreur lors de la suppression de {rar_path}: {str(e)}")
def play_random_music(music_files, music_folder, current_music=None): def play_random_music(music_files, music_folder, current_music=None):
"""Joue une musique aléatoire et configure l'événement de fin.""" """Joue une musique aléatoire et configure l'événement de fin de manière non-bloquante."""
if music_files: if music_files:
# Éviter de rejouer la même musique consécutivement # Éviter de rejouer la même musique consécutivement
available_music = [f for f in music_files if f != current_music] available_music = [f for f in music_files if f != current_music]
@@ -583,11 +611,21 @@ def play_random_music(music_files, music_folder, current_music=None):
music_file = random.choice(available_music) music_file = random.choice(available_music)
music_path = os.path.join(music_folder, music_file) music_path = os.path.join(music_folder, music_file)
logger.debug(f"Lecture de la musique : {music_path}") logger.debug(f"Lecture de la musique : {music_path}")
pygame.mixer.music.load(music_path)
pygame.mixer.music.set_volume(0.5) def load_and_play_music():
pygame.mixer.music.play(loops=0) # Jouer une seule fois try:
pygame.mixer.music.set_endevent(pygame.USEREVENT + 1) # Événement de fin pygame.mixer.music.load(music_path)
set_music_popup(music_file) # Afficher le nom de la musique dans la popup pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play(loops=0) # Jouer une seule fois
pygame.mixer.music.set_endevent(pygame.USEREVENT + 1) # Événement de fin
set_music_popup(music_file) # Afficher le nom de la musique dans la popup
except Exception as e:
logger.error(f"Erreur lors du chargement de la musique {music_path}: {str(e)}")
# Charger et jouer la musique dans un thread séparé pour éviter le blocage
music_thread = threading.Thread(target=load_and_play_music, daemon=True)
music_thread.start()
return music_file # Retourner la nouvelle musique pour mise à jour return music_file # Retourner la nouvelle musique pour mise à jour
else: else:
logger.debug("Aucune musique trouvée dans /RGSX/assets/music") logger.debug("Aucune musique trouvée dans /RGSX/assets/music")
@@ -602,13 +640,17 @@ def set_music_popup(music_name):
def load_api_key_1fichier(): def load_api_key_1fichier():
"""Charge la clé API 1fichier depuis le dossier de sauvegarde, crée le fichier si absent.""" """Charge la clé API 1fichier depuis le dossier de sauvegarde, crée le fichier si absent."""
api_path = os.path.join(SAVE_FOLDER, "1fichierAPI.txt") api_path = os.path.join(SAVE_FOLDER, "1fichierAPI.txt")
logger.debug(f"Tentative de chargement de la clé API depuis: {api_path}")
try: try:
# Vérifie si le fichier existe déjà # Vérifie si le fichier existe déjà
if not os.path.exists(api_path): if not os.path.exists(api_path):
# Crée le dossier parent si nécessaire
os.makedirs(SAVE_FOLDER, exist_ok=True)
# Crée le fichier vide si absent # Crée le fichier vide si absent
with open(api_path, "w") as f: with open(api_path, "w") as f:
f.write("") f.write("")
logger.info(f"Fichier de clé API créé : {api_path}") logger.info(f"Fichier de clé API créé : {api_path}")
return ""
except OSError as e: except OSError as e:
logger.error(f"Erreur lors de la création du fichier de clé API : {e}") logger.error(f"Erreur lors de la création du fichier de clé API : {e}")
return "" return ""
@@ -616,9 +658,10 @@ def load_api_key_1fichier():
try: try:
with open(api_path, "r", encoding="utf-8") as f: with open(api_path, "r", encoding="utf-8") as f:
api_key = f.read().strip() api_key = f.read().strip()
logger.debug(f"Clé API 1fichier chargée : {api_key}") logger.debug(f"Clé API 1fichier lue: '{api_key}' (longueur: {len(api_key)})")
if not api_key: if not api_key:
logger.warning("Clé API 1fichier vide, veuillez la renseigner dans le fichier pour pouvoir utiliser les fonctionnalités de téléchargement sur 1fichier.") logger.warning("Clé API 1fichier vide, veuillez la renseigner dans le fichier pour pouvoir utiliser les fonctionnalités de téléchargement sur 1fichier.")
config.API_KEY_1FICHIER = api_key
return api_key return api_key
except OSError as e: except OSError as e:
logger.error(f"Erreur lors de la lecture de la clé API : {e}") logger.error(f"Erreur lors de la lecture de la clé API : {e}")