[cli/core/downloader] Add --exclude option to ignore files when downloading

Useful to exclude unnecessary files such as redistributables.

Can be used together with --prefix to exclude files that would
still match the specified --prefix.
This commit is contained in:
derrod 2020-05-01 10:43:55 +02:00
parent 28d7a0d2ca
commit 531af3f586
3 changed files with 30 additions and 13 deletions

View file

@ -272,7 +272,8 @@ class LegendaryCLI:
override_old_manifest=args.override_old_manifest, override_old_manifest=args.override_old_manifest,
override_base_url=args.override_base_url, override_base_url=args.override_base_url,
platform_override=args.platform_override, platform_override=args.platform_override,
file_prefix_filter=args.file_prefix) file_prefix_filter=args.file_prefix,
file_exclude_filter=args.file_exclude_prefix)
# game is either up to date or hasn't changed, so we have nothing to do # game is either up to date or hasn't changed, so we have nothing to do
if not analysis.dl_size: if not analysis.dl_size:
@ -451,6 +452,8 @@ def main():
type=str, help='Platform override for download (disables install)') type=str, help='Platform override for download (disables install)')
install_parser.add_argument('--prefix', dest='file_prefix', action='store', metavar='<prefix>', type=str, install_parser.add_argument('--prefix', dest='file_prefix', action='store', metavar='<prefix>', type=str,
help='Only fetch files whose path starts with <prefix> (case insensitive)') help='Only fetch files whose path starts with <prefix> (case insensitive)')
install_parser.add_argument('--exclude', dest='file_exclude_prefix', action='store', metavar='<prefix>',
type=str, help='Exclude files starting with <prefix> (case insensitive)')
launch_parser.add_argument('--offline', dest='offline', action='store_true', launch_parser.add_argument('--offline', dest='offline', action='store_true',
default=False, help='Skip login and launch game without online authentication') default=False, help='Skip login and launch game without online authentication')

View file

@ -355,9 +355,8 @@ class LegendaryCore:
force: bool = False, disable_patching: bool = False, force: bool = False, disable_patching: bool = False,
game_folder: str = '', override_manifest: str = '', game_folder: str = '', override_manifest: str = '',
override_old_manifest: str = '', override_base_url: str = '', override_old_manifest: str = '', override_base_url: str = '',
platform_override: str = '', file_prefix_filter: str = '' platform_override: str = '', file_prefix_filter: str = '',
) -> (DLManager, AnalysisResult, ManifestMeta): file_exclude_filter: str = '' ) -> (DLManager, AnalysisResult, ManifestMeta):
# load old manifest # load old manifest
old_manifest = None old_manifest = None
@ -430,7 +429,8 @@ class LegendaryCore:
max_shared_memory=max_shm * 1024 * 1024, max_workers=max_workers) max_shared_memory=max_shm * 1024 * 1024, max_workers=max_workers)
anlres = dlm.run_analysis(manifest=new_manifest, old_manifest=old_manifest, 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) file_prefix_filter=file_prefix_filter,
file_exclude_filter=file_exclude_filter)
prereq = None prereq = None
if new_manifest.meta.prereq_ids: if new_manifest.meta.prereq_ids:

View file

@ -241,7 +241,8 @@ class DLManager(Process):
self.log.info('Writer result handler quitting...') self.log.info('Writer result handler quitting...')
def run_analysis(self, manifest: Manifest, old_manifest: Manifest = None, def run_analysis(self, manifest: Manifest, old_manifest: Manifest = None,
patch=True, resume=True, file_prefix_filter=None) -> AnalysisResult: patch=True, resume=True, file_prefix_filter=None,
file_exclude_filter=None) -> AnalysisResult:
""" """
Run analysis on manifest and old manifest (if not None) and return a result 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. with a summary resources required in order to install the provided manifest.
@ -251,12 +252,10 @@ class DLManager(Process):
:param patch: Patch instead of redownloading the entire file :param patch: Patch instead of redownloading the entire file
:param resume: Continue based on resume file if it exists :param resume: Continue based on resume file if it exists
:param file_prefix_filter: Only download files that start with this prefix :param file_prefix_filter: Only download files that start with this prefix
:param file_exclude_filter: Exclude files with this prefix from download
:return: AnalysisResult :return: AnalysisResult
""" """
if file_prefix_filter:
file_prefix_filter = file_prefix_filter.lower()
analysis_res = AnalysisResult() analysis_res = AnalysisResult()
analysis_res.install_size = sum(fm.file_size for fm in manifest.file_manifest_list.elements) 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) analysis_res.biggest_chunk = max(c.window_size for c in manifest.chunk_data_list.elements)
@ -279,14 +278,29 @@ class DLManager(Process):
except Exception as e: except Exception as e:
self.log.warning(f'Reading resume file failed: {e!r}, continuing as normal...') 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 include/exclude prefix has been set: mark all files that are not to be downloaded as unchanged
if file_prefix_filter: if file_exclude_filter:
files_to_skip = set(i for i in mc.added | mc.changed if not i.lower().startswith(file_prefix_filter)) file_exclude_filter = file_exclude_filter.lower()
self.log.info(f'Found {len(files_to_skip)} files to skip based on prefix.') files_to_skip = set(i for i in mc.added | mc.changed if i.lower().startswith(file_exclude_filter))
self.log.info(f'Found {len(files_to_skip)} files to skip based on exclude prefix.')
mc.added -= files_to_skip mc.added -= files_to_skip
mc.changed -= files_to_skip mc.changed -= files_to_skip
mc.unchanged |= files_to_skip mc.unchanged |= files_to_skip
if file_prefix_filter:
file_prefix_filter = file_prefix_filter.lower()
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 include prefix.')
mc.added -= files_to_skip
mc.changed -= files_to_skip
mc.unchanged |= files_to_skip
if file_prefix_filter or file_exclude_filter:
self.log.info(f'Remaining files after filtering: {len(mc.added) + len(mc.changed)}')
# correct install size after filtering
analysis_res.install_size = sum(fm.file_size for fm in manifest.file_manifest_list.elements
if fm.filename in mc.added)
if mc.removed: if mc.removed:
analysis_res.removed = len(mc.removed) analysis_res.removed = len(mc.removed)
self.log.debug(f'{analysis_res.removed} removed files') self.log.debug(f'{analysis_res.removed} removed files')