Afegit: media_mover.py

Retocat: fbneo_roms_by_manufacturer.py
This commit is contained in:
2025-01-12 20:28:52 +01:00
parent 9400205773
commit 70db46d4f2
8 changed files with 173541 additions and 257 deletions
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
# Fichero a usar en caso de no especificar ninguno por parametro
XML_FILE="FinalBurn Neo v1.0.0.02 (ClrMame Pro XML).dat"
# Opciones globales
IGNORE_LIST = ["DECO Cassette", "Bootleg", "prototype", "Quiz", "Mahjong", "Demo"]
IGNORE_SYSTEM = ["neogeo"]
# Directorio de entrada a usar en caso de no especificar ninguno por parametro
INPUT_FOLDER="/sustancia/roms/FB Neo v1.0.0.2/roms/arcade"
# Directorio de salida a usar en caso de no especificar ninguno por parametro
OUTPUT_FOLDER="/home/sergio/tmp"
@@ -0,0 +1,310 @@
# Script para copiar roms a partir de un xml de fbneo
# Copia las roms por desarrollador y sin clones
# Por hacer:
# pasar por parametro si se quieren clones
import os
import shutil
import sys
import argparse
import config
from xml.dom import minidom
def normalize_path(path):
"""Elimina los caracteres ilegales de la cadena de texto"""
# Lista de caracteres ilegales que deben ser eliminados
illegal_chars = ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]
# Carácter de reemplazo
replace_with = "_"
# Reemplaza cada carácter ilegal en la cadena de texto
for char in illegal_chars:
path = path.replace(char, replace_with)
return path
def parse_arguments():
"""Parsea los argumentos de la línea de comandos"""
parser = argparse.ArgumentParser(
description="Gestiona las roms de fbneo por desarrolladores a partir de un fichero .dat"
)
# Grupo de argumentos mutuos exclusivos
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-l",
"--list",
help="Muestra la lista de desarrolladores o de juegos",
action="store_true",
)
group.add_argument(
"-c",
"--copy",
help="Copia las roms",
action="store_true"
)
group.add_argument(
"-f",
"--find",
help="Busca juegos que contengan la cadena de texto especificada",
)
# Otros argumentos
parser.add_argument(
"-s",
"--sort",
help="Ordena los resultados por carpetas de desarrollador al copiar todas las roms",
action="store_true",
)
parser.add_argument(
"-cf",
"--create_folder",
help="Copia las roms dentro de la carpeta del desarrollador",
action="store_true",
)
parser.add_argument(
"-m",
"--manufacturer",
help="Selecciona un desarrollador"
)
parser.add_argument(
"-d",
"--dat",
help="Ruta del fichero .dat con información de las roms"
)
parser.add_argument(
"-i",
"--input",
help="Ruta donde se encuentran las roms",
default=""
)
parser.add_argument(
"-o",
"--output",
help="Ruta donde depositar las roms",
default=""
)
return parser.parse_args()
def load_dat_file(dat_path):
"""Carga y analiza el fichero .dat especificado"""
if not dat_path:
print("No se ha especificado un fichero .dat")
if os.path.exists(config.XML_FILE):
# Usa el fichero por defecto si no se especifica uno
print(f"Usando el fichero por defecto: {config.XML_FILE}")
dat_path = config.XML_FILE
else:
# Salir si no se encuentra el fichero por defecto
sys.exit(2)
elif not os.path.isfile(dat_path):
# Salir si el fichero especificado no existe
print(f"No se encuentra el fichero .dat especificado: {dat_path}")
sys.exit(2)
print(f"Analizando el fichero {dat_path}")
return minidom.parse(dat_path)
def list_manufacturers(file):
"""Lista todos los desarrolladores presentes en el fichero .dat"""
print("Lista de todos los desarrolladores:")
manufacturers = []
games = file.getElementsByTagName("game")
# Recoge todos los desarrolladores de los juegos
for game in games:
manufacturer = game.getElementsByTagName("manufacturer")[0]
manufacturers.append(manufacturer.firstChild.data)
# Elimina los duplicados
manufacturers = list(dict.fromkeys(manufacturers))
# Ordena alfabéticamente la lista de desarrolladores
manufacturers.sort()
# Imprime cada desarrollador en una nueva línea
for manufacturer in manufacturers:
print(manufacturer)
def list_games_by_manufacturer(file, manufacturer):
"""Lista todos los juegos de un desarrollador específico"""
games = file.getElementsByTagName("game")
filtered_games = []
# Filtra los juegos según el fabricante especificado y excluye clones y BIOS
for game in games:
cloneof = game.getAttribute("cloneof")
isbios = game.getAttribute("isbios")
game_manufacturer = game.getElementsByTagName("manufacturer")[0]
description = game.getElementsByTagName("description")[0]
if (
(game_manufacturer.firstChild.data == manufacturer or manufacturer == 'all')
and not cloneof
and not isbios
):
filtered_games.append((description.firstChild.data, game.getAttribute('name'), game_manufacturer.firstChild.data))
# Encuentra el ancho máximo para cada columna
max_desc_length = max(len("Descripción"), max(len(row[0]) for row in filtered_games))
max_name_length = max(len("Nombre del archivo"), max(len(row[1] + '.zip') for row in filtered_games))
max_manufacturer_length = max(len("Desarrollador"), max(len(row[2]) for row in filtered_games))
# Imprime el encabezado de la tabla
print(f"Lista de todos los juegos de {manufacturer}: {len(filtered_games)}")
print(f"{'='*(max_desc_length+max_name_length+max_manufacturer_length+10)}")
print(f"| {'Descripción':<{max_desc_length}} | {'Nombre del archivo':<{max_name_length}} | {'Desarrollador':<{max_manufacturer_length}} |")
print(f"|{'-'*(max_desc_length+2)}|{'-'*(max_name_length+2)}|{'-'*(max_manufacturer_length+2)}|")
# Imprime cada juego en la tabla
for description, name, game_manufacturer in filtered_games:
print(f"| {description:<{max_desc_length}} | {name + '.zip':<{max_name_length}} | {game_manufacturer:<{max_manufacturer_length}} |")
# Imprime el pie de la tabla
print(f"{'='*(max_desc_length+max_name_length+max_manufacturer_length+10)}")
def find_games(file, search_string):
"""Busca juegos que contengan la cadena de texto especificada"""
games = file.getElementsByTagName("game")
filtered_games = []
# Filtra los juegos según la cadena de búsqueda especificada
for game in games:
description = game.getElementsByTagName("description")[0]
if search_string.casefold() in description.firstChild.data.casefold():
game_manufacturer = game.getElementsByTagName("manufacturer")[0]
filtered_games.append((description.firstChild.data, game.getAttribute('name'), game_manufacturer.firstChild.data))
# Encuentra el ancho máximo para cada columna
max_desc_length = max(len("Descripción"), max(len(row[0]) for row in filtered_games))
max_name_length = max(len("Nombre del archivo"), max(len(row[1] + '.zip') for row in filtered_games))
max_manufacturer_length = max(len("Desarrollador"), max(len(row[2]) for row in filtered_games))
# Imprime el encabezado de la tabla
print(f"Juegos que contienen '{search_string}': {len(filtered_games)}")
print(f"{'='*(max_desc_length+max_name_length+max_manufacturer_length+10)}")
print(f"| {'Descripción':<{max_desc_length}} | {'Nombre del archivo':<{max_name_length}} | {'Desarrollador':<{max_manufacturer_length}} |")
print(f"|{'-'*(max_desc_length+2)}|{'-'*(max_name_length+2)}|{'-'*(max_manufacturer_length+2)}|")
# Imprime cada juego en la tabla
for description, name, game_manufacturer in filtered_games:
print(f"| {description:<{max_desc_length}} | {name + '.zip':<{max_name_length}} | {game_manufacturer:<{max_manufacturer_length}} |")
# Imprime el pie de la tabla
print(f"{'='*(max_desc_length+max_name_length+max_manufacturer_length+10)}")
def copy_games(file, args, all_games=False):
"""Copia los juegos según los criterios especificados"""
if args.create_folder and not all_games:
# Normaliza el nombre del desarrollador para usarlo como nombre de carpeta
normalized_manufacturer = normalize_path(args.manufacturer)
os.mkdir(os.path.join(args.output, args.manufacturer))
# Listas para almacenar los juegos no encontrados, ignorados y copiados
notfound = []
ignored_games = []
copied_games = []
games = file.getElementsByTagName("game")
if all_games:
print(f"Copiando todos los juegos: {len(games)}")
else:
print(f"Copiando todos los juegos de {args.manufacturer}: {len(games)}")
for game in games:
isignored = False
name = game.getAttribute("name") + ".zip"
cloneof = game.getAttribute("cloneof")
isbios = game.getAttribute("isbios")
romof = game.getAttribute("romof")
manufacturer = game.getElementsByTagName("manufacturer")[0]
description = game.getElementsByTagName("description")[0]
driver = game.getElementsByTagName("driver")
status = (
game.getElementsByTagName("driver")[0].getAttribute("status")
if driver
else ""
)
# Verifica si el juego cumple con los criterios para ser copiado
if (all_games or manufacturer.firstChild.data == args.manufacturer) and not cloneof and not isbios and status == "good":
# Verifica si el juego debe ser ignorado
for ignore_element in config.IGNORE_LIST:
if ignore_element.casefold() in description.firstChild.data.casefold():
ignored_games.append(description.firstChild.data)
isignored = True
for ignore_element in config.IGNORE_SYSTEM:
if ignore_element.casefold() in romof.casefold():
ignored_games.append(description.firstChild.data)
isignored = True
if not isignored:
# Define la ruta de origen y destino del archivo
src = os.path.join(args.input, name)
if args.sort:
x = manufacturer.firstChild.data
x = x.replace(r"/", r"-")
dst = os.path.join(args.output, x, name)
if not os.path.isdir(os.path.join(args.output, x)):
os.mkdir(os.path.join(args.output, x))
else:
if args.create_folder and not all_games:
dst = os.path.join(args.output, normalized_manufacturer, name)
else:
dst = os.path.join(args.output, name)
# Copia el archivo si existe en la ruta de origen
if os.path.isfile(src):
shutil.copyfile(src, dst)
copied_games.append(description.firstChild.data)
print(description.firstChild.data)
else:
notfound.append(description.firstChild.data)
# Imprime los resultados de los juegos no encontrados, ignorados y copiados
print(f"\nJuegos faltantes: {len(notfound)}")
for game in notfound:
print(game)
print(f"\nJuegos ignorados: {len(ignored_games)}")
for game in ignored_games:
print(game)
print(f"\nJuegos copiados: {len(copied_games)}")
print(f"Juegos faltantes: {len(notfound)}")
print(f"Juegos ignorados: {len(ignored_games)}")
def main():
"""Función principal que coordina la ejecución del script"""
# Parsear los argumentos de la línea de comandos
args = parse_arguments()
# Cargar y analizar el fichero .dat
file = load_dat_file(dat_path=args.dat)
# Manejar la opción de listar desarrolladores o juegos
if args.list:
if args.manufacturer:
list_games_by_manufacturer(file=file, manufacturer=args.manufacturer)
else:
list_manufacturers(file=file)
# Manejar la opción de copiar roms
if args.copy:
if args.manufacturer:
copy_games(file=file, args=args, all_games=False)
else:
copy_games(file=file, args=args, all_games=True)
# Manejar la opción de búsqueda de juegos
if args.find:
find_games(file=file, search_string=args.find)
if __name__ == "__main__":
main()