import os import json import argparse import logging import sqlite3 from collections import defaultdict from datetime import datetime from tqdm import tqdm # Importa tqdm per la progress bar # --- CONFIGURAZIONE --- BASE_PATH = "/mnt/user" EXCLUDE_DIRS = [ "appdata", "domains", "isos", "Backup_Appdata", "system", "TUTTO", "MUSICA", "AUDIOLIBRI", ] # Nomi dei file di output DB_FILE_NAME = "media_index.db" JSON_FILE_NAME = "media_index.json" TXT_FILE_PREFIX = "index" # -------------------- def setup_logging(debug_mode: bool): """Configura il logging.""" level = logging.DEBUG if debug_mode else logging.INFO logging.basicConfig( level=level, format="[%(asctime)s] [%(levelname)s] - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) def setup_database(db_path: str): """Crea la tabella nel database SQLite se non esiste.""" try: conn = sqlite3.connect(db_path) cursor = conn.cursor() # Una tabella semplice per memorizzare percorsi e file # L'indice su 'path' velocizzerà le future ricerche cursor.execute(""" CREATE TABLE IF NOT EXISTS files ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT NOT NULL, filename TEXT NOT NULL, UNIQUE(path, filename) ); """) cursor.execute("CREATE INDEX IF NOT EXISTS idx_path ON files (path);") # Pulisci la tabella per un nuovo inserimento cursor.execute("DELETE FROM files;") conn.commit() return conn except sqlite3.Error as e: logging.error(f"Errore del database SQLite: {e}") return None def index_media_and_save(base_path, exclude_dirs, follow_links, output_format): """ Scansiona le directory e salva i risultati direttamente nel formato scelto per minimizzare l'uso della memoria. """ full_exclude_paths = [os.path.join(base_path, d) for d in exclude_dirs] logging.info(f"Inizio scansione da '{base_path}'. Formato output: {output_format}") # Prepara connessione al DB se necessario conn = None cursor = None if output_format in ["sqlite", "all"]: conn = setup_database(DB_FILE_NAME) if conn: cursor = conn.cursor() else: # Se il DB fallisce, non procedere con l'output sqlite output_format = "json" if output_format == "all" else "none" # Struttura dati in memoria solo se richiesta in_memory_index = defaultdict(list) if output_format in ["json", "txt", "all"] else None total_files_count = 0 # Usiamo un iteratore per poterlo passare a tqdm walker = os.walk(base_path, topdown=True, followlinks=follow_links) # tqdm mostra una barra di avanzamento dinamica for root, dirs, files in tqdm(walker, desc="Scansione cartelle", unit="dir"): if any(root.startswith(ex_path) for ex_path in full_exclude_paths): dirs[:] = [] continue if not files: continue sorted_files = sorted(files) # Salva in memoria se necessario if in_memory_index is not None: in_memory_index[root] = sorted_files # Salva direttamente su SQLite if cursor: try: # Usa executemany per un inserimento in blocco, molto più veloce entries_to_insert = [(root, f) for f in sorted_files] cursor.executemany("INSERT INTO files (path, filename) VALUES (?, ?)", entries_to_insert) except sqlite3.Error as e: logging.error(f"Errore inserimento dati per la cartella {root}: {e}") total_files_count += len(sorted_files) if conn: conn.commit() conn.close() logging.info(f"Dati salvati con successo nel database '{DB_FILE_NAME}'.") logging.info(f"Scansione completata. Trovati {total_files_count} file in {len(in_memory_index) if in_memory_index is not None else 'N/A'} cartelle.") # Gestione output finali da dati in memoria if output_format in ["json", "all"]: save_json_index(in_memory_index, JSON_FILE_NAME) if output_format in ["txt", "all"]: save_txt_index(in_memory_index, TXT_FILE_PREFIX) def save_json_index(index_data, filename): """Salva l'indice in formato JSON.""" logging.info(f"Salvataggio indice JSON in '{filename}'...") try: with open(filename, "w", encoding="utf-8") as f: json.dump(index_data, f, ensure_ascii=False, indent=2) logging.info("Indice JSON salvato.") except IOError as e: logging.error(f"Impossibile scrivere il file JSON '{filename}': {e}") def save_txt_index(index_data, filename_prefix): """Salva un report testuale formattato.""" date_str = datetime.now().strftime("%Y-%m-%d") filename = f"{filename_prefix}_{date_str}.txt" logging.info(f"Creazione report TXT in '{filename}'...") try: with open(filename, "w", encoding="utf-8") as out: for folder, files in sorted(index_data.items()): display_folder = folder.replace(BASE_PATH, "", 1) or "/" out.write(f"📁 {display_folder}\n") for file in files: out.write(f" - {file}\n") out.write("\n") logging.info(f"Report TXT creato con {len(index_data)} cartelle.") except IOError as e: logging.error(f"Impossibile scrivere il file TXT '{filename}': {e}") def main(): parser = argparse.ArgumentParser(description="Indicizzatore di file per Unraid.") parser.add_argument("--debug", action="store_true", help="Abilita log dettagliati.") parser.add_argument("--follow-links", action="store_true", help="Segue i link simbolici.") parser.add_argument( "--output-format", choices=["json", "txt", "sqlite", "all"], default="all", help="Scegli il formato di output." ) args = parser.parse_args() setup_logging(args.debug) index_media_and_save(BASE_PATH, EXCLUDE_DIRS, args.follow_links, args.output_format) if __name__ == "__main__": main()