Amélioration historique, suppression fenetre progression intégrée, et ajout controles en bas de l'ecran

This commit is contained in:
skymike03
2025-07-15 12:28:32 +02:00
parent 78343143ad
commit 6958897a45
8 changed files with 558 additions and 280 deletions

View File

@@ -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 peuttre 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