1
0
forked from Mirrors/RGSX
- correct ps3 extracting/folder_rename bug
- added support for Anbernic RG35XX
- added support for local custom source of games (games.zip) in /saves/ports/rgsx folder when choose "custom source" on rgsx games settings.
This commit is contained in:
retrogamesets
2025-09-16 22:08:46 +02:00
parent 7b6d839654
commit fa394ecc15
2 changed files with 193 additions and 62 deletions

View File

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

View File

@@ -720,6 +720,11 @@ def extract_zip_data(zip_path, dest_dir, url):
"""Extrait le contenu du fichier ZIP dans le dossier config.APP_FOLDER sans progression a l'ecran"""
logger.debug(f"Extraction de {zip_path} dans {dest_dir}")
try:
# Capture existing directories before extraction to identify the newly created one(s)
try:
before_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
before_dirs = set()
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.testzip() # Vérifier l'intégrité de l'archive
for info in zip_ref.infolist():
@@ -730,6 +735,19 @@ def extract_zip_data(zip_path, dest_dir, url):
with zip_ref.open(info) as source, open(file_path, 'wb') as dest:
shutil.copyfileobj(source, dest)
logger.info(f"Extraction terminée de {zip_path}")
# PS3: renommer uniquement le dossier nouvellement extrait (parité avec RAR)
ps3_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "ps3")
if dest_dir == ps3_dir:
try:
after_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
after_dirs = set()
ignore_names = {"ps3", "images", "videos", "manuals", "media"}
new_dirs = [d for d in (after_dirs - before_dirs) if d not in ignore_names and not d.endswith('.ps3')]
expected_base = os.path.splitext(os.path.basename(zip_path))[0]
success, error_msg = handle_ps3(dest_dir, new_dirs=new_dirs, extracted_basename=expected_base)
if not success:
return False, error_msg
return True, "Extraction terminée avec succès"
except zipfile.BadZipFile as e:
logger.error(f"Erreur: Archive ZIP corrompue: {str(e)}")
@@ -754,6 +772,12 @@ def extract_zip(zip_path, dest_dir, url):
if file.lower().endswith('.iso'):
iso_before.add(os.path.abspath(os.path.join(root, file)))
# Capturer les dossiers avant extraction (pour détection PS3)
try:
before_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
before_dirs = set()
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())
@@ -817,6 +841,20 @@ def extract_zip(zip_path, dest_dir, url):
logger.warning("Aucun nouvel ISO détecté après extraction pour conversion Xbox.")
# On ne retourne pas d'erreur fatale ici, on continue
# PS3: renommer uniquement le dossier nouvellement extrait (parité avec RAR)
ps3_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "ps3")
if dest_dir == ps3_dir:
try:
after_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
after_dirs = set()
ignore_names = {"ps3", "images", "videos", "manuals", "media"}
new_dirs = [d for d in (after_dirs - before_dirs) if d not in ignore_names and not d.endswith('.ps3')]
expected_base = os.path.splitext(os.path.basename(zip_path))[0]
success, error_msg = handle_ps3(dest_dir, new_dirs=new_dirs, extracted_basename=expected_base)
if not success:
return False, error_msg
try:
os.remove(zip_path)
logger.info(f"Fichier ZIP {zip_path} extrait dans {dest_dir} et supprimé")
@@ -910,7 +948,7 @@ def extract_rar(rar_path, dest_dir, url):
root_dir = file_name.split('/')[0] if '/' in file_name else ''
if root_dir:
root_dirs.add(root_dir)
logger.debug(f"Ligne parsée: {file_name}, taille: {file_size}, date: {file_date}")
#logger.debug(f"Ligne parsée: {file_name}, taille: {file_size}, date: {file_date}")
else:
logger.debug(f"Dossier ignoré: {file_name}")
else:
@@ -933,10 +971,28 @@ def extract_rar(rar_path, dest_dir, url):
config.download_progress[url]["status"] = "Extracting"
config.download_progress[url]["progress_percent"] = 0
config.needs_redraw = True
# Mettre à jour l'historique pour indiquer le début d'extraction
try:
if isinstance(config.history, list):
for entry in config.history:
if entry.get("url") == url and entry.get("status") in ["Téléchargement", "downloading", "Extracting"]:
entry["status"] = "Extracting"
entry["progress"] = 0
entry["message"] = "Extraction en cours"
save_history(config.history)
break
except Exception:
pass
except Exception as e:
logger.error(f"Erreur lors de la mise à jour de la progression: {str(e)}")
# Continuer l'extraction même en cas d'erreur de mise à jour de la progression
# Capture existing directories before extraction to identify the newly created one(s)
try:
before_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
before_dirs = set()
escaped_rar_path = rar_path.replace(" ", "\\ ")
escaped_dest_dir = dest_dir.replace(" ", "\\ ")
process = subprocess.Popen(unrar_cmd + ['x', '-y', escaped_rar_path, escaped_dest_dir],
@@ -948,6 +1004,9 @@ def extract_rar(rar_path, dest_dir, url):
return False, f"Erreur lors de l'extraction: {stderr}"
extracted_size = 0
# Sauvegarde périodique de l'historique (comme ZIP)
last_save_time = time.time()
save_interval = 0.5
extracted_files = []
total_files = len(files_to_extract)
for i, (expected_file, file_size) in enumerate(files_to_extract):
@@ -956,14 +1015,31 @@ def extract_rar(rar_path, dest_dir, url):
extracted_size += file_size
extracted_files.append(expected_file)
os.chmod(file_path, 0o644)
logger.debug(f"Fichier extrait: {expected_file}, taille: {file_size}, chemin: {file_path}")
#logger.debug(f"Fichier extrait: {expected_file}, taille: {file_size}, chemin: {file_path}")
try:
with lock:
if url in config.download_progress:
percent = int(((i + 1) / total_files * 100)) if total_files > 0 else 0
percent = max(0, min(100, percent))
config.download_progress[url]["downloaded_size"] = extracted_size
config.download_progress[url]["status"] = "Extracting"
config.download_progress[url]["progress_percent"] = ((i + 1) / total_files * 100) if total_files > 0 else 0
config.download_progress[url]["progress_percent"] = percent
config.needs_redraw = True
# MAJ historique (progression extraction)
try:
if isinstance(config.history, list):
for entry in config.history:
if entry.get("url") == url and entry.get("status") in ["Téléchargement", "Extracting", "downloading"]:
entry["status"] = "Extracting"
entry["progress"] = percent
entry["message"] = "Extraction en cours"
now = time.time()
if now - last_save_time >= save_interval:
save_history(config.history)
last_save_time = now
break
except Exception:
pass
except Exception as e:
logger.error(f"Erreur lors de la mise à jour de la progression d'extraction: {str(e)}")
# Continuer l'extraction même en cas d'erreur de mise à jour de la progression
@@ -971,9 +1047,18 @@ def extract_rar(rar_path, dest_dir, url):
logger.warning(f"Fichier non trouvé après extraction: {expected_file}")
# Vérifier si c'est un dossier PS3 et le traiter si nécessaire
ps3_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "ps3")
ps3_dir = os.path.join(config.ROMS_FOLDER, "ps3")
if dest_dir == ps3_dir:
success, error_msg = handle_ps3(dest_dir)
# Déterminer les nouveaux dossiers créés par cette extraction
try:
after_dirs = set([d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))])
except Exception:
after_dirs = set()
ignore_names = {"ps3", "images", "videos", "manuals", "media"}
new_dirs = [d for d in (after_dirs - before_dirs) if d not in ignore_names and not d.endswith('.ps3')]
# Nom attendu à partir du nom de l'archive
expected_base = os.path.splitext(os.path.basename(rar_path))[0]
success, error_msg = handle_ps3(dest_dir, new_dirs=new_dirs, extracted_basename=expected_base)
if not success:
return False, error_msg
@@ -981,6 +1066,29 @@ def extract_rar(rar_path, dest_dir, url):
for dir_name in dirs:
os.chmod(os.path.join(root, dir_name), 0o755)
# Finaliser: marquer comme terminé dans l'historique et la progression
try:
# Mettre à jour l'historique final
if isinstance(config.history, list):
for entry in config.history:
if entry.get("url") == url and entry.get("status") in ("Extracting", "Téléchargement", "downloading"):
entry["status"] = "Download_OK"
entry["progress"] = 100
message_text = _("utils_extracted").format(os.path.basename(rar_path))
entry["message"] = message_text
save_history(config.history)
config.needs_redraw = True
break
# Mettre à jour la progression
if url in getattr(config, 'download_progress', {}):
try:
config.download_progress[url]["status"] = "Download_OK"
config.download_progress[url]["progress_percent"] = 100
except Exception:
pass
except Exception as e:
logger.debug(f"MAJ statut final après extraction RAR: ignorée ({e})")
os.remove(rar_path)
logger.info(f"Fichier RAR {rar_path} extrait dans {dest_dir} et supprimé")
return True, "RAR extrait avec succès"
@@ -996,68 +1104,91 @@ def extract_rar(rar_path, dest_dir, url):
except Exception as e:
logger.error(f"Erreur lors de la suppression de {rar_path}: {str(e)}")
def handle_ps3(dest_dir):
"""Gère le renommage spécifique des dossiers PS3 extraits."""
def handle_ps3(dest_dir, new_dirs=None, extracted_basename=None):
"""Gère le renommage spécifique des dossiers PS3 extraits.
- Si new_dirs est fourni, ne considère que ces dossiers.
- Ignore les dossiers système connus: images, videos, manuals, ps3, media.
- Essaie de faire correspondre le dossier attendu au nom du RAR (underscores -> espaces).
"""
logger.debug(f"Traitement spécifique PS3 dans: {dest_dir}")
# Attendre un peu que tous les processus d'extraction se terminent
time.sleep(2)
# Rechercher le dossier extrait directement dans dest_dir
extracted_dirs = [d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))]
logger.debug(f"Dossiers trouvés dans {dest_dir}: {extracted_dirs}")
# Filtrer pour ne garder que les dossiers nouvellement extraits
ps3_dirs = [d for d in extracted_dirs if not d.endswith('.ps3')]
logger.debug(f"Dossiers PS3 à renommer: {ps3_dirs}")
if len(ps3_dirs) == 1:
old_path = os.path.join(dest_dir, ps3_dirs[0])
new_path = os.path.join(dest_dir, f"{ps3_dirs[0]}.ps3")
logger.debug(f"Tentative de renommage PS3: {old_path} -> {new_path}")
max_retries = 3
retry_delay = 2
for attempt in range(max_retries):
try:
# Fermer les handles potentiellement ouverts
for root, dirs, files in os.walk(old_path):
for f in files:
try:
os.chmod(os.path.join(root, f), 0o644)
except (OSError, PermissionError):
pass
for d in dirs:
try:
os.chmod(os.path.join(root, d), 0o755)
except (OSError, PermissionError):
pass
time.sleep(2) # petite latence post-extraction
if os.path.exists(new_path):
shutil.rmtree(new_path, ignore_errors=True)
time.sleep(1)
ignore_names = {"ps3", "images", "videos", "manuals", "media"}
os.rename(old_path, new_path)
logger.info(f"Dossier renommé avec succès: {old_path} -> {new_path}")
return True, None
except Exception as e:
logger.warning(f"Tentative {attempt + 1}/{max_retries} échouée: {str(e)}")
if attempt < max_retries - 1:
time.sleep(retry_delay)
else:
error_msg = f"Erreur lors du renommage de {old_path} en {new_path}: {str(e)}"
logger.error(error_msg)
return False, error_msg
elif len(ps3_dirs) > 1:
logger.warning(f"Plusieurs dossiers PS3 détectés: {ps3_dirs}")
return True, None
if new_dirs is None:
try:
candidates = [d for d in os.listdir(dest_dir) if os.path.isdir(os.path.join(dest_dir, d))]
except Exception:
candidates = []
else:
logger.warning("Aucun dossier PS3 à renommer trouvé")
candidates = list(new_dirs)
# Filtrer: ignorer .ps3 déjà traité et dossiers système
ps3_dirs = [d for d in candidates if not d.endswith('.ps3') and d not in ignore_names]
logger.debug(f"Dossiers PS3 candidats: {ps3_dirs}")
# Tenter une correspondance au nom de l'archive (remplacer '_' -> ' ' et normaliser)
target = None
if extracted_basename:
def norm(s: str) -> str:
s = s.replace('_', ' ')
s = ' '.join(s.split()).strip().lower()
return s
expected = norm(extracted_basename)
for d in ps3_dirs:
if norm(d) == expected:
target = d
break
# Si pas de match exact: si un seul candidat, prendre celui-ci
if target is None and len(ps3_dirs) == 1:
target = ps3_dirs[0]
if not target:
if ps3_dirs:
logger.warning(f"Plusieurs dossiers PS3 détectés (aucune correspondance unique): {ps3_dirs}")
else:
logger.warning("Aucun dossier PS3 à renommer trouvé")
return True, None
old_path = os.path.join(dest_dir, target)
new_path = os.path.join(dest_dir, f"{target}.ps3")
logger.debug(f"Tentative de renommage PS3: {old_path} -> {new_path}")
max_retries = 3
retry_delay = 2
for attempt in range(max_retries):
try:
# Fermer les handles potentiellement ouverts
for root, dirs, files in os.walk(old_path):
for f in files:
try:
os.chmod(os.path.join(root, f), 0o644)
except (OSError, PermissionError):
pass
for d in dirs:
try:
os.chmod(os.path.join(root, d), 0o755)
except (OSError, PermissionError):
pass
if os.path.exists(new_path):
shutil.rmtree(new_path, ignore_errors=True)
time.sleep(1)
os.rename(old_path, new_path)
logger.info(f"Dossier renommé avec succès: {old_path} -> {new_path}")
return True, None
except Exception as e:
logger.warning(f"Tentative {attempt + 1}/{max_retries} échouée: {str(e)}")
if attempt < max_retries - 1:
time.sleep(retry_delay)
else:
error_msg = f"Erreur lors du renommage de {old_path} en {new_path}: {str(e)}"
logger.error(error_msg)
return False, error_msg
def handle_xbox(dest_dir, iso_files, url=None):
"""Gère la conversion des fichiers Xbox extraits et met à jour l'UI (Converting)."""