diff --git a/legendary/cli.py b/legendary/cli.py index acd669bb..920e93b0 100644 --- a/legendary/cli.py +++ b/legendary/cli.py @@ -251,15 +251,14 @@ def sync_saves(self, args): # evaluate current save state for each game. for igame in igames: - if igame.app_name not in latest_save: - continue - game = self.core.get_game(igame.app_name) if 'CloudSaveFolder' not in game.metadata['customAttributes']: - # this should never happen unless cloud save support was removed from a game - logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!') + if igame.app_name in latest_save: + # this should never happen unless cloud save support was removed from a game + logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!') continue + logger.info(f'Checking "{igame.title}" ({igame.app_name})') # override save path only if app name is specified if args.app_name and args.save_path: logger.info(f'Overriding save path with "{args.save_path}"...') @@ -290,8 +289,11 @@ def sync_saves(self, args): igame.save_path = save_path self.core.lgd.set_installed_game(igame.app_name, igame) - # check if *any* file in the save game directory is newer than the latest uploaded save - res, (dt_l, dt_r) = self.core.check_savegame_state(igame.save_path, latest_save[igame.app_name]) + res, (dt_l, dt_r) = self.core.check_savegame_state(igame.save_path, latest_save.get(igame.app_name)) + + if res == SaveGameStatus.NO_SAVE: + logger.info('No cloud or local savegame found.') + continue if res == SaveGameStatus.SAME_AGE and not (args.force_upload or args.force_download): logger.info(f'Save game for "{igame.title}" is up to date, skipping...') @@ -300,8 +302,11 @@ def sync_saves(self, args): if (res == SaveGameStatus.REMOTE_NEWER and not args.force_upload) or args.force_download: if res == SaveGameStatus.REMOTE_NEWER: # only print this info if not forced logger.info(f'Cloud save for "{igame.title}" is newer:') - logger.info(f'- Cloud save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') - logger.info(f'- Local save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') + logger.info(f'- Cloud save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') + if dt_l: + logger.info(f'- Local save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') + else: + logger.info('- Local save date: N/A') if args.upload_only: logger.info('Save game downloading is disabled, skipping...') @@ -319,8 +324,11 @@ def sync_saves(self, args): elif res == SaveGameStatus.LOCAL_NEWER or args.force_upload: if res == SaveGameStatus.LOCAL_NEWER: logger.info(f'Local save for "{igame.title}" is newer') - logger.info(f'- Cloud save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') - logger.info(f'- Local save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') + if dt_r: + logger.info(f'- Cloud save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') + else: + logger.info('- Cloud save date: N/A') + logger.info(f'- Local save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') if args.download_only: logger.info('Save game uploading is disabled, skipping...') diff --git a/legendary/core.py b/legendary/core.py index c7f0b040..51c84b62 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -325,9 +325,18 @@ def check_savegame_state(self, path: str, save: SaveGameFile) -> (SaveGameStatus s = os.stat(os.path.join(_dir, _file)) latest = max(latest, s.st_mtime) + if not latest and not save: + return SaveGameStatus.NO_SAVE, (None, None) + # timezones are fun! dt_local = datetime.fromtimestamp(latest).replace(tzinfo=self.local_timezone).astimezone(timezone.utc) + if not save: + return SaveGameStatus.LOCAL_NEWER, (dt_local, None) + dt_remote = datetime.strptime(save.manifest_name, '%Y.%m.%d-%H.%M.%S.manifest').replace(tzinfo=timezone.utc) + if not latest: + return SaveGameStatus.REMOTE_NEWER, (None, dt_remote) + self.log.debug(f'Local save date: {str(dt_local)}, Remote save date: {str(dt_remote)}') # Ideally we check the files themselves based on manifest, @@ -348,9 +357,9 @@ def upload_save(self, app_name, save_dir, local_dt: datetime = None, include_f = exclude_f = None if not disable_filtering: # get file inclusion and exclusion filters if they exist - if _include := custom_attr.get('CloudIncludeList', {}).get('value', None) is not None: + if (_include := custom_attr.get('CloudIncludeList', {}).get('value', None)) is not None: include_f = _include.split(',') - if _exclude := custom_attr.get('CloudExcludeList', {}).get('value', None) is not None: + if (_exclude := custom_attr.get('CloudExcludeList', {}).get('value', None)) is not None: exclude_f = _exclude.split(',') if not save_path: diff --git a/legendary/models/game.py b/legendary/models/game.py index 3b19c109..43852add 100644 --- a/legendary/models/game.py +++ b/legendary/models/game.py @@ -124,6 +124,8 @@ def __init__(self, app_name='', filename='', manifest='', datetime=None): class SaveGameStatus(Enum): - LOCAL_NEWER = 1 - REMOTE_NEWER = -1 - SAME_AGE = 0 + LOCAL_NEWER = 0 + REMOTE_NEWER = 1 + SAME_AGE = 2 + NO_SAVE = 3 +