From 477827033e80aaead163bc0f7ca9a5679576fdca Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 2 Nov 2020 15:53:11 +0100 Subject: [PATCH] [cli/lfs] Add "cleanup" command to remove unused files --- legendary/cli.py | 27 ++++++++++++++++++++++++++- legendary/lfs/lgndry.py | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/legendary/cli.py b/legendary/cli.py index c9890bb..01956c6 100644 --- a/legendary/cli.py +++ b/legendary/cli.py @@ -1020,6 +1020,25 @@ class LegendaryCLI: print(f'EGL Sync enabled: {self.core.egl_sync_enabled}') print(f'Config directory: {self.core.lgd.path}') + def cleanup(self, args): + before = self.core.lgd.get_dir_size() + # delete metadata + logger.debug('Removing app metadata...') + app_names = set(g.app_name for g in self.core.get_assets(update_assets=False)) + self.core.lgd.clean_metadata(app_names) + + if not args.keep_manifests: + logger.debug('Removing manifests...') + installed = [(ig.app_name, ig.version) for ig in self.core.get_installed_list()] + installed.extend((ig.app_name, ig.version) for ig in self.core.get_installed_dlc_list()) + self.core.lgd.clean_manifests(installed) + + logger.debug('Removing tmp data') + self.core.lgd.clean_tmp_data() + + after = self.core.lgd.get_dir_size() + logger.info(f'Cleanup complete! Removed {(before - after)/1024/1024:.02f} MiB.') + def main(): parser = argparse.ArgumentParser(description=f'Legendary v{__version__} - "{__codename__}"') @@ -1050,6 +1069,7 @@ def main(): import_parser = subparsers.add_parser('import-game', help='Import an already installed game') egl_sync_parser = subparsers.add_parser('egl-sync', help='Setup or run Epic Games Launcher sync') status_parser = subparsers.add_parser('status', help='Show legendary status information') + clean_parser = subparsers.add_parser('cleanup', help='Remove old temporary, metadata, and manifest files') install_parser.add_argument('app_name', help='Name of the app', metavar='') uninstall_parser.add_argument('app_name', help='Name of the app', metavar='') @@ -1234,6 +1254,9 @@ def main(): status_parser.add_argument('--json', dest='json', action='store_true', help='Show status in JSON format') + clean_parser.add_argument('--keep-manifests', dest='keep_manifests', action='store_true', + help='Do not delete old manifests') + args, extra = parser.parse_known_args() if args.version: @@ -1243,7 +1266,7 @@ def main(): if args.subparser_name not in ('auth', 'list-games', 'list-installed', 'list-files', 'launch', 'download', 'uninstall', 'install', 'update', 'repair', 'list-saves', 'download-saves', 'sync-saves', - 'verify-game', 'import-game', 'egl-sync', 'status'): + 'verify-game', 'import-game', 'egl-sync', 'status', 'cleanup'): print(parser.format_help()) # Print the main help *and* the help for all of the subcommands. Thanks stackoverflow! @@ -1302,6 +1325,8 @@ def main(): cli.egs_sync(args) elif args.subparser_name == 'status': cli.status(args) + elif args.subparser_name == 'cleanup': + cli.cleanup(args) except KeyboardInterrupt: logger.info('Command was aborted via KeyboardInterrupt, cleaning up...') diff --git a/legendary/lfs/lgndry.py b/legendary/lfs/lgndry.py index 9f78ef6..1008c1e 100644 --- a/legendary/lfs/lgndry.py +++ b/legendary/lfs/lgndry.py @@ -5,6 +5,8 @@ import os import configparser import logging +from pathlib import Path + from legendary.models.game import * from legendary.utils.lfs import clean_filename @@ -195,6 +197,24 @@ class LGDLFS: except Exception as e: self.log.warning(f'Failed to delete file "{f}": {e!r}') + def clean_metadata(self, app_names): + for f in os.listdir(os.path.join(self.path, 'metadata')): + app_name = f.rpartition('.')[0] + if app_name not in app_names: + try: + os.remove(os.path.join(self.path, 'metadata', f)) + except Exception as e: + self.log.warning(f'Failed to delete file "{f}": {e!r}') + + def clean_manifests(self, in_use): + in_use_files = set(f'{clean_filename(f"{app_name}_{version}")}.manifest' for app_name, version in in_use) + for f in os.listdir(os.path.join(self.path, 'manifests')): + if f not in in_use_files: + try: + os.remove(os.path.join(self.path, 'manifests', f)) + except Exception as e: + self.log.warning(f'Failed to delete file "{f}": {e!r}') + def get_installed_game(self, app_name): if self._installed is None: try: @@ -244,3 +264,5 @@ class LGDLFS: with open(os.path.join(self.path, 'config.ini'), 'w') as cf: self.config.write(cf) + def get_dir_size(self): + return sum(f.stat().st_size for f in Path(self.path).glob('**/*') if f.is_file())