From 1becd0bae6573908eaad00344f69f4010fb67831 Mon Sep 17 00:00:00 2001 From: sjp761 <171447425+sjp761@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:45:18 -0500 Subject: [PATCH] File Exclusion --- legendary/core.py | 11 +++++++++++ legendary/downloader/mp/manager.py | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/legendary/core.py b/legendary/core.py index 6e27a52..9770915 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -1654,6 +1654,16 @@ class LegendaryCore: if not max_workers: max_workers = self.lgd.config.getint('Legendary', 'max_workers', fallback=0) + + exclude_file_path = os.path.join(self.lgd.path, "exclude", game.app_name) + self.log.info(f'Using exclude file if it exists: {exclude_file_path}') + + try: + with open(exclude_file_path, 'r') as f: + file_exclude_configured = [line.strip().replace('/', os.sep).replace('\\', os.sep).lower() for line in f if line.strip()] + except Exception: + file_exclude_configured = [] + dlm = DLManager(install_path, base_url, manifest_secrets, resume_file=resume_file, status_q=status_q, max_shared_memory=max_shm * 1024 * 1024, max_workers=max_workers, dl_timeout=dl_timeout, bind_ip=bind_ip, case_insensitive=platform.startswith('Win')) @@ -1661,6 +1671,7 @@ class LegendaryCore: patch=not disable_patching, resume=not force, file_prefix_filter=file_prefix_filter, file_exclude_filter=file_exclude_filter, + file_exclude_configured=file_exclude_configured, file_install_tag=file_install_tag, processing_optimization=process_opt) diff --git a/legendary/downloader/mp/manager.py b/legendary/downloader/mp/manager.py index c44fb79..92963d8 100644 --- a/legendary/downloader/mp/manager.py +++ b/legendary/downloader/mp/manager.py @@ -3,6 +3,7 @@ import logging import os +from pathlib import PurePath import time from collections import Counter, defaultdict, deque from logging.handlers import QueueHandler @@ -12,7 +13,6 @@ from multiprocessing.shared_memory import SharedMemory from queue import Empty from sys import exit from threading import Condition, Thread - from legendary.downloader.mp.workers import DLWorker, FileWorker from legendary.models.downloading import ( AnalysisResult, @@ -90,10 +90,16 @@ class DLManager(Process): self.num_processed_since_last = 0 self.num_tasks_processed_since_last = 0 + def matches(file, excludelist): + for pattern in excludelist: + if PurePath(file).full_match(pattern): + return True + return False + def run_analysis(self, manifest: Manifest, old_manifest: Manifest = None, patch=True, resume=True, file_prefix_filter=None, file_exclude_filter=None, file_install_tag=None, - processing_optimization=False) -> AnalysisResult: + file_exclude_configured=None, processing_optimization=False) -> AnalysisResult: """ Run analysis on manifest and old manifest (if not None) and return a result with a summary resources required in order to install the provided manifest. @@ -105,6 +111,7 @@ class DLManager(Process): :param file_prefix_filter: Only download files that start with this prefix :param file_exclude_filter: Exclude files with this prefix from download :param file_install_tag: Only install files with the specified tag + :param file_exclude_configured: Exclude files based on a file listing globs :param processing_optimization: Attempt to optimize processing order and RAM usage :return: AnalysisResult """ @@ -210,6 +217,14 @@ class DLManager(Process): mc.changed -= files_to_skip mc.unchanged |= files_to_skip + if file_exclude_configured: + if isinstance(file_exclude_configured, str): + file_exclude_configured = [file_exclude_configured] + files_to_skip = set(i.filename for i in manifest.file_manifest_list.elements if DLManager.matches(i.filename.lower().replace('/', os.sep).replace('\\', os.sep), file_exclude_configured)) + mc.added -= files_to_skip + mc.changed -= files_to_skip + mc.unchanged |= files_to_skip + if file_prefix_filter or file_exclude_filter or file_install_tag: self.log.info(f'Remaining files after filtering: {len(mc.added) + len(mc.changed)}') # correct install size after filtering