import os import json import argparse import logging from collections import defaultdict from datetime import datetime # --- CONFIGURAZIONE --- # Percorso base da cui iniziare la scansione BASE_PATH = "/mnt/user" # Elenco delle directory di primo livello da escludere completamente # Lo script controllerà se un percorso INIZIA con uno di questi. EXCLUDE_DIRS = [ "appdata", "domains", "isos", "Backup_Appdata", "system", "TUTTO", "MUSICA", "AUDIOLIBRI", ] # Nome del file JSON di output JSON_FILE_NAME = "media_index.json" # Prefisso per il file di testo di output TXT_FILE_PREFIX = "index" # -------------------- def setup_logging(debug_mode: bool): """Configura il logging per l'applicazione.""" 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 index_media(base_path: str, exclude_dirs: list, follow_links: bool): """ Scansiona le directory, indicizza i file e restituisce una struttura dati. La struttura è un dizionario { "percorso_cartella": ["file1", "file2", ...] }. """ media_index = defaultdict(list) total_files_count = 0 # Pre-calcola i percorsi completi da escludere per efficienza full_exclude_paths = [os.path.join(base_path, d) for d in exclude_dirs] logging.debug(f"Percorsi di esclusione completi: {full_exclude_paths}") logging.info(f"Inizio scansione da '{base_path}'...") logging.info(f"Seguire i link simbolici: {'Sì' if follow_links else 'No'}") for root, dirs, files in os.walk(base_path, topdown=True, followlinks=follow_links): # Escludi le directory specificate # `topdown=True` permette di modificare `dirs` per potare l'albero di ricerca if any(root.startswith(excluded_path) for excluded_path in full_exclude_paths): logging.debug(f"Salto la directory esclusa e le sue sottodirectory: {root}") dirs[:] = [] # Svuota la lista di sottodirectory per non visitarle continue if not files: continue # Salta le cartelle che non contengono file try: # Aggiungiamo solo i file di questa directory all'indice # I file vengono già ordinati per un output consistente sorted_files = sorted(files) media_index[root] = sorted_files total_files_count += len(sorted_files) # Log di progresso ogni 1000 cartelle indicizzate if len(media_index) % 1000 == 0: logging.info(f"Indicizzate {len(media_index)} cartelle e {total_files_count} file...") except OSError as e: logging.error(f"Errore di accesso durante la scansione di '{root}': {e}") continue logging.info(f"Scansione completata. Trovate {total_files_count} file in {len(media_index)} cartelle.") return media_index def save_json_index(index_data: dict, filename: str): """Salva l'indice in formato JSON.""" logging.info(f"Salvataggio dell'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 con successo.") except IOError as e: logging.error(f"Impossibile scrivere il file JSON '{filename}': {e}") def save_txt_index(index_data: dict, filename_prefix: str): """Salva un report testuale formattato dell'indice.""" date_str = datetime.now().strftime("%Y-%m-%d") filename = f"{filename_prefix}_{date_str}.txt" logging.info(f"Creazione del report testuale in '{filename}'...") try: with open(filename, "w", encoding="utf-8") as out: # Ordina le cartelle per percorso per un output pulito for folder, files in sorted(index_data.items()): # Corregge il percorso per rimuovere il prefisso base se non è "/" display_folder = folder if BASE_PATH != "/" and folder.startswith(BASE_PATH): display_folder = folder[len(BASE_PATH):] or "/" out.write(f"📁 {display_folder}\n") for file in files: # i file sono già ordinati out.write(f" - {file}\n") out.write("\n") logging.info(f"Report testuale creato con successo con {len(index_data)} cartelle.") except IOError as e: logging.error(f"Impossibile scrivere il file TXT '{filename}': {e}") def main(): """Funzione principale dello script.""" parser = argparse.ArgumentParser( description="Indicizzatore di file multimediali per Unraid.", formatter_class=argparse.ArgumentDefaultsHelpFormatter ) parser.add_argument( "--debug", action="store_true", help="Abilita il logging di debug dettagliato." ) parser.add_argument( "--follow-links", action="store_true", help="Segue i collegamenti simbolici durante la scansione." ) args = parser.parse_args() setup_logging(args.debug) # Esegui l'indicizzazione media_index = index_media(BASE_PATH, EXCLUDE_DIRS, args.follow_links) if not media_index: logging.warning("Nessun file trovato. L'indice non verrà creato.") return # Salva gli output save_json_index(media_index, JSON_FILE_NAME) save_txt_index(media_index, TXT_FILE_PREFIX) if __name__ == "__main__": main()