Afegits docstrings a les funcions
This commit is contained in:
@@ -22,7 +22,7 @@ from urllib3.util.retry import Retry
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
# Configuración de base de datos
|
||||
config_db = {
|
||||
CONFIG_DB = {
|
||||
"user": config.DB_USER,
|
||||
"password": config.DB_PASSWORD,
|
||||
"host": config.DB_HOST,
|
||||
@@ -32,32 +32,32 @@ config_db = {
|
||||
}
|
||||
|
||||
# Direcciones de internet de donde descargar los datos
|
||||
url_prefix = {
|
||||
URL_PREFIX = {
|
||||
"spectrum_computing": r"https://spectrumcomputing.co.uk",
|
||||
"wos": r"https://php.sustancia.synology.me/wos",
|
||||
"nvg": r"https://php.sustancia.synology.me/nvg",
|
||||
}
|
||||
|
||||
# Rutas locales donde depositar los resultados
|
||||
destination_path = config.DESTINATION_PATH
|
||||
cache_path = config.CACHE_PATH
|
||||
temp_file = config.TEMP_FILE
|
||||
DESTINATION_PATH = config.DESTINATION_PATH
|
||||
CACHE_PATH = config.CACHE_PATH
|
||||
TEMP_FILE = config.TEMP_FILE
|
||||
|
||||
# Parámetros de configuración
|
||||
should_clear_destination_path = config.SHOULD_CLEAR_DESTINATION_PATH # Establece si se limpia primero la carpeta de destino
|
||||
should_split_modern_and_classic = config.SHOULD_SPLIT_MODERN_AND_CLASSIC # Separa los juegos en dos carpetas a partir de un año especificado
|
||||
should_sort_by_year = config.SHOULD_SORT_BY_YEAR # Separa los juegos por carpetas en función de su año de lanzamiento
|
||||
should_sort_by_letter = config.SHOULD_SORT_BY_LETTER # Separa los juegos por carpetas en función de su primera letra
|
||||
should_sort_by_developer = config.SHOULD_SORT_BY_DEVELOPER # Separa los juegos por desarrollador
|
||||
wait = config.WAIT # Establece una pausa aleatoria entre descargas
|
||||
SHOULD_CLEAR_DESTINATION_PATH = config.SHOULD_CLEAR_DESTINATION_PATH # Establece si se limpia primero la carpeta de destino
|
||||
SHOULD_SPLIT_MODERN_AND_CLASSIC = config.SHOULD_SPLIT_MODERN_AND_CLASSIC # Separa los juegos en dos carpetas a partir de un año especificado
|
||||
SHOULD_SORT_BY_YEAR = config.SHOULD_SORT_BY_YEAR # Separa los juegos por carpetas en función de su año de lanzamiento
|
||||
SHOULD_SORT_BY_LETTER = config.SHOULD_SORT_BY_LETTER # Separa los juegos por carpetas en función de su primera letra
|
||||
SHOULD_SORT_BY_DEVELOPER = config.SHOULD_SORT_BY_DEVELOPER # Separa los juegos por desarrollador
|
||||
WAIT = config.WAIT # Establece una pausa aleatoria entre descargas
|
||||
|
||||
# Leer variables numéricas
|
||||
min_wait = config.MIN_WAIT # Cantidad de segundos mínima a esperar entre descargas
|
||||
max_wait = config.MAX_WAIT # Cantidad de segundos máxima a esperar entre descargas
|
||||
last_classic_year = config.LAST_CLASSIC_YEAR # Año usado para la separación entre juegos clásicos y modernos
|
||||
MIN_WAIT = config.MIN_WAIT # Cantidad de segundos mínima a esperar entre descargas
|
||||
MAX_WAIT = config.MAX_WAIT # Cantidad de segundos máxima a esperar entre descargas
|
||||
LAST_CLASSIC_YEAR = config.LAST_CLASSIC_YEAR # Año usado para la separación entre juegos clásicos y modernos
|
||||
|
||||
# Tipos de fichero que se guardan en la carpeta raíz del juego
|
||||
filetypes_on_root = [
|
||||
FILETYPES_ON_ROOT = [
|
||||
"Tape image",
|
||||
"Disk image",
|
||||
"Snapshot image",
|
||||
@@ -65,7 +65,7 @@ filetypes_on_root = [
|
||||
]
|
||||
|
||||
# Resto de variables globales
|
||||
elements = []
|
||||
ELEMENTS = []
|
||||
|
||||
# Carga un fichero con consultas SQL
|
||||
def load_queries(file_path):
|
||||
@@ -74,12 +74,29 @@ def load_queries(file_path):
|
||||
return [query.strip() for query in queries if query.strip()]
|
||||
|
||||
# Carga las consultas desde el archivo
|
||||
queries = load_queries('queries.sql')
|
||||
QUERIES = load_queries('queries.sql')
|
||||
|
||||
# Listado con las consultas
|
||||
def select(cursor, query_index=0):
|
||||
"""
|
||||
Ejecuta la consulta seleccionada y procesa los resultados.
|
||||
|
||||
Parámetros:
|
||||
cursor (MySQLCursor): El cursor de la base de datos para ejecutar la consulta.
|
||||
query_index (int): El índice de la consulta a ejecutar en la lista de consultas QUERIES (predeterminado es 0).
|
||||
|
||||
Comportamiento:
|
||||
- Ejecuta la consulta indicada por `query_index` usando el cursor proporcionado.
|
||||
- Procesa los resultados de la consulta y los guarda en la lista ELEMENTS.
|
||||
- Cada fila de resultados se convierte en un diccionario con las claves 'title', 'developer', 'release_year', 'url', y 'filetype'.
|
||||
- Registra un mensaje informativo indicando que la consulta se ejecutó correctamente y el número de resultados obtenidos.
|
||||
|
||||
Ejemplo:
|
||||
>>> cursor = connection.cursor()
|
||||
>>> select(cursor, query_index=1)
|
||||
"""
|
||||
|
||||
# Ejecutar la consulta seleccionada
|
||||
cursor.execute(queries[query_index])
|
||||
cursor.execute(QUERIES[query_index])
|
||||
|
||||
# Procesar los resultados
|
||||
for row in cursor:
|
||||
@@ -90,15 +107,34 @@ def select(cursor, query_index=0):
|
||||
"url": row[3],
|
||||
"filetype": row[4],
|
||||
}
|
||||
elements.append(element)
|
||||
ELEMENTS.append(element)
|
||||
|
||||
# Registro de consulta ejecutada
|
||||
logging.info(f"Consulta {query_index} ejecutada correctamente con {len(elements)} resultados.")
|
||||
logging.info(f"Consulta {query_index} ejecutada correctamente con {len(ELEMENTS)} resultados.")
|
||||
|
||||
|
||||
# Establece la conexión a la BBDD y ejecuta la consulta
|
||||
def connect(query_index=0):
|
||||
"""
|
||||
Establece la conexión a la base de datos y ejecuta la consulta.
|
||||
|
||||
Parámetros:
|
||||
query_index (int): Índice de la consulta a ejecutar (predeterminado es 0).
|
||||
|
||||
Excepciones:
|
||||
mysql.connector.Error: Captura y maneja errores específicos de MySQL.
|
||||
Exception: Captura y maneja cualquier otro error inesperado.
|
||||
|
||||
Comportamiento:
|
||||
- Conecta a la base de datos usando los parámetros de configuración en CONFIG_DB.
|
||||
- Ejecuta la consulta definida por la función select utilizando el cursor.
|
||||
- Maneja errores de acceso denegado y base de datos no encontrada, así como cualquier otro error inesperado.
|
||||
|
||||
Ejemplo:
|
||||
>>> connect(1)
|
||||
"""
|
||||
|
||||
try:
|
||||
with mysql.connector.connect(**config_db) as connection:
|
||||
with mysql.connector.connect(**CONFIG_DB) as connection:
|
||||
with connection.cursor() as cursor:
|
||||
# Ejecutar la consulta 1
|
||||
select(cursor, query_index=query_index)
|
||||
@@ -112,8 +148,25 @@ def connect(query_index=0):
|
||||
except Exception as e:
|
||||
logging.error(f"Error inesperado: {e}")
|
||||
|
||||
# Añade un prefijo a la url
|
||||
|
||||
def update_url(element, url_prefix):
|
||||
"""
|
||||
Añade un prefijo a la URL en el diccionario `element` según el prefijo proporcionado en `url_prefix`.
|
||||
|
||||
Parámetros:
|
||||
element (dict): Diccionario que contiene la clave 'url'.
|
||||
url_prefix (dict): Diccionario que contiene los prefijos de las URLs para 'spectrum_computing', 'wos' y 'nvg'.
|
||||
|
||||
Modifica:
|
||||
element (dict): Actualiza el valor de 'url' en el diccionario `element` con el prefijo correspondiente.
|
||||
|
||||
Ejemplo:
|
||||
>>> element = {"url": "/zxdb/example"}
|
||||
>>> url_prefix = {"spectrum_computing": "https://spectrumcomputing.co.uk", "wos": "https://php.sustancia.synology.me/wos", "nvg": "https://php.sustancia.synology.me/nvg"}
|
||||
>>> update_url(element, url_prefix)
|
||||
>>> print(element["url"])
|
||||
https://spectrumcomputing.co.uk/zxdb/example
|
||||
"""
|
||||
url = element["url"]
|
||||
if url.startswith("/zxdb"):
|
||||
element["url"] = url_prefix["spectrum_computing"] + url
|
||||
@@ -122,41 +175,96 @@ def update_url(element, url_prefix):
|
||||
elif url.startswith("/nvg"):
|
||||
element["url"] = url_prefix["nvg"] + url[4:]
|
||||
|
||||
# Procesa todos lo elementos, modificando cada uno de sus parametros
|
||||
|
||||
def process_elements():
|
||||
global elements
|
||||
for i in range(len(elements)):
|
||||
"""
|
||||
Procesa todos los elementos, modificando cada uno de sus parámetros.
|
||||
|
||||
Comportamiento:
|
||||
- Construye el nombre de la carpeta raíz basado en el título y año de lanzamiento, o título, año de lanzamiento y desarrollador.
|
||||
- Normaliza el nombre de la carpeta raíz.
|
||||
- Añade el prefijo a la URL y normaliza los enlaces de "wos".
|
||||
- Obtiene el nombre del fichero a partir de la URL de descarga.
|
||||
- Establece la subcarpeta dentro de la raíz basada en el tipo de archivo.
|
||||
- Verifica si el fichero está en formato .zip.
|
||||
- Calcula el nombre del fichero si es un zip.
|
||||
|
||||
Modifica:
|
||||
- ELEMENTS (list): Actualiza cada diccionario en ELEMENTS con las claves 'root_folder', 'url', 'file_name', 'subfolder', 'is_zip', y 'non_zip_file_name'.
|
||||
|
||||
Ejemplo:
|
||||
>>> process_elements()
|
||||
"""
|
||||
global ELEMENTS
|
||||
for i in range(len(ELEMENTS)):
|
||||
# Construye el nombre de la carpeta raiz
|
||||
if should_sort_by_developer:
|
||||
elements[i]["root_folder"] = f"{elements[i]['title']} ({elements[i]['release_year']})"
|
||||
if SHOULD_SORT_BY_DEVELOPER:
|
||||
ELEMENTS[i]["root_folder"] = f"{ELEMENTS[i]['title']} ({ELEMENTS[i]['release_year']})"
|
||||
else:
|
||||
elements[i]["root_folder"] = f"{elements[i]['title']} ({elements[i]['release_year']})({elements[i]['developer']})"
|
||||
elements[i]["root_folder"] = normalize_path(elements[i]["root_folder"])
|
||||
ELEMENTS[i]["root_folder"] = f"{ELEMENTS[i]['title']} ({ELEMENTS[i]['release_year']})({ELEMENTS[i]['developer']})"
|
||||
ELEMENTS[i]["root_folder"] = normalize_path(ELEMENTS[i]["root_folder"])
|
||||
|
||||
# Añade el prefijo a la url y normaliza los enlaces de "wos"
|
||||
update_url(elements[i], url_prefix)
|
||||
update_url(ELEMENTS[i], URL_PREFIX)
|
||||
|
||||
# Obtiene el nombre del fichero a partir de la url de descarga
|
||||
elements[i]["file_name"] = url_filename(elements[i]["url"])
|
||||
ELEMENTS[i]["file_name"] = url_filename(ELEMENTS[i]["url"])
|
||||
|
||||
# Establece la subcarpeta dentro de la raiz
|
||||
elements[i]["subfolder"] = normalize_path(elements[i]["filetype"]) if elements[i]["filetype"] not in filetypes_on_root else ""
|
||||
ELEMENTS[i]["subfolder"] = normalize_path(ELEMENTS[i]["filetype"]) if ELEMENTS[i]["filetype"] not in FILETYPES_ON_ROOT else ""
|
||||
|
||||
# Averigua si el fichero está en formato .zip
|
||||
elements[i]["is_zip"] = elements[i]["file_name"].lower().endswith(".zip")
|
||||
ELEMENTS[i]["is_zip"] = ELEMENTS[i]["file_name"].lower().endswith(".zip")
|
||||
|
||||
# Calcula el nombre del fichero si es un zip
|
||||
elements[i]["non_zip_file_name"] = elements[i]["file_name"][:-4] if elements[i]["is_zip"] else elements[i]["file_name"]
|
||||
ELEMENTS[i]["non_zip_file_name"] = ELEMENTS[i]["file_name"][:-4] if ELEMENTS[i]["is_zip"] else ELEMENTS[i]["file_name"]
|
||||
|
||||
|
||||
# Devuelve el fichero que forma la parte final de una URL
|
||||
def url_filename(url):
|
||||
"""
|
||||
Devuelve el fichero que forma la parte final de una URL.
|
||||
|
||||
Parámetros:
|
||||
url (str): La URL de la cual se quiere extraer el nombre del fichero.
|
||||
|
||||
Retorna:
|
||||
str: El nombre del fichero que forma la parte final de la URL.
|
||||
|
||||
Ejemplo:
|
||||
>>> url = "https://example.com/path/to/file.txt"
|
||||
>>> url_filename(url)
|
||||
'file.txt'
|
||||
"""
|
||||
parsed_url = urlparse(url)
|
||||
path = parsed_url.path
|
||||
filename = os.path.basename(path)
|
||||
return filename
|
||||
|
||||
# Descarga un fichero a partir de una URL
|
||||
|
||||
def download_file(url, destination):
|
||||
"""
|
||||
Descarga un fichero a partir de una URL.
|
||||
|
||||
Parámetros:
|
||||
url (str): La URL del fichero que se desea descargar.
|
||||
destination (str): La ruta de destino donde se guardará el fichero descargado.
|
||||
|
||||
Retorna:
|
||||
bool: True si la descarga fue exitosa, False en caso de error.
|
||||
|
||||
Comportamiento:
|
||||
- Crea una sesión de requests con un adaptador que permite reintentos en caso de fallos.
|
||||
- Intenta descargar el fichero desde la URL especificada.
|
||||
- Guarda el contenido del fichero en la ruta de destino especificada.
|
||||
- Maneja errores de conexión y otros problemas de la red, registrando cualquier error ocurrido.
|
||||
|
||||
Ejemplo:
|
||||
>>> success = download_file("https://example.com/file.zip", "/path/to/destination/file.zip")
|
||||
>>> if success:
|
||||
>>> print("Descarga completada con éxito.")
|
||||
>>> else:
|
||||
>>> print("Error en la descarga.")
|
||||
"""
|
||||
session = requests.Session()
|
||||
retries = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
|
||||
session.mount('https://', HTTPAdapter(max_retries=retries))
|
||||
@@ -171,8 +279,31 @@ def download_file(url, destination):
|
||||
logging.error(f"Error al descargar el archivo: {e}")
|
||||
return False
|
||||
|
||||
# Descomprime los ficheros que coinciden con la lista de extensiones
|
||||
|
||||
def unzip_file(src, dst):
|
||||
"""
|
||||
Descomprime los ficheros que coinciden con la lista de extensiones.
|
||||
|
||||
Parámetros:
|
||||
src (str): Ruta del archivo ZIP que se desea descomprimir.
|
||||
dst (str): Ruta del directorio donde se extraerán los ficheros.
|
||||
|
||||
Extensiones soportadas:
|
||||
.z80, .sna, .tzx, .tap, .dsk, .trd
|
||||
|
||||
Comportamiento:
|
||||
- Abre el archivo ZIP especificado por `src`.
|
||||
- Extrae los ficheros que coinciden con las extensiones definidas en el directorio especificado por `dst`.
|
||||
- Maneja errores de archivo ZIP corrupto, archivo no encontrado y otros errores generales, registrándolos adecuadamente.
|
||||
|
||||
Excepciones:
|
||||
zipfile.BadZipFile: El archivo ZIP está corrupto.
|
||||
FileNotFoundError: El archivo ZIP no se encontró.
|
||||
Exception: Cualquier otro error inesperado.
|
||||
|
||||
Ejemplo:
|
||||
>>> unzip_file('/path/to/archivo.zip', '/path/to/destination/')
|
||||
"""
|
||||
archive = src
|
||||
directory = dst
|
||||
extensions = (".z80", ".sna", ".tzx", ".tap", ".dsk", ".trd")
|
||||
@@ -190,8 +321,26 @@ def unzip_file(src, dst):
|
||||
except Exception as e:
|
||||
logging.error(f"Ocurrió un error: {e}")
|
||||
|
||||
# Imprime el estado de un archivo en el proceso de descarga
|
||||
|
||||
def print_status(current_file, total_files, element, total_files_width, status="cached"):
|
||||
"""
|
||||
Imprime el estado de un archivo en el proceso de descarga.
|
||||
|
||||
Parámetros:
|
||||
current_file (int): El número del archivo actual en el proceso de descarga.
|
||||
total_files (int): El número total de archivos en el proceso de descarga.
|
||||
element (dict): Diccionario que contiene información del archivo actual, incluyendo 'file_name' y 'filetype'.
|
||||
total_files_width (int): El ancho del campo para el número total de archivos, para un formato de impresión alineado.
|
||||
status (str): El estado del archivo (por defecto es "cached").
|
||||
|
||||
Comportamiento:
|
||||
- Imprime el estado del archivo actual en el proceso de descarga de una manera formateada y alineada.
|
||||
|
||||
Ejemplo:
|
||||
>>> element = {"file_name": "example.zip", "filetype": "zip"}
|
||||
>>> print_status(1, 100, element, 3)
|
||||
( 1 / 100) : cached : example.zip (zip)
|
||||
"""
|
||||
print(
|
||||
"({:{width}} / {}) : {:<10} : {} ({})".format(
|
||||
current_file,
|
||||
@@ -203,34 +352,56 @@ def print_status(current_file, total_files, element, total_files_width, status="
|
||||
)
|
||||
)
|
||||
|
||||
# Compone la carpeta de destino en función de varios parámetros
|
||||
|
||||
def get_final_destination_folder(developer, year, root_folder):
|
||||
"""
|
||||
Compone la carpeta de destino en función de varios parámetros.
|
||||
|
||||
Parámetros:
|
||||
developer (str): Nombre del desarrollador del juego.
|
||||
year (int o str): Año de lanzamiento del juego.
|
||||
root_folder (str): Nombre de la carpeta raíz.
|
||||
|
||||
Retorna:
|
||||
str: La ruta completa de la carpeta de destino final.
|
||||
|
||||
Comportamiento:
|
||||
- Determina la carpeta base (`folder1`) en función de si el juego es clásico o moderno, el desarrollador o el año.
|
||||
- Determina la subcarpeta (`folder2`) basada en el desarrollador, si está habilitado.
|
||||
- Determina la subcarpeta (`folder3`) basada en el año de lanzamiento, si está habilitado.
|
||||
- Determina la subcarpeta (`folder4`) basada en la primera letra del nombre de la carpeta raíz, si está habilitado.
|
||||
- Combina las carpetas calculadas y la carpeta raíz para obtener la ruta final de la carpeta de destino.
|
||||
|
||||
Ejemplo:
|
||||
>>> get_final_destination_folder("Nintendo", 1985, "Super Mario")
|
||||
'by_developer/N/Nintendo/1985/Super Mario'
|
||||
"""
|
||||
# Carpeta basada en los años de vida comercial del spectrum
|
||||
folder1 = ""
|
||||
if should_split_modern_and_classic:
|
||||
if year == "none" or year is None or year > last_classic_year:
|
||||
if SHOULD_SPLIT_MODERN_AND_CLASSIC:
|
||||
if year == "none" or year is None or year > LAST_CLASSIC_YEAR:
|
||||
folder1 = "modern"
|
||||
else:
|
||||
folder1 = "classics"
|
||||
elif should_sort_by_developer:
|
||||
elif SHOULD_SORT_BY_DEVELOPER:
|
||||
folder1 = "by_developer"
|
||||
elif should_sort_by_year:
|
||||
elif SHOULD_SORT_BY_YEAR:
|
||||
folder1 = "by_year"
|
||||
|
||||
# Carpeta basada en el desarrollador
|
||||
folder2 = ""
|
||||
if should_sort_by_developer:
|
||||
if SHOULD_SORT_BY_DEVELOPER:
|
||||
developer = developer or ""
|
||||
folder2 = os.path.join(developer[0].upper(), developer)
|
||||
|
||||
# Carpeta basada en el año de lanzamiento
|
||||
folder3 = ""
|
||||
if should_sort_by_year:
|
||||
if SHOULD_SORT_BY_YEAR:
|
||||
folder3 = str(year) if year not in [None, "none"] else "unknown"
|
||||
|
||||
# Carpeta basada en la primera letra del nombre de la carpeta raíz
|
||||
folder4 = ""
|
||||
if should_sort_by_letter:
|
||||
if SHOULD_SORT_BY_LETTER:
|
||||
if root_folder[0].isdigit():
|
||||
folder4 = "0-9"
|
||||
else:
|
||||
@@ -243,29 +414,63 @@ def get_final_destination_folder(developer, year, root_folder):
|
||||
return os.path.join(folder1, folder2, folder3, folder4, root_folder)
|
||||
|
||||
|
||||
# Crea las carpetas de destino y copia o extrae el archivo de la caché
|
||||
|
||||
def process_cache_file(cache_file, destination_subfolder, destination_file, element):
|
||||
"""
|
||||
Crea las carpetas de destino y copia o extrae el archivo de la caché.
|
||||
|
||||
Parámetros:
|
||||
cache_file (str): Ruta del archivo en la caché.
|
||||
destination_subfolder (str): Ruta del subdirectorio de destino donde se guardarán los archivos extraídos.
|
||||
destination_file (str): Ruta del archivo de destino si no se extrae.
|
||||
element (dict): Diccionario que contiene información del archivo, incluyendo si tiene subcarpeta específica.
|
||||
|
||||
Comportamiento:
|
||||
- Crea las carpetas de destino necesarias.
|
||||
- Si el archivo en caché es un zip y no tiene subcarpeta especificada, descomprime el archivo en el subdirectorio de destino.
|
||||
- Si no, copia el archivo en caché al archivo de destino especificado.
|
||||
|
||||
Ejemplo:
|
||||
>>> process_cache_file('/path/to/cache.zip', '/path/to/destination/subfolder', '/path/to/destination/file', element)
|
||||
"""
|
||||
os.makedirs(destination_subfolder, exist_ok=True)
|
||||
if cache_file.endswith(".zip") and element["subfolder"] == "":
|
||||
unzip_file(cache_file, destination_subfolder)
|
||||
else:
|
||||
shutil.copyfile(cache_file, destination_file)
|
||||
|
||||
# Obtiene los ficheros de la consulta desde internet o desde la caché
|
||||
# y los deposita en la carpeta destino, descomprimiendo los archivos necesarios
|
||||
|
||||
def get_files():
|
||||
"""
|
||||
Obtiene los ficheros de la consulta desde internet o desde la caché y los deposita en la carpeta destino,
|
||||
descomprimiendo los archivos necesarios.
|
||||
|
||||
Comportamiento:
|
||||
- Presenta en pantalla el progreso de la descarga.
|
||||
- Para cada elemento en ELEMENTS:
|
||||
- Calcula la carpeta de clasificación basada en el desarrollador, año de lanzamiento y carpeta raíz.
|
||||
- Determina las rutas de la carpeta de destino y de caché.
|
||||
- Si el fichero no existe en la carpeta de destino:
|
||||
- Si el fichero existe en la caché, lo copia al destino.
|
||||
- Si no existe en la caché, lo descarga y lo guarda en la caché y en el destino.
|
||||
- Si el fichero ya existe en el destino, lo omite.
|
||||
- Maneja errores durante el procesamiento, registrándolos adecuadamente.
|
||||
|
||||
Ejemplo:
|
||||
>>> get_files()
|
||||
"""
|
||||
# Variables para la presentación en pantalla de la descarga
|
||||
current_file = 0
|
||||
total_files = len(elements)
|
||||
total_files = len(ELEMENTS)
|
||||
total_files_width = len(str(total_files))
|
||||
last_game_folder = ""
|
||||
|
||||
for element in elements:
|
||||
for element in ELEMENTS:
|
||||
classification_folder = get_final_destination_folder(element["developer"], element["release_year"], element["root_folder"])
|
||||
|
||||
destination_folder = os.path.join(destination_path, classification_folder)
|
||||
destination_folder = os.path.join(DESTINATION_PATH, classification_folder)
|
||||
destination_subfolder = os.path.join(destination_folder, element["subfolder"])
|
||||
cache_folder = os.path.join(cache_path, element["root_folder"])
|
||||
cache_folder = os.path.join(CACHE_PATH, element["root_folder"])
|
||||
cache_subfolder = os.path.join(cache_folder, element["subfolder"])
|
||||
|
||||
# Ruta completa hasta el fichero de destino y de caché
|
||||
@@ -290,20 +495,20 @@ def get_files():
|
||||
|
||||
# Si no existe en la caché, lo descarga
|
||||
else:
|
||||
if download_file(element["url"], temp_file):
|
||||
if download_file(element["url"], TEMP_FILE):
|
||||
print_status(current_file, total_files, element, total_files_width, status="downloaded")
|
||||
if os.path.isfile(temp_file):
|
||||
if os.path.isfile(TEMP_FILE):
|
||||
# Mueve el fichero temporal descargado a la cache
|
||||
os.makedirs(cache_subfolder, exist_ok=True)
|
||||
shutil.move(temp_file, cache_file)
|
||||
shutil.move(TEMP_FILE, cache_file)
|
||||
# Copia el fichero de la cache al destino
|
||||
if os.path.isfile(cache_file):
|
||||
process_cache_file(cache_file, destination_subfolder, destination_file, element)
|
||||
else:
|
||||
print_status(current_file, total_files, element, total_files_width, status="not found")
|
||||
|
||||
if wait:
|
||||
time.sleep(random.randint(min_wait, max_wait))
|
||||
if WAIT:
|
||||
time.sleep(random.randint(MIN_WAIT, MAX_WAIT))
|
||||
|
||||
# Si el fichero ya existe en el destino, no hace nada
|
||||
else:
|
||||
@@ -312,16 +517,49 @@ def get_files():
|
||||
except Exception as e:
|
||||
logging.error(f"Error al procesar el fichero {element['file_name']}: {e}")
|
||||
|
||||
# Elimina los caracteres ilegales de la cadena de texto
|
||||
|
||||
def normalize_path(path):
|
||||
"""
|
||||
Elimina los caracteres ilegales de la cadena de texto.
|
||||
|
||||
Parámetros:
|
||||
path (str): La cadena de texto que se desea normalizar.
|
||||
|
||||
Retorna:
|
||||
str: La cadena de texto normalizada sin los caracteres ilegales.
|
||||
|
||||
Comportamiento:
|
||||
- Reemplaza los caracteres ilegales en la cadena de texto con un carácter vacío.
|
||||
- Los caracteres ilegales incluyen: <, >, :, ", /, \, |, ?, *.
|
||||
- Utiliza la función unidecode para manejar caracteres Unicode.
|
||||
|
||||
Ejemplo:
|
||||
>>> normalize_path("file:name/with|illegal*chars?")
|
||||
'filenamewithillegalchars'
|
||||
"""
|
||||
illegal_chars = ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]
|
||||
replace_with = ""
|
||||
for char in illegal_chars:
|
||||
path = unidecode(path.replace(char, replace_with))
|
||||
return path
|
||||
|
||||
# Elimina los subdirectorios vacios
|
||||
|
||||
def remove_empty_directories(path):
|
||||
"""
|
||||
Elimina los subdirectorios vacíos.
|
||||
|
||||
Parámetros:
|
||||
path (str): La ruta del directorio raíz desde donde se iniciará la eliminación de subdirectorios vacíos.
|
||||
|
||||
Comportamiento:
|
||||
- Recorre la estructura de directorios desde el `path` especificado, de forma descendente.
|
||||
- Intenta eliminar cada subdirectorio encontrado.
|
||||
- Registra un mensaje informativo si un subdirectorio vacío es eliminado.
|
||||
- Si no se puede eliminar un directorio porque no está vacío u ocurre otro error, el error es ignorado.
|
||||
|
||||
Ejemplo:
|
||||
>>> remove_empty_directories('/path/to/directory')
|
||||
"""
|
||||
for root, dirs, files in os.walk(path, topdown=False):
|
||||
for dir in dirs:
|
||||
dir_path = os.path.join(root, dir)
|
||||
@@ -332,12 +570,24 @@ def remove_empty_directories(path):
|
||||
# El directorio no está vacío o ocurrió otro error
|
||||
pass
|
||||
|
||||
# Limpia la carpeta de destino
|
||||
|
||||
def clear_destination_folder():
|
||||
if should_clear_destination_path:
|
||||
"""
|
||||
Limpia la carpeta de destino.
|
||||
|
||||
Comportamiento:
|
||||
- Verifica si la opción de limpiar la carpeta de destino (`SHOULD_CLEAR_DESTINATION_PATH`) está habilitada.
|
||||
- Si está habilitada, elimina todos los archivos y subdirectorios en la carpeta de destino (`DESTINATION_PATH`).
|
||||
- Registra un mensaje informativo para cada archivo o directorio eliminado.
|
||||
- Maneja y registra cualquier error que ocurra durante la eliminación.
|
||||
|
||||
Ejemplo:
|
||||
>>> clear_destination_folder()
|
||||
"""
|
||||
if SHOULD_CLEAR_DESTINATION_PATH:
|
||||
logging.info("Limpiando la carpeta de destino ...")
|
||||
for filename in os.listdir(destination_path):
|
||||
file_path = os.path.join(destination_path, filename)
|
||||
for filename in os.listdir(DESTINATION_PATH):
|
||||
file_path = os.path.join(DESTINATION_PATH, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
@@ -348,18 +598,34 @@ def clear_destination_folder():
|
||||
except Exception as e:
|
||||
logging.error(f'No se pudo eliminar {file_path}. Razón: {e}')
|
||||
|
||||
# Imprime la lista de elementos
|
||||
|
||||
def print_elements(mode=0):
|
||||
"""
|
||||
Imprime la lista de elementos en diferentes modos.
|
||||
|
||||
Parámetros:
|
||||
mode (int): El modo de impresión.
|
||||
- Si es 0, imprime todos los elementos con sus claves y valores.
|
||||
- Si es 1, imprime los nombres de las carpetas raíz eliminando duplicados y muestra el número de entradas únicas.
|
||||
|
||||
Comportamiento:
|
||||
- Modo 0: Recorre todos los elementos en ELEMENTS e imprime cada clave y valor en un formato legible.
|
||||
- Modo 1: Elimina duplicados basándose en la carpeta raíz ('root_folder') y los imprime. Luego, muestra el número total de entradas únicas.
|
||||
|
||||
Ejemplo:
|
||||
>>> print_elements(0)
|
||||
>>> print_elements(1)
|
||||
"""
|
||||
if mode == 0:
|
||||
# Primer bucle for
|
||||
for element in elements:
|
||||
for element in ELEMENTS:
|
||||
print('')
|
||||
for key, value in element.items():
|
||||
print(key, ':', value)
|
||||
elif mode == 1:
|
||||
# Segundo bucle for con eliminación de duplicados
|
||||
seen = set()
|
||||
for element in elements:
|
||||
for element in ELEMENTS:
|
||||
root_folder = element['root_folder']
|
||||
if root_folder not in seen:
|
||||
print(root_folder)
|
||||
@@ -367,14 +633,30 @@ def print_elements(mode=0):
|
||||
# Imprimir el número de elementos únicos
|
||||
print(f"Número de entradas: {len(seen)}")
|
||||
|
||||
# Bucle principal
|
||||
|
||||
def main():
|
||||
"""
|
||||
Bucle principal que ejecuta las principales funciones del programa.
|
||||
|
||||
Comportamiento:
|
||||
- Establece la conexión a la base de datos y ejecuta la consulta con `query_index=3`.
|
||||
- Procesa todos los elementos, modificando sus parámetros.
|
||||
- Imprime la lista de elementos en el modo 1, que elimina duplicados y muestra el número total de entradas únicas.
|
||||
- Limpia la carpeta de destino si la opción está habilitada.
|
||||
- Obtiene los archivos desde internet o desde la caché, y los deposita en la carpeta de destino, descomprimiendo los necesarios.
|
||||
- Elimina los subdirectorios vacíos en la carpeta de destino.
|
||||
|
||||
Ejemplo:
|
||||
>>> main()
|
||||
"""
|
||||
|
||||
connect(query_index=3)
|
||||
process_elements()
|
||||
print_elements(mode=1)
|
||||
clear_destination_folder()
|
||||
get_files()
|
||||
remove_empty_directories(destination_path)
|
||||
remove_empty_directories(DESTINATION_PATH)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user