Amélioration historique, suppression fenetre progression intégrée, et ajout controles en bas de l'ecran
This commit is contained in:
182
__main__.py
182
__main__.py
@@ -5,12 +5,14 @@ import asyncio
|
||||
import platform
|
||||
import logging
|
||||
import requests
|
||||
import queue
|
||||
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, THEME_COLORS
|
||||
from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates
|
||||
from controls import handle_controls, validate_menu_state
|
||||
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
|
||||
from utils import detect_non_pc, load_sources, check_extension_before_download, extract_zip, play_random_music
|
||||
from history import load_history
|
||||
from history import load_history, save_history
|
||||
import config
|
||||
from config import OTA_VERSION_ENDPOINT, OTA_UPDATE_SCRIPT, OTA_data_ZIP
|
||||
|
||||
@@ -132,7 +134,7 @@ async def main():
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while running:
|
||||
clock.tick(60) # Limite à 60 FPS
|
||||
clock.tick(30) # Limite à 60 FPS
|
||||
if config.update_triggered:
|
||||
logger.debug("Mise à jour déclenchée, arrêt de la boucle principale")
|
||||
break
|
||||
@@ -143,6 +145,12 @@ async def main():
|
||||
if config.menu_state == "download_progress" and current_time - last_redraw_time >= 100:
|
||||
config.needs_redraw = True
|
||||
last_redraw_time = current_time
|
||||
# Forcer redraw toutes les 100 ms dans history avec téléchargement actif
|
||||
if config.menu_state == "history" and any(entry["status"] == "Téléchargement" for entry in config.history):
|
||||
if current_time - last_redraw_time >= 100:
|
||||
config.needs_redraw = True
|
||||
last_redraw_time = current_time
|
||||
# logger.debug("Forcing redraw in history state due to active download")
|
||||
|
||||
# Gestion de la fin du popup
|
||||
if config.menu_state == "restart_popup" and config.popup_timer > 0:
|
||||
@@ -218,7 +226,40 @@ async def main():
|
||||
logger.debug(f"Événement transmis à handle_controls dans redownload_game_cache: {event.type}")
|
||||
continue
|
||||
|
||||
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning", "history"]:
|
||||
if config.menu_state == "extension_warning":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
if action == "confirm":
|
||||
if config.pending_download and config.extension_confirm_selection == 0: # Oui
|
||||
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||
logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}")
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
config.history.append({
|
||||
"platform": platform,
|
||||
"game_name": game_name,
|
||||
"status": "downloading",
|
||||
"progress": 0,
|
||||
"url": url,
|
||||
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
config.current_history_item = len(config.history) - 1
|
||||
save_history(config.history)
|
||||
config.download_tasks[task_id] = (
|
||||
asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported, task_id)),
|
||||
url, game_name, platform
|
||||
)
|
||||
config.menu_state = "history"
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Téléchargement démarré pour {game_name}, task_id={task_id}")
|
||||
elif config.extension_confirm_selection == 1: # Non
|
||||
config.menu_state = config.previous_menu_state
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.debug("Téléchargement annulé, retour à l'état précédent")
|
||||
continue
|
||||
|
||||
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "history"]:
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
if action == "quit":
|
||||
@@ -227,10 +268,20 @@ async def main():
|
||||
elif action == "download" and config.menu_state == "game" and config.filtered_games:
|
||||
game = config.filtered_games[config.current_game]
|
||||
game_name = game[0] if isinstance(game, (list, tuple)) else game
|
||||
platform = config.platforms[config.current_platform]
|
||||
platform = config.platforms[config.current_platform]["name"] # Utiliser le nom de la plateforme
|
||||
url = game[1] if isinstance(game, (list, tuple)) and len(game) > 1 else None
|
||||
if url:
|
||||
logger.debug(f"Vérification pour {game_name}, URL: {url}")
|
||||
# Ajouter une entrée temporaire à l'historique
|
||||
config.history.append({
|
||||
"platform": platform,
|
||||
"game_name": game_name,
|
||||
"status": "downloading",
|
||||
"progress": 0,
|
||||
"url": url,
|
||||
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
config.current_history_item = len(config.history) - 1 # Sélectionner l'entrée en cours
|
||||
if is_1fichier_url(url):
|
||||
if not config.API_KEY_1FICHIER:
|
||||
config.previous_menu_state = config.menu_state
|
||||
@@ -238,6 +289,11 @@ async def main():
|
||||
config.error_message = (
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt"
|
||||
)
|
||||
# Mettre à jour l'entrée temporaire avec l'erreur
|
||||
config.history[-1]["status"] = "Erreur"
|
||||
config.history[-1]["progress"] = 0
|
||||
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.error("Clé API 1fichier absente")
|
||||
config.pending_download = None
|
||||
@@ -249,18 +305,20 @@ async def main():
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Extension non reconnue pour lien 1fichier, passage à extension_warning pour {game_name}")
|
||||
# Supprimer l'entrée temporaire si erreur
|
||||
config.history.pop()
|
||||
else:
|
||||
config.previous_menu_state = config.menu_state
|
||||
logger.debug(f"Previous menu state défini: {config.previous_menu_state}")
|
||||
success, message = download_from_1fichier(url, platform, game_name, is_zip_non_supported)
|
||||
config.download_result_message = message
|
||||
config.download_result_error = not success
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
# Lancer le téléchargement dans une tâche asynchrone
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
config.download_tasks[task_id] = (
|
||||
asyncio.create_task(download_from_1fichier(url, platform, game_name, is_zip_non_supported)),
|
||||
url, game_name, platform
|
||||
)
|
||||
config.menu_state = "history" # Passer à l'historique
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Téléchargement 1fichier terminé pour {game_name}, succès={success}, message={message}")
|
||||
logger.debug(f"Téléchargement 1fichier démarré pour {game_name}, passage à l'historique")
|
||||
else:
|
||||
is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name)
|
||||
if not is_supported:
|
||||
@@ -269,18 +327,20 @@ async def main():
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Extension non reconnue, passage à extension_warning pour {game_name}")
|
||||
# Supprimer l'entrée temporaire si erreur
|
||||
config.history.pop()
|
||||
else:
|
||||
config.previous_menu_state = config.menu_state
|
||||
logger.debug(f"Previous menu state défini: {config.previous_menu_state}")
|
||||
success, message = download_rom(url, platform, game_name, is_zip_non_supported)
|
||||
config.download_result_message = message
|
||||
config.download_result_error = not success
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
# Lancer le téléchargement dans une tâche asynchrone
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
config.download_tasks[task_id] = (
|
||||
asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported)),
|
||||
url, game_name, platform
|
||||
)
|
||||
config.menu_state = "history" # Passer à l'historique
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Téléchargement terminé pour {game_name}, succès={success}, message={message}")
|
||||
logger.debug(f"Téléchargement démarré pour {game_name}, passage à l'historique")
|
||||
elif action == "redownload" and config.menu_state == "history" and config.history:
|
||||
entry = config.history[config.current_history_item]
|
||||
platform = entry["platform"]
|
||||
@@ -340,13 +400,27 @@ async def main():
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Retéléchargement terminé pour {game_name}, succès={success}, message={message}")
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Gestion des téléchargements
|
||||
if config.download_tasks:
|
||||
for task_id, (task, url, game_name, platform) in list(config.download_tasks.items()):
|
||||
if task.done():
|
||||
try:
|
||||
success, message = await task
|
||||
if "http" in message:
|
||||
message = message.split("https://")[0].strip()
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["status"] = "Download_OK" if success else "Erreur"
|
||||
entry["progress"] = 100 if success else 0
|
||||
entry["message"] = message
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}, task_id={task_id}")
|
||||
break
|
||||
config.download_result_message = message
|
||||
config.download_result_error = not success
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
@@ -355,9 +429,59 @@ async def main():
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
del config.download_tasks[task_id]
|
||||
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}")
|
||||
except Exception as e:
|
||||
config.download_result_message = f"Erreur lors du téléchargement : {str(e)}"
|
||||
message = f"Erreur lors du téléchargement: {str(e)}"
|
||||
if "http" in message:
|
||||
message = message.split("https://")[0].strip()
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["status"] = "Erreur"
|
||||
entry["progress"] = 0
|
||||
entry["message"] = message
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Erreur téléchargement: {game_name}, message={message}, task_id={task_id}")
|
||||
break
|
||||
config.download_result_message = message
|
||||
config.download_result_error = True
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
del config.download_tasks[task_id]
|
||||
else:
|
||||
# Traiter les mises à jour de progression
|
||||
|
||||
progress_queue = queue.Queue()
|
||||
while not progress_queue.empty():
|
||||
data = progress_queue.get()
|
||||
# logger.debug(f"Progress queue data received: {data}, task_id={task_id}")
|
||||
if len(data) != 3 or data[0] != task_id: # Ignorer les données d'une autre tâche
|
||||
logger.debug(f"Ignoring queue data for task_id={data[0]}, expected={task_id}")
|
||||
continue
|
||||
if isinstance(data[1], bool): # Fin du téléchargement
|
||||
success, message = data[1], data[2]
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["status"] = "Download_OK" if success else "Erreur"
|
||||
entry["progress"] = 100 if success else 0
|
||||
entry["message"] = message
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
|
||||
break
|
||||
else:
|
||||
downloaded, total_size = data[1], data[2]
|
||||
progress = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["progress"] = progress
|
||||
entry["status"] = "Téléchargement"
|
||||
config.needs_redraw = True
|
||||
# logger.debug(f"Progress updated in history: {progress:.1f}% for {game_name}, task_id={task_id}")
|
||||
break
|
||||
config.download_result_message = message
|
||||
config.download_result_error = True
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
@@ -365,19 +489,20 @@ async def main():
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
del config.download_tasks[task_id]
|
||||
logger.error(f"Erreur dans tâche de téléchargement: {str(e)}")
|
||||
|
||||
# Gestion de la fin du popup download_result
|
||||
if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000:
|
||||
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "history"] else "game"
|
||||
config.menu_state = "history" # Rester dans l'historique après le popup
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Fin popup download_result, retour à {config.menu_state}")
|
||||
logger.debug(f"Fin popup download_result, retour à history")
|
||||
|
||||
# Affichage
|
||||
if config.needs_redraw:
|
||||
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
|
||||
|
||||
|
||||
if config.menu_state == "controls_mapping":
|
||||
draw_controls_mapping(screen, ACTIONS[0], None, False, 0.0)
|
||||
elif config.menu_state == "loading":
|
||||
@@ -406,7 +531,8 @@ async def main():
|
||||
elif config.menu_state == "controls_help":
|
||||
draw_controls_help(screen, config.previous_menu_state)
|
||||
elif config.menu_state == "history":
|
||||
draw_history_list(screen)
|
||||
draw_history_list(screen)
|
||||
# logger.debug("Screen updated with draw_history_list")
|
||||
elif config.menu_state == "confirm_clear_history":
|
||||
draw_clear_history_dialog(screen)
|
||||
elif config.menu_state == "redownload_game_cache":
|
||||
@@ -420,7 +546,9 @@ async def main():
|
||||
logger.error(f"État de menu non valide détecté: {config.menu_state}, retour à platform")
|
||||
draw_controls(screen, config.menu_state)
|
||||
pygame.display.flip()
|
||||
|
||||
config.needs_redraw = False
|
||||
# logger.debug("Screen flipped with pygame.display.flip()")
|
||||
|
||||
# Gestion de l'état controls_mapping
|
||||
if config.menu_state == "controls_mapping":
|
||||
|
||||
BIN
assets/music/aquatic_ambience.mp3
Normal file
BIN
assets/music/aquatic_ambience.mp3
Normal file
Binary file not shown.
20
config.py
20
config.py
@@ -5,7 +5,7 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "1.9.5"
|
||||
app_version = "1.9.6"
|
||||
|
||||
|
||||
# URL du serveur OTA
|
||||
@@ -37,11 +37,7 @@ transition_state = "idle"
|
||||
transition_progress = 0.0
|
||||
transition_duration = 18
|
||||
games_count = {}
|
||||
download_tasks = {}
|
||||
download_progress = {}
|
||||
download_result_message = ""
|
||||
download_result_error = False
|
||||
download_result_start_time = 0
|
||||
|
||||
loading_progress = 0.0
|
||||
current_loading_system = ""
|
||||
error_message = ""
|
||||
@@ -58,8 +54,16 @@ pending_download = None
|
||||
controls_config = {}
|
||||
selected_option = 0
|
||||
previous_menu_state = None
|
||||
history = [] # Liste des entrées de l'historique
|
||||
current_history_item = 0 # Index de l'élément sélectionné dans l'historique
|
||||
history = [] # Liste des entrées d'historique avec platform, game_name, status, url, progress, message, timestamp
|
||||
download_progress = {}
|
||||
download_tasks = {} # Dictionnaire pour les tâches de téléchargement
|
||||
download_result_message = ""
|
||||
download_result_error = False
|
||||
download_result_start_time = 0
|
||||
pending_download = None
|
||||
needs_redraw = False
|
||||
current_history_item = 0
|
||||
history_scroll_offset = 0
|
||||
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)
|
||||
confirm_clear_selection = 0 # confirmation clear historique
|
||||
|
||||
133
controls.py
133
controls.py
@@ -8,7 +8,7 @@ import os
|
||||
from display import draw_validation_transition
|
||||
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 history import load_history, clear_history
|
||||
from history import load_history, clear_history, add_to_history, save_history
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -177,7 +177,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif is_input_matched(event, "up"):
|
||||
if current_grid_index - GRID_COLS >= 0:
|
||||
config.selected_platform -= GRID_COLS
|
||||
@@ -186,7 +186,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif is_input_matched(event, "left"):
|
||||
if col > 0:
|
||||
config.selected_platform -= 1
|
||||
@@ -195,7 +195,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif config.current_page > 0:
|
||||
config.current_page -= 1
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS + (GRID_COLS - 1)
|
||||
@@ -206,7 +206,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif is_input_matched(event, "right"):
|
||||
if col < GRID_COLS - 1 and current_grid_index < max_index:
|
||||
config.selected_platform += 1
|
||||
@@ -215,7 +215,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif (config.current_page + 1) * systems_per_page < len(config.platforms):
|
||||
config.current_page += 1
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS
|
||||
@@ -226,7 +226,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
elif is_input_matched(event, "page_down"):
|
||||
if (config.current_page + 1) * systems_per_page < len(config.platforms):
|
||||
config.current_page += 1
|
||||
@@ -238,7 +238,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
#logger.debug("Page suivante, répétition réinitialisée")
|
||||
elif is_input_matched(event, "page_up"):
|
||||
if config.current_page > 0:
|
||||
@@ -251,7 +251,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
#logger.debug("Page précédente, répétition réinitialisée")
|
||||
elif is_input_matched(event, "page_up"):
|
||||
if config.current_page > 0:
|
||||
@@ -264,7 +264,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
# logger.debug(f"Plateforme sélectionnée: {config.selected_platform}")
|
||||
#logger.debug("Page précédente, répétition réinitialisée")
|
||||
elif is_input_matched(event, "progress"):
|
||||
if config.download_tasks:
|
||||
@@ -411,7 +411,6 @@ def handle_controls(event, sources, joystick, screen):
|
||||
logger.debug("Sortie du mode recherche")
|
||||
|
||||
else:
|
||||
|
||||
if is_input_matched(event, "up"):
|
||||
if config.current_game > 0:
|
||||
config.current_game -= 1
|
||||
@@ -435,7 +434,6 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
#logger.debug("Page précédente dans la liste des jeux")
|
||||
elif is_input_matched(event, "page_down"):
|
||||
config.current_game = min(len(games) - 1, config.current_game + config.visible_games)
|
||||
config.repeat_action = None
|
||||
@@ -443,7 +441,6 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
#logger.debug("Page suivante dans la liste des jeux")
|
||||
elif is_input_matched(event, "filter"):
|
||||
config.search_mode = True
|
||||
config.search_query = ""
|
||||
@@ -462,8 +459,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
elif is_input_matched(event, "history"):
|
||||
config.menu_state = "history"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Ouverture history depuis game")
|
||||
|
||||
logger.debug("Ouverture history depuis game")
|
||||
elif is_input_matched(event, "cancel"):
|
||||
config.menu_state = "platform"
|
||||
config.current_game = 0
|
||||
@@ -475,27 +471,39 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.menu_state = "redownload_game_cache"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Passage à redownload_game_cache depuis game")
|
||||
|
||||
# Sélectionner un jeu , evenent confirm
|
||||
# Sélectionner un jeu, événement confirm
|
||||
elif is_input_matched(event, "confirm"):
|
||||
if games:
|
||||
url = games[config.current_game][1]
|
||||
game_name = games[config.current_game][0]
|
||||
platform = config.platforms[config.current_platform]
|
||||
platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform]
|
||||
logger.debug(f"Vérification pour {game_name}, URL: {url}")
|
||||
# Ajouter une entrée temporaire à l'historique
|
||||
config.history.append(add_to_history(
|
||||
platform=platform,
|
||||
game_name=game_name,
|
||||
status="downloading",
|
||||
url=url,
|
||||
progress=0,
|
||||
message="Téléchargement en cours"
|
||||
))
|
||||
config.current_history_item = len(config.history) - 1
|
||||
# Vérifier d'abord si c'est un lien 1fichier
|
||||
if is_1fichier_url(url):
|
||||
if not config.API_KEY_1FICHIER:
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "error"
|
||||
config.error_message = (
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt à ouvrir dans un editeur de texte et coller la clé API"
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt à ouvrir dans un éditeur de texte et coller la clé API"
|
||||
)
|
||||
config.history[-1]["status"] = "Erreur"
|
||||
config.history[-1]["progress"] = 0
|
||||
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.error("Clé API 1fichier absente, téléchargement impossible.")
|
||||
config.pending_download = None
|
||||
return action
|
||||
# Vérifier l'extension pour les liens 1fichier
|
||||
config.pending_download = check_extension_before_download(url, platform, game_name)
|
||||
if config.pending_download:
|
||||
is_supported = is_extension_supported(
|
||||
@@ -509,14 +517,15 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Extension non supportée, passage à extension_warning pour {game_name}")
|
||||
config.history.pop() # Supprimer l'entrée temporaire
|
||||
else:
|
||||
loop = asyncio.get_running_loop()
|
||||
task = loop.run_in_executor(None, download_from_1fichier, url, platform, game_name, config.pending_download[3])
|
||||
config.download_tasks[task] = (task, url, game_name, platform)
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id))
|
||||
config.download_tasks[task_id] = (task, url, game_name, platform)
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "download_progress"
|
||||
config.menu_state = "history" # Passer à l'historique
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Début du téléchargement 1fichier: {game_name} pour {platform} depuis {url}")
|
||||
logger.debug(f"Début du téléchargement 1fichier: {game_name} pour {platform} depuis {url}, task_id={task_id}")
|
||||
config.pending_download = None
|
||||
action = "download"
|
||||
else:
|
||||
@@ -525,8 +534,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.error(f"config.pending_download est None pour {game_name}")
|
||||
config.history.pop() # Supprimer l'entrée temporaire
|
||||
else:
|
||||
# Vérifier l'extension pour les liens non-1fichier
|
||||
config.pending_download = check_extension_before_download(url, platform, game_name)
|
||||
if config.pending_download:
|
||||
is_supported = is_extension_supported(
|
||||
@@ -540,13 +549,15 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Extension non supportée, passage à extension_warning pour {game_name}")
|
||||
config.history.pop() # Supprimer l'entrée temporaire
|
||||
else:
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, config.pending_download[3]))
|
||||
config.download_tasks[task] = (task, url, game_name, platform)
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, config.pending_download[3], task_id))
|
||||
config.download_tasks[task_id] = (task, url, game_name, platform)
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "download_progress"
|
||||
config.menu_state = "history" # Passer à l'historique
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Début du téléchargement: {game_name} pour {platform} depuis {url}")
|
||||
logger.debug(f"Début du téléchargement: {game_name} pour {platform} depuis {url}, task_id={task_id}")
|
||||
config.pending_download = None
|
||||
action = "download"
|
||||
else:
|
||||
@@ -555,6 +566,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.error(f"config.pending_download est None pour {game_name}")
|
||||
config.history.pop() # Supprimer l'entrée temporaire
|
||||
|
||||
# Avertissement extension
|
||||
elif config.menu_state == "extension_warning":
|
||||
@@ -562,6 +574,16 @@ def handle_controls(event, sources, joystick, screen):
|
||||
if config.extension_confirm_selection == 1:
|
||||
if config.pending_download and len(config.pending_download) == 4:
|
||||
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||
# Ajouter une entrée temporaire à l'historique
|
||||
config.history.append(add_to_history(
|
||||
platform=platform,
|
||||
game_name=game_name,
|
||||
status="downloading",
|
||||
url=url,
|
||||
progress=0,
|
||||
message="Téléchargement en cours"
|
||||
))
|
||||
config.current_history_item = len(config.history) - 1
|
||||
if is_1fichier_url(url):
|
||||
if not config.API_KEY_1FICHIER:
|
||||
config.previous_menu_state = config.menu_state
|
||||
@@ -569,19 +591,24 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.error_message = (
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt"
|
||||
)
|
||||
config.history[-1]["status"] = "Erreur"
|
||||
config.history[-1]["progress"] = 0
|
||||
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.error("Clé API 1fichier absente, téléchargement impossible.")
|
||||
config.pending_download = None
|
||||
return action
|
||||
loop = asyncio.get_running_loop()
|
||||
task = loop.run_in_executor(None, download_from_1fichier, url, platform, game_name, is_zip_non_supported)
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, is_zip_non_supported, task_id))
|
||||
else:
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||
config.download_tasks[task] = (task, url, game_name, platform)
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported, task_id))
|
||||
config.download_tasks[task_id] = (task, url, game_name, platform)
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.menu_state = "download_progress"
|
||||
config.menu_state = "history" # Passer à l'historique
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}")
|
||||
logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}, task_id={task_id}")
|
||||
config.pending_download = None
|
||||
action = "download"
|
||||
else:
|
||||
@@ -590,6 +617,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.pending_download = None
|
||||
config.needs_redraw = True
|
||||
logger.error("config.pending_download invalide")
|
||||
config.history.pop() # Supprimer l'entrée temporaire
|
||||
else:
|
||||
config.pending_download = None
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
@@ -598,7 +626,6 @@ def handle_controls(event, sources, joystick, screen):
|
||||
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||
config.extension_confirm_selection = 1 - config.extension_confirm_selection
|
||||
config.needs_redraw = True
|
||||
#logger.debug(f"Changement sélection extension_warning: {config.extension_confirm_selection}")
|
||||
elif is_input_matched(event, "cancel"):
|
||||
config.pending_download = None
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
@@ -653,22 +680,40 @@ def handle_controls(event, sources, joystick, screen):
|
||||
game_name = entry["game_name"]
|
||||
for game in config.games:
|
||||
if game[0] == game_name and config.platforms[config.current_platform] == platform:
|
||||
config.pending_download = check_extension_before_download(game_name, platform, game[1])
|
||||
config.pending_download = check_extension_before_download(game[1], platform, game_name)
|
||||
if config.pending_download:
|
||||
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||
if is_zip_non_supported:
|
||||
config.previous_menu_state = config.menu_state # Remplacer cette ligne
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "extension_warning"
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Extension non supportée pour retéléchargement, passage à extension_warning pour {game_name}")
|
||||
else:
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||
config.download_tasks[task] = (task, url, game_name, platform)
|
||||
config.previous_menu_state = config.menu_state # Remplacer cette ligne
|
||||
config.menu_state = "download_progress"
|
||||
task_id = str(pygame.time.get_ticks())
|
||||
if is_1fichier_url(url):
|
||||
if not config.API_KEY_1FICHIER:
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "error"
|
||||
config.error_message = (
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt"
|
||||
)
|
||||
config.history[-1]["status"] = "Erreur"
|
||||
config.history[-1]["progress"] = 0
|
||||
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.error("Clé API 1fichier absente, retéléchargement impossible.")
|
||||
config.pending_download = None
|
||||
return action
|
||||
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, is_zip_non_supported, task_id))
|
||||
else:
|
||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported, task_id))
|
||||
config.download_tasks[task_id] = (task, url, game_name, platform)
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "history"
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}")
|
||||
logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}, task_id={task_id}")
|
||||
config.pending_download = None
|
||||
action = "redownload"
|
||||
else:
|
||||
|
||||
82
display.py
82
display.py
@@ -1,6 +1,6 @@
|
||||
import pygame # type: ignore
|
||||
import config
|
||||
from utils import truncate_text_middle, wrap_text, load_system_image
|
||||
from utils import truncate_text_middle, wrap_text, load_system_image, truncate_text_end
|
||||
import logging
|
||||
import math
|
||||
from history import load_history # Ajout de l'import
|
||||
@@ -437,13 +437,20 @@ def draw_game_scrollbar(screen, scroll_offset, total_items, visible_items, x, y,
|
||||
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (x, scrollbar_y, 15, scrollbar_height), border_radius=4)
|
||||
|
||||
def draw_history_list(screen):
|
||||
"""Affiche l'historique des téléchargements avec un style moderne."""
|
||||
# logger.debug(f"Dessin historique, history={config.history}, needs_redraw={config.needs_redraw}")
|
||||
history = config.history if hasattr(config, 'history') else load_history()
|
||||
history_count = len(history)
|
||||
|
||||
col_platform_width = int((0.95 * config.screen_width - 60) * 0.33)
|
||||
col_game_width = int((0.95 * config.screen_width - 60) * 0.50)
|
||||
col_status_width = int((0.95 * config.screen_width - 60) * 0.17)
|
||||
# Define column widths as percentages of available space
|
||||
column_width_percentages = {
|
||||
"platform": 0.25, # platform column
|
||||
"game_name": 0.50, # game name column
|
||||
"status": 0.25 # status column
|
||||
}
|
||||
available_width = int(0.95 * config.screen_width - 60) # Total available width for columns
|
||||
col_platform_width = int(available_width * column_width_percentages["platform"])
|
||||
col_game_width = int(available_width * column_width_percentages["game_name"])
|
||||
col_status_width = int(available_width * column_width_percentages["status"])
|
||||
rect_width = int(0.95 * config.screen_width)
|
||||
|
||||
line_height = config.small_font.get_height() + 10
|
||||
@@ -514,7 +521,6 @@ def draw_history_list(screen):
|
||||
text_rect = text_surface.get_rect(center=(x_pos, header_y))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Ajouter un séparateur sous les en-têtes
|
||||
separator_y = rect_y + margin_top_bottom + header_height
|
||||
pygame.draw.line(screen, THEME_COLORS["border"], (rect_x + 20, separator_y), (rect_x + rect_width - 20, separator_y), 2)
|
||||
|
||||
@@ -523,10 +529,28 @@ def draw_history_list(screen):
|
||||
platform = entry.get("platform", "Inconnu")
|
||||
game_name = entry.get("game_name", "Inconnu")
|
||||
status = entry.get("status", "Inconnu")
|
||||
progress = entry.get("progress", 0)
|
||||
# Personnaliser l'affichage du statut
|
||||
if status in ["Téléchargement", "downloading"]:
|
||||
status_text = f"Téléchargement : {progress:.1f}%"
|
||||
# logger.debug(f"Affichage progression: {progress:.1f}% pour {game_name}, status={status_text}")
|
||||
elif status == "Extracting":
|
||||
status_text = f"Extraction : {progress:.1f}%"
|
||||
# logger.debug(f"Affichage extraction: {progress:.1f}% pour {game_name}, status={status_text}")
|
||||
elif status == "Download_OK":
|
||||
status_text = "Terminé"
|
||||
# logger.debug(f"Affichage terminé: {game_name}, status={status_text}")
|
||||
elif status == "Erreur":
|
||||
status_text = f"Erreur : {entry.get('message', 'Échec')}"
|
||||
logger.debug(f"Affichage erreur: {game_name}, status={status_text}")
|
||||
else:
|
||||
status_text = status
|
||||
logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}")
|
||||
|
||||
color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"]
|
||||
platform_text = truncate_text_middle(platform, config.small_font, col_platform_width - 10)
|
||||
game_text = truncate_text_middle(game_name, config.small_font, col_game_width - 10)
|
||||
status_text = truncate_text_middle(status, config.small_font, col_status_width - 10)
|
||||
platform_text = truncate_text_end(platform, config.small_font, col_platform_width - 10)
|
||||
game_text = truncate_text_end(game_name, config.small_font, col_game_width - 10)
|
||||
status_text = truncate_text_middle(status_text, config.small_font, col_status_width - 10, is_filename=False)
|
||||
|
||||
y_pos = rect_y + margin_top_bottom + header_height + idx * line_height + line_height // 2
|
||||
platform_surface = config.small_font.render(platform_text, True, color)
|
||||
@@ -697,31 +721,41 @@ def draw_progress_screen(screen):
|
||||
|
||||
# Écran popup résultat téléchargement
|
||||
def draw_popup_result_download(screen, message, is_error):
|
||||
"""Affiche une popup avec un message de résultat."""
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
"""Affiche une popup flottante centrée avec un message de résultat."""
|
||||
if message is None:
|
||||
message = "Téléchargement annulé par l'utilisateur."
|
||||
logger.debug(f"Message popup : {message}, is_error={is_error}")
|
||||
|
||||
# Réduire la largeur maximale pour le wrapping
|
||||
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 160)
|
||||
# Débogage pour vérifier les lignes wrappées
|
||||
logger.debug(f"Lignes wrappées : {wrapped_message}")
|
||||
max_popup_width = config.screen_width // 3 # Popup prend 1/3 de la largeur de l'écran
|
||||
wrapped_message = wrap_text(message, config.small_font, max_popup_width - 40) # 40 pixels de marge interne
|
||||
line_height = config.small_font.get_height() + 5
|
||||
text_height = len(wrapped_message) * line_height
|
||||
margin_top_bottom = 20
|
||||
margin_top_bottom = 15
|
||||
margin_sides = 20
|
||||
rect_height = text_height + 2 * margin_top_bottom
|
||||
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
|
||||
rect_width = max_text_width + 100 # Augmenter la marge
|
||||
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=200)
|
||||
rect_width = min(max_text_width + 2 * margin_sides, max_popup_width)
|
||||
|
||||
# Positionner la popup au centre de l'écran
|
||||
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)
|
||||
# Fond semi-transparent pour un effet flottant
|
||||
popup_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
|
||||
# Utiliser button_idle sans modifier son alpha (déjà à 150)
|
||||
pygame.draw.rect(popup_surface, THEME_COLORS["button_idle"], (0, 0, rect_width, rect_height), border_radius=10)
|
||||
# Bordure sans alpha modifié
|
||||
pygame.draw.rect(popup_surface, THEME_COLORS["border"], (0, 0, rect_width, rect_height), 2, border_radius=10)
|
||||
|
||||
# Afficher le texte
|
||||
for i, line in enumerate(wrapped_message):
|
||||
text = config.small_font.render(line, True, THEME_COLORS["error_text"] if is_error else THEME_COLORS["fond_lignes"])
|
||||
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)
|
||||
text = config.small_font.render(line, True, THEME_COLORS["error_text"] if is_error else THEME_COLORS["text"])
|
||||
text_rect = text.get_rect(center=(rect_width // 2, margin_top_bottom + i * line_height + line_height // 2))
|
||||
popup_surface.blit(text, text_rect)
|
||||
|
||||
# Appliquer la surface de la popup sur l'écran
|
||||
screen.blit(popup_surface, (rect_x, rect_y))
|
||||
|
||||
# Écran avertissement extension non supportée téléchargement
|
||||
def draw_extension_warning(screen):
|
||||
@@ -795,7 +829,9 @@ def draw_extension_warning(screen):
|
||||
def draw_controls(screen, menu_state):
|
||||
"""Affiche les contrôles sur une seule ligne en bas de l’écran."""
|
||||
start_button = get_control_display('start', 'START')
|
||||
control_text = f"RGSX v{config.app_version} - {start_button} : Options - History - Help"
|
||||
history_button = get_control_display('history', 'H')
|
||||
filter_button = get_control_display('filter', 'F')
|
||||
control_text = f"RGSX v{config.app_version} - {start_button} : Options - {history_button}: Historique - {filter_button} : Filtrer (bug)"
|
||||
max_width = config.screen_width - 40
|
||||
wrapped_controls = wrap_text(control_text, config.small_font, max_width)
|
||||
line_height = config.small_font.get_height() + 5
|
||||
|
||||
35
history.py
35
history.py
@@ -2,6 +2,7 @@ import json
|
||||
import os
|
||||
import logging
|
||||
import config
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -15,26 +16,30 @@ def init_history():
|
||||
if not os.path.exists(history_path):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(history_path), exist_ok=True)
|
||||
with open(history_path, "w") as f:
|
||||
with open(history_path, "w", encoding='utf-8') as f:
|
||||
json.dump([], f) # Initialise avec une liste vide
|
||||
logger.info(f"Fichier d'historique créé : {history_path}")
|
||||
except OSError as e:
|
||||
logger.error(f"Erreur lors de la création du fichier d'historique : {e}")
|
||||
else:
|
||||
logger.info(f"Fichier d'historique trouvé : {history_path}")
|
||||
return history_path
|
||||
return history_path
|
||||
|
||||
def load_history():
|
||||
"""Charge l'historique depuis history.json."""
|
||||
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||
try:
|
||||
with open(history_path, "r") as f:
|
||||
if not os.path.exists(history_path):
|
||||
logger.debug(f"Aucun fichier d'historique trouvé à {history_path}")
|
||||
return []
|
||||
with open(history_path, "r", encoding='utf-8') as f:
|
||||
history = json.load(f)
|
||||
# Valider la structure : liste de dictionnaires avec 'platform', 'game_name', 'status'
|
||||
for entry in history:
|
||||
if not all(key in entry for key in ['platform', 'game_name', 'status']):
|
||||
logger.warning(f"Entrée d'historique invalide : {entry}")
|
||||
return []
|
||||
logger.debug(f"Historique chargé depuis {history_path}, {len(history)} entrées")
|
||||
return history
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
logger.error(f"Erreur lors de la lecture de {history_path} : {e}")
|
||||
@@ -44,28 +49,36 @@ def save_history(history):
|
||||
"""Sauvegarde l'historique dans history.json."""
|
||||
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||
try:
|
||||
with open(history_path, "w") as f:
|
||||
json.dump(history, f, indent=2)
|
||||
os.makedirs(os.path.dirname(history_path), exist_ok=True)
|
||||
with open(history_path, "w", encoding='utf-8') as f:
|
||||
json.dump(history, f, indent=2, ensure_ascii=False)
|
||||
logger.debug(f"Historique sauvegardé dans {history_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'écriture de {history_path} : {e}")
|
||||
|
||||
def add_to_history(platform, game_name, status):
|
||||
def add_to_history(platform, game_name, status, url=None, progress=0, message=None, timestamp=None):
|
||||
"""Ajoute une entrée à l'historique."""
|
||||
history = load_history()
|
||||
history.append({
|
||||
entry = {
|
||||
"platform": platform,
|
||||
"game_name": game_name,
|
||||
"status": status
|
||||
})
|
||||
"status": status,
|
||||
"url": url,
|
||||
"progress": progress,
|
||||
"timestamp": timestamp or datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
if message:
|
||||
entry["message"] = message
|
||||
history.append(entry)
|
||||
save_history(history)
|
||||
logger.info(f"Ajout à l'historique : platform={platform}, game_name={game_name}, status={status}")
|
||||
logger.info(f"Ajout à l'historique : platform={platform}, game_name={game_name}, status={status}, progress={progress}")
|
||||
return entry
|
||||
|
||||
def clear_history():
|
||||
"""Vide l'historique."""
|
||||
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||
try:
|
||||
with open(history_path, "w") as f:
|
||||
with open(history_path, "w", encoding='utf-8') as f:
|
||||
json.dump([], f)
|
||||
logger.info(f"Historique vidé : {history_path}")
|
||||
except Exception as e:
|
||||
|
||||
314
network.py
314
network.py
@@ -8,8 +8,10 @@ import asyncio
|
||||
import config
|
||||
from config import OTA_VERSION_ENDPOINT, OTA_UPDATE_SCRIPT
|
||||
from utils import sanitize_filename, extract_zip, extract_rar
|
||||
from history import add_to_history, load_history
|
||||
from history import save_history
|
||||
import logging
|
||||
import queue
|
||||
import time
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -118,13 +120,26 @@ async def check_for_updates():
|
||||
|
||||
|
||||
|
||||
# File d'attente pour la progression
|
||||
import queue
|
||||
progress_queue = queue.Queue()
|
||||
|
||||
async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
logger.debug(f"Début téléchargement: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}")
|
||||
|
||||
|
||||
async def download_rom(url, platform, game_name, is_zip_non_supported=False, task_id=None):
|
||||
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]
|
||||
|
||||
# Vider la file d'attente avant de commencer
|
||||
while not progress_queue.empty():
|
||||
try:
|
||||
progress_queue.get_nowait()
|
||||
logger.debug(f"File progress_queue vidée pour {game_name}")
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
def download_thread():
|
||||
logger.debug(f"Thread téléchargement démarré pour {url}")
|
||||
logger.debug(f"Thread téléchargement démarré pour {url}, task_id={task_id}")
|
||||
try:
|
||||
dest_dir = None
|
||||
for platform_dict in config.platform_dicts:
|
||||
@@ -132,10 +147,8 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
dest_dir = platform_dict.get("folder")
|
||||
break
|
||||
if not dest_dir:
|
||||
logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}")
|
||||
dest_dir = os.path.join("/userdata/roms", platform)
|
||||
dest_dir = os.path.join("/userdata/roms", platform.lower().replace(" ", ""))
|
||||
|
||||
logger.debug(f"Vérification répertoire destination: {dest_dir}")
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
if not os.access(dest_dir, os.W_OK):
|
||||
raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}")
|
||||
@@ -144,118 +157,106 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
dest_path = os.path.join(dest_dir, f"{sanitized_name}")
|
||||
logger.debug(f"Chemin destination: {dest_path}")
|
||||
|
||||
lock = threading.Lock()
|
||||
with lock:
|
||||
config.download_progress[url] = {
|
||||
"downloaded_size": 0,
|
||||
"total_size": 0,
|
||||
"status": "Téléchargement",
|
||||
"progress_percent": 0,
|
||||
"game_name": game_name
|
||||
}
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
logger.debug(f"Progression initialisée pour {url}")
|
||||
|
||||
headers = {'User-Agent': 'Mozilla/5.0'}
|
||||
logger.debug(f"Envoi requête GET à {url}")
|
||||
response = requests.get(url, stream=True, headers=headers, timeout=30)
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
response.raise_for_status()
|
||||
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
logger.debug(f"Taille totale: {total_size} octets")
|
||||
with lock:
|
||||
config.download_progress[url]["total_size"] = total_size
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
|
||||
# Initialiser la progression avec task_id
|
||||
progress_queue.put((task_id, 0, total_size))
|
||||
logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}")
|
||||
|
||||
downloaded = 0
|
||||
chunk_size = 4096
|
||||
last_update_time = time.time()
|
||||
update_interval = 0.5 # Mettre à jour toutes les 0,5 secondes
|
||||
with open(dest_path, 'wb') as f:
|
||||
logger.debug(f"Ouverture fichier: {dest_path}")
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
for chunk in response.iter_content(chunk_size=chunk_size):
|
||||
if chunk:
|
||||
size_received = len(chunk)
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = downloaded
|
||||
config.download_progress[url]["status"] = "Téléchargement"
|
||||
config.download_progress[url]["progress_percent"] = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
#logger.debug(f"Progression: {downloaded}/{total_size} octets, {config.download_progress[url]['progress_percent']:.1f}%")
|
||||
downloaded += size_received
|
||||
current_time = time.time()
|
||||
if current_time - last_update_time >= update_interval:
|
||||
progress = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
progress_queue.put((task_id, downloaded, total_size))
|
||||
# logger.debug(f"Progress update sent: {progress:.1f}% for {game_name}, task_id={task_id}")
|
||||
last_update_time = current_time
|
||||
else:
|
||||
logger.debug("Chunk vide reçu")
|
||||
|
||||
if is_zip_non_supported:
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = 0
|
||||
config.download_progress[url]["total_size"] = 0
|
||||
config.download_progress[url]["status"] = "Extracting"
|
||||
config.download_progress[url]["progress_percent"] = 0
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
extension = os.path.splitext(dest_path)[1].lower()
|
||||
if extension == ".zip":
|
||||
success, msg = extract_zip(dest_path, dest_dir, url)
|
||||
elif extension == ".rar":
|
||||
success, msg = extract_rar(dest_path, dest_dir, url)
|
||||
else:
|
||||
raise Exception(f"Type d'archive non supporté: {extension}")
|
||||
if not success:
|
||||
raise Exception(f"Échec de l'extraction de l'archive: {msg}")
|
||||
result[0] = True
|
||||
result[1] = f"Downloaded / extracted : {game_name}"
|
||||
else:
|
||||
os.chmod(dest_path, 0o644)
|
||||
logger.debug(f"Téléchargement terminé: {dest_path}")
|
||||
result[0] = True
|
||||
result[1] = f"Download_OK : {game_name}"
|
||||
os.chmod(dest_path, 0o644)
|
||||
logger.debug(f"Téléchargement terminé: {dest_path}")
|
||||
result[0] = True
|
||||
result[1] = f"Download_OK: {game_name}"
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur téléchargement {url}: {str(e)}")
|
||||
if url in config.download_progress:
|
||||
with lock:
|
||||
del config.download_progress[url]
|
||||
if os.path.exists(dest_path):
|
||||
os.remove(dest_path)
|
||||
result[0] = False
|
||||
result[1] = f"Erreur téléchargement {game_name}"
|
||||
result[1] = f"Erreur téléchargement {game_name}: {str(e)}"
|
||||
finally:
|
||||
logger.debug(f"Thread téléchargement terminé pour {url}")
|
||||
with lock:
|
||||
config.download_result_message = result[1]
|
||||
config.download_result_error = not result[0]
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
# Enregistrement dans l'historique
|
||||
add_to_history(platform, game_name, "OK" if result[0] else "Error")
|
||||
config.history = load_history() # Recharger l'historique
|
||||
logger.debug(f"Enregistrement dans l'historique: platform={platform}, game_name={game_name}, status={'Download_OK' if result[0] else 'Erreur'}")
|
||||
logger.debug(f"Thread téléchargement terminé pour {url}, task_id={task_id}")
|
||||
progress_queue.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}")
|
||||
|
||||
thread = threading.Thread(target=download_thread)
|
||||
logger.debug(f"Démarrage thread pour {url}")
|
||||
thread.start()
|
||||
while thread.is_alive():
|
||||
pygame.event.pump()
|
||||
await asyncio.sleep(0.1)
|
||||
thread.join()
|
||||
logger.debug(f"Thread rejoint pour {url}")
|
||||
|
||||
# Boucle principale pour mettre à jour la progression
|
||||
while thread.is_alive():
|
||||
try:
|
||||
while not progress_queue.empty():
|
||||
data = progress_queue.get()
|
||||
logger.debug(f"Progress queue data received: {data}")
|
||||
if len(data) != 3 or data[0] != task_id: # Ignorer les données d'une autre tâche
|
||||
logger.debug(f"Ignoring queue data for task_id={data[0]}, expected={task_id}")
|
||||
continue
|
||||
if isinstance(data[1], bool): # Fin du téléchargement
|
||||
success, message = data[1], data[2]
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["status"] = "Download_OK" if success else "Erreur"
|
||||
entry["progress"] = 100 if success else 0
|
||||
entry["message"] = message
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
|
||||
break
|
||||
else:
|
||||
downloaded, total_size = data[1], data[2]
|
||||
progress = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["progress"] = progress
|
||||
entry["status"] = "Téléchargement"
|
||||
config.needs_redraw = True
|
||||
# logger.debug(f"Progress updated in history: {progress:.1f}% for {game_name}, task_id={task_id}")
|
||||
break
|
||||
await asyncio.sleep(0.2)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur mise à jour progression: {str(e)}")
|
||||
|
||||
thread.join()
|
||||
logger.debug(f"Thread joined for {url}, task_id={task_id}")
|
||||
return result[0], result[1]
|
||||
|
||||
|
||||
def is_1fichier_url(url):
|
||||
"""Détecte si l'URL est un lien 1fichier."""
|
||||
return "1fichier.com" in url
|
||||
|
||||
|
||||
def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False):
|
||||
"""Télécharge un fichier depuis 1fichier en utilisant l'API officielle."""
|
||||
logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}")
|
||||
async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None):
|
||||
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}")
|
||||
result = [None, None]
|
||||
|
||||
def download_thread():
|
||||
logger.debug(f"Thread téléchargement 1fichier démarré pour {url}")
|
||||
# Vider la file d'attente avant de commencer
|
||||
while not progress_queue.empty():
|
||||
try:
|
||||
# Nettoyer l'URL
|
||||
link = url.split('&af=')[0]
|
||||
progress_queue.get_nowait()
|
||||
logger.debug(f"File progress_queue vidée pour {game_name}")
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
# Déterminer le répertoire de destination
|
||||
def download_thread():
|
||||
logger.debug(f"Thread téléchargement 1fichier démarré pour {url}, task_id={task_id}")
|
||||
try:
|
||||
link = url.split('&af=')[0]
|
||||
dest_dir = None
|
||||
for platform_dict in config.platform_dicts:
|
||||
if platform_dict["platform"] == platform:
|
||||
@@ -270,7 +271,6 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
if not os.access(dest_dir, os.W_OK):
|
||||
raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}")
|
||||
|
||||
# Préparer les en-têtes et le payload
|
||||
headers = {
|
||||
"Authorization": f"Bearer {config.API_KEY_1FICHIER}",
|
||||
"Content-Type": "application/json"
|
||||
@@ -280,7 +280,6 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
"pretty": 1
|
||||
}
|
||||
|
||||
# Étape 1 : Obtenir les informations du fichier
|
||||
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)
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
@@ -304,7 +303,6 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
dest_path = os.path.join(dest_dir, sanitized_filename)
|
||||
logger.debug(f"Chemin destination: {dest_path}")
|
||||
|
||||
# Étape 2 : Obtenir le jeton de téléchargement
|
||||
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)
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
@@ -318,22 +316,12 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
result[1] = "Impossible de récupérer l'URL de téléchargement"
|
||||
return
|
||||
|
||||
# Étape 3 : Initialiser la progression
|
||||
lock = threading.Lock()
|
||||
with lock:
|
||||
config.download_progress[url] = {
|
||||
"downloaded_size": 0,
|
||||
"total_size": 0,
|
||||
"status": "Téléchargement",
|
||||
"progress_percent": 0,
|
||||
"game_name": game_name
|
||||
}
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Progression initialisée pour {url}")
|
||||
|
||||
# Étape 4 : Télécharger le fichier
|
||||
retries = 10
|
||||
retry_delay = 10
|
||||
# Initialiser la progression avec task_id
|
||||
progress_queue.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):
|
||||
try:
|
||||
logger.debug(f"Tentative {attempt + 1} : Envoi requête GET à {final_url}")
|
||||
@@ -343,31 +331,44 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
logger.debug(f"Taille totale: {total_size} octets")
|
||||
with lock:
|
||||
config.download_progress[url]["total_size"] = total_size
|
||||
config.needs_redraw = True
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] == "downloading":
|
||||
entry["total_size"] = total_size
|
||||
config.needs_redraw = True
|
||||
break
|
||||
progress_queue.put((task_id, 0, total_size)) # Mettre à jour la taille totale
|
||||
|
||||
downloaded = 0
|
||||
chunk_size = 8192
|
||||
last_update_time = time.time()
|
||||
update_interval = 0.5 # Mettre à jour toutes les 0,5 secondes
|
||||
with open(dest_path, 'wb') as f:
|
||||
logger.debug(f"Ouverture fichier: {dest_path}")
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
for chunk in response.iter_content(chunk_size=chunk_size):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = downloaded
|
||||
config.download_progress[url]["status"] = "Téléchargement"
|
||||
config.download_progress[url]["progress_percent"] = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
config.needs_redraw = True
|
||||
#logger.debug(f"Progression: {downloaded}/{total_size} octets, {config.download_progress[url]['progress_percent']:.1f}%")
|
||||
current_time = time.time()
|
||||
if current_time - last_update_time >= update_interval:
|
||||
with lock:
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] == "downloading":
|
||||
entry["progress"] = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
entry["status"] = "Téléchargement"
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Progression mise à jour: {entry['progress']:.1f}% pour {game_name}")
|
||||
break
|
||||
progress_queue.put((task_id, downloaded, total_size))
|
||||
last_update_time = current_time
|
||||
|
||||
# Étape 5 : Extraire si nécessaire
|
||||
if is_zip_non_supported:
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = 0
|
||||
config.download_progress[url]["total_size"] = 0
|
||||
config.download_progress[url]["status"] = "Extracting"
|
||||
config.download_progress[url]["progress_percent"] = 0
|
||||
config.needs_redraw = True
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] == "Téléchargement":
|
||||
entry["progress"] = 0
|
||||
entry["status"] = "Extracting"
|
||||
config.needs_redraw = True
|
||||
break
|
||||
extension = os.path.splitext(dest_path)[1].lower()
|
||||
if extension == ".zip":
|
||||
success, msg = extract_zip(dest_path, dest_dir, url)
|
||||
@@ -389,7 +390,6 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Tentative {attempt + 1} échouée : {e}")
|
||||
if attempt < retries - 1:
|
||||
import time
|
||||
time.sleep(retry_delay)
|
||||
else:
|
||||
logger.error("Nombre maximum de tentatives atteint")
|
||||
@@ -400,25 +400,57 @@ def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False)
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Erreur API 1fichier : {e}")
|
||||
result[0] = False
|
||||
result[1] = f"Erreur lors de la requête API, la clé est peut etre incorrecte: {str(e)}"
|
||||
result[1] = f"Erreur lors de la requête API, la clé est peut-être incorrecte: {str(e)}"
|
||||
|
||||
finally:
|
||||
logger.debug(f"Thread téléchargement 1fichier terminé pour {url}")
|
||||
with lock:
|
||||
config.download_result_message = result[1]
|
||||
config.download_result_error = not result[0]
|
||||
config.download_result_start_time = pygame.time.get_ticks()
|
||||
config.menu_state = "download_result"
|
||||
config.needs_redraw = True
|
||||
# Enregistrement dans l'historique
|
||||
add_to_history(platform, game_name, "Download_OK" if result[0] else "Erreur")
|
||||
config.history = load_history()
|
||||
logger.debug(f"Enregistrement dans l'historique: platform={platform}, game_name={game_name}, status={'Download_OK' if result[0] else 'Erreur'}")
|
||||
logger.debug(f"Thread téléchargement 1fichier terminé pour {url}, task_id={task_id}")
|
||||
progress_queue.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}")
|
||||
|
||||
thread = threading.Thread(target=download_thread)
|
||||
logger.debug(f"Démarrage thread pour {url}")
|
||||
logger.debug(f"Démarrage thread pour {url}, task_id={task_id}")
|
||||
thread.start()
|
||||
thread.join()
|
||||
logger.debug(f"Thread rejoint pour {url}")
|
||||
|
||||
return result[0], result[1]
|
||||
# Boucle principale pour mettre à jour la progression
|
||||
while thread.is_alive():
|
||||
try:
|
||||
while not progress_queue.empty():
|
||||
data = progress_queue.get()
|
||||
logger.debug(f"Progress queue data received: {data}")
|
||||
if len(data) != 3 or data[0] != task_id: # Ignorer les données d'une autre tâche
|
||||
logger.debug(f"Ignoring queue data for task_id={data[0]}, expected={task_id}")
|
||||
continue
|
||||
if isinstance(data[1], bool): # Fin du téléchargement
|
||||
success, message = data[1], data[2]
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["status"] = "Download_OK" if success else "Erreur"
|
||||
entry["progress"] = 100 if success else 0
|
||||
entry["message"] = message
|
||||
save_history(config.history)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Final update in history: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
|
||||
break
|
||||
else:
|
||||
downloaded, total_size = data[1], data[2]
|
||||
progress = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["downloading", "Téléchargement"]:
|
||||
entry["progress"] = progress
|
||||
entry["status"] = "Téléchargement"
|
||||
config.needs_redraw = True
|
||||
# logger.debug(f"Progress updated in history: {progress:.1f}% for {game_name}, task_id={task_id}")
|
||||
break
|
||||
await asyncio.sleep(0.2)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur mise à jour progression: {str(e)}")
|
||||
|
||||
thread.join()
|
||||
logger.debug(f"Thread joined for {url}, task_id={task_id}")
|
||||
return result[0], result[1]
|
||||
|
||||
|
||||
def is_1fichier_url(url):
|
||||
"""Détecte si l'URL est un lien 1fichier."""
|
||||
return "1fichier.com" in url
|
||||
|
||||
|
||||
72
utils.py
72
utils.py
@@ -9,8 +9,10 @@ import subprocess
|
||||
import config
|
||||
import threading
|
||||
import zipfile
|
||||
import time
|
||||
import random
|
||||
from config import JSON_EXTENSIONS
|
||||
from history import save_history
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
@@ -177,11 +179,12 @@ def write_unavailable_systems():
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'écriture du fichier {log_file} : {str(e)}")
|
||||
|
||||
|
||||
def truncate_text_middle(text, font, max_width):
|
||||
"""Tronque le texte en insérant '...' au milieu, en préservant le début et la fin, sans extension de fichier."""
|
||||
# Supprimer l'extension de fichier
|
||||
text = text.rsplit('.', 1)[0] if '.' in text else text
|
||||
def truncate_text_middle(text, font, max_width, is_filename=True):
|
||||
"""Tronque le texte en insérant '...' au milieu, en préservant le début et la fin.
|
||||
Si is_filename=False, ne supprime pas l'extension."""
|
||||
# Supprimer l'extension uniquement si is_filename est True
|
||||
if is_filename:
|
||||
text = text.rsplit('.', 1)[0] if '.' in text else text
|
||||
text_width = font.size(text)[0]
|
||||
if text_width <= max_width:
|
||||
return text
|
||||
@@ -191,7 +194,7 @@ def truncate_text_middle(text, font, max_width):
|
||||
if max_text_width <= 0:
|
||||
return ellipsis
|
||||
|
||||
# Diviser la largeur disponible entre début et fin
|
||||
# Diviser la largeur disponible entre début et fin, en priorisant la fin
|
||||
chars = list(text)
|
||||
left = []
|
||||
right = []
|
||||
@@ -200,14 +203,9 @@ def truncate_text_middle(text, font, max_width):
|
||||
left_idx = 0
|
||||
right_idx = len(chars) - 1
|
||||
|
||||
# Préserver plus de caractères à droite pour garder le '%'
|
||||
while left_idx <= right_idx and (left_width + right_width) < max_text_width:
|
||||
if left_idx < right_idx:
|
||||
left.append(chars[left_idx])
|
||||
left_width = font.size(''.join(left))[0]
|
||||
if left_width + right_width > max_text_width:
|
||||
left.pop()
|
||||
break
|
||||
left_idx += 1
|
||||
# Ajouter à droite en priorité
|
||||
if left_idx <= right_idx:
|
||||
right.insert(0, chars[right_idx])
|
||||
right_width = font.size(''.join(right))[0]
|
||||
@@ -215,6 +213,14 @@ def truncate_text_middle(text, font, max_width):
|
||||
right.pop(0)
|
||||
break
|
||||
right_idx -= 1
|
||||
# Ajouter à gauche seulement si nécessaire
|
||||
if left_idx < right_idx:
|
||||
left.append(chars[left_idx])
|
||||
left_width = font.size(''.join(left))[0]
|
||||
if left_width + right_width > max_text_width:
|
||||
left.pop()
|
||||
break
|
||||
left_idx += 1
|
||||
|
||||
# Reculer jusqu'à un espace pour éviter de couper un mot
|
||||
while left and left[-1] != ' ' and left_width + right_width > max_text_width:
|
||||
@@ -305,12 +311,13 @@ def load_system_image(platform_dict):
|
||||
return None
|
||||
|
||||
|
||||
# Fonction pour extraire le contenu d'un fichier ZIP
|
||||
def extract_zip(zip_path, dest_dir, url):
|
||||
"""Extrait le contenu du fichier ZIP dans le dossier cible avec un suivi progressif de la progression."""
|
||||
logger.debug(f"Extraction de {zip_path} dans {dest_dir}")
|
||||
try:
|
||||
lock = threading.Lock()
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
zip_ref.testzip() # Vérifier l'intégrité de l'archive
|
||||
total_size = sum(info.file_size for info in zip_ref.infolist() if not info.is_dir())
|
||||
logger.info(f"Taille totale à extraire: {total_size} octets")
|
||||
if total_size == 0:
|
||||
@@ -319,7 +326,9 @@ def extract_zip(zip_path, dest_dir, url):
|
||||
|
||||
extracted_size = 0
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
chunk_size = 8192
|
||||
chunk_size = 2048 # Réduire pour plus de mises à jour
|
||||
last_save_time = time.time()
|
||||
save_interval = 0.5 # Sauvegarder toutes les 0.5 secondes
|
||||
for info in zip_ref.infolist():
|
||||
if info.is_dir():
|
||||
continue
|
||||
@@ -335,15 +344,19 @@ def extract_zip(zip_path, dest_dir, url):
|
||||
dest.write(chunk)
|
||||
file_extracted += len(chunk)
|
||||
extracted_size += len(chunk)
|
||||
current_time = time.time()
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = extracted_size
|
||||
config.download_progress[url]["total_size"] = total_size
|
||||
config.download_progress[url]["status"] = "Extracting"
|
||||
config.download_progress[url]["progress_percent"] = (extracted_size / total_size * 100) if total_size > 0 else 0
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
# Logger une seule ligne à la fin de l'extraction du fichier
|
||||
progress_percentage = (extracted_size / total_size * 100) if total_size > 0 else 0
|
||||
logger.debug(f"Extraction terminée pour {info.filename}, file_extracted: {file_extracted}/{file_size}, total_extracted: {extracted_size}/{total_size}, progression: {progress_percentage:.1f}%")
|
||||
for entry in config.history:
|
||||
if entry["url"] == url and entry["status"] in ["Téléchargement", "Extracting"]:
|
||||
entry["status"] = "Extracting"
|
||||
entry["progress"] = (extracted_size / total_size * 100) if total_size > 0 else 0
|
||||
entry["message"] = "Extraction en cours"
|
||||
if current_time - last_save_time >= save_interval:
|
||||
save_history(config.history)
|
||||
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={entry['progress']:.1f}%")
|
||||
config.needs_redraw = True
|
||||
break
|
||||
os.chmod(file_path, 0o644)
|
||||
|
||||
for root, dirs, _ in os.walk(dest_dir):
|
||||
@@ -352,10 +365,17 @@ def extract_zip(zip_path, dest_dir, url):
|
||||
|
||||
os.remove(zip_path)
|
||||
logger.info(f"Fichier ZIP {zip_path} extrait dans {dest_dir} et supprimé")
|
||||
return True, "ZIP extrait avec succès"
|
||||
return True, f"Extracted: {os.path.basename(zip_path)}"
|
||||
except zipfile.BadZipFile as e:
|
||||
logger.error(f"Erreur: Archive ZIP corrompue: {str(e)}")
|
||||
return False, f"Archive ZIP corrompue: {str(e)}"
|
||||
except PermissionError as e:
|
||||
logger.error(f"Erreur: Permission refusée lors de l'extraction: {str(e)}")
|
||||
return False, f"Permission refusée lors de l'extraction: {str(e)}"
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'extraction de {zip_path}: {e}")
|
||||
return False, str(e)
|
||||
logger.error(f"Erreur lors de l'extraction de {zip_path}: {str(e)}")
|
||||
return False, f"Échec de l'extraction: {str(e)}"
|
||||
|
||||
|
||||
# Fonction pour extraire le contenu d'un fichier RAR
|
||||
def extract_rar(rar_path, dest_dir, url):
|
||||
|
||||
Reference in New Issue
Block a user