diff --git a/legendary/cli.py b/legendary/cli.py index a1daff7..75194c5 100644 --- a/legendary/cli.py +++ b/legendary/cli.py @@ -205,7 +205,8 @@ class LegendaryCLI: override_manifest=args.override_manifest, override_old_manifest=args.override_old_manifest, override_base_url=args.override_base_url, - platform_override=args.platform_override) + platform_override=args.platform_override, + file_prefix_filter=args.file_prefix) # game is either up to date or hasn't changed, so we have nothing to do if not analysis.dl_size: @@ -380,6 +381,8 @@ def main(): help='Set download manager and worker processes\' loglevel to debug') install_parser.add_argument('--platform', dest='platform_override', action='store', metavar='', type=str, help='Platform override for download (disables install)') + install_parser.add_argument('--prefix-filter', dest='file_prefix', action='store', metavar='', + type=str, help='Only fetch files whose path starts with (case insensitive)') launch_parser.add_argument('--offline', dest='offline', action='store_true', default=False, help='Skip login and launch game without online authentication') diff --git a/legendary/core.py b/legendary/core.py index 79f3f24..b8967b3 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -307,9 +307,10 @@ class LegendaryCore: def prepare_download(self, game: Game, base_game: Game = None, base_path: str = '', status_q: Queue = None, max_shm: int = 0, max_workers: int = 0, - force: bool = False, disable_patching: bool = False, game_folder: str = '', - override_manifest: str = '', override_old_manifest: str = '', - override_base_url: str = '', platform_override: str = '', + force: bool = False, disable_patching: bool = False, + game_folder: str = '', override_manifest: str = '', + override_old_manifest: str = '', override_base_url: str = '', + platform_override: str = '', file_prefix_filter: str = '' ) -> (DLManager, AnalysisResult, ManifestMeta): # load old manifest @@ -418,7 +419,8 @@ class LegendaryCore: dlm = DLManager(install_path, base_url, resume_file=resume_file, status_q=status_q, max_shared_memory=max_shm * 1024 * 1024, max_workers=max_workers) anlres = dlm.run_analysis(manifest=new_manifest, old_manifest=old_manifest, - patch=not disable_patching, resume=not force) + patch=not disable_patching, resume=not force, + file_prefix_filter=file_prefix_filter) prereq = None if new_manifest.meta.prereq_ids: diff --git a/legendary/downloader/manager.py b/legendary/downloader/manager.py index 0635af0..1d32784 100644 --- a/legendary/downloader/manager.py +++ b/legendary/downloader/manager.py @@ -242,7 +242,7 @@ class DLManager(Process): self.log.info('Writer result handler quitting...') def run_analysis(self, manifest: Manifest, old_manifest: Manifest = None, - patch=True, resume=True) -> AnalysisResult: + patch=True, resume=True, file_prefix_filter=None) -> 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. @@ -251,9 +251,13 @@ class DLManager(Process): :param old_manifest: Old manifest to patch from (if applicable) :param patch: Patch instead of redownloading the entire file :param resume: Continue based on resume file if it exists + :param file_prefix_filter: Only download files that start with this prefix :return: AnalysisResult """ + if file_prefix_filter: + file_prefix_filter = file_prefix_filter.lower() + analysis_res = AnalysisResult() analysis_res.install_size = sum(fm.file_size for fm in manifest.file_manifest_list.elements) analysis_res.biggest_chunk = max(c.window_size for c in manifest.chunk_data_list.elements) @@ -276,6 +280,14 @@ class DLManager(Process): except Exception as e: self.log.warning(f'Reading resume file failed: {e!r}, continuing as normal...') + # if prefix has been set: mark all files that are not to be downloaded as unchanged (not removed) + if file_prefix_filter: + files_to_skip = set(i for i in mc.added | mc.changed if not i.lower().startswith(file_prefix_filter)) + self.log.info(f'Found {len(files_to_skip)} files to skip based on prefix.') + mc.added -= files_to_skip + mc.changed -= files_to_skip + mc.unchanged |= files_to_skip + if mc.removed: analysis_res.removed = len(mc.removed) self.log.debug(f'{analysis_res.removed} removed files')