mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 17:55:27 +00:00
[api/cli/core/lfs] Add Legendary update check
This commit is contained in:
parent
69eeccec21
commit
a465966954
22
legendary/api/lgd.py
Normal file
22
legendary/api/lgd.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# !/usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import legendary
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class LGDAPI:
|
||||||
|
_user_agent = f'Legendary/{legendary.__version__}'
|
||||||
|
_api_host = 'legendary.rodney.io'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.session = requests.session()
|
||||||
|
self.log = logging.getLogger('LGDAPI')
|
||||||
|
self.session.headers['User-Agent'] = self._user_agent
|
||||||
|
|
||||||
|
def get_version_information(self):
|
||||||
|
r = self.session.get(f'https://{self._api_host}/version.json',
|
||||||
|
timeout=10.0)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
|
@ -1065,6 +1065,8 @@ class LegendaryCLI:
|
||||||
exit(1)
|
exit(1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
# if automatic checks are off force an update here
|
||||||
|
self.core.check_for_updates(force=True)
|
||||||
|
|
||||||
if not self.core.lgd.userdata:
|
if not self.core.lgd.userdata:
|
||||||
user_name = '<not logged in>'
|
user_name = '<not logged in>'
|
||||||
|
@ -1089,6 +1091,16 @@ class LegendaryCLI:
|
||||||
print(f'Games installed: {games_installed}')
|
print(f'Games installed: {games_installed}')
|
||||||
print(f'EGL Sync enabled: {self.core.egl_sync_enabled}')
|
print(f'EGL Sync enabled: {self.core.egl_sync_enabled}')
|
||||||
print(f'Config directory: {self.core.lgd.path}')
|
print(f'Config directory: {self.core.lgd.path}')
|
||||||
|
print(f'\nLegendary version: {__version__} - "{__codename__}"')
|
||||||
|
print(f'Update available: {"yes" if self.core.update_available else "no"}')
|
||||||
|
if self.core.update_available:
|
||||||
|
if update_info := self.core.get_update_info():
|
||||||
|
print(f'- New version: {update_info["version"]} - "{update_info["name"]}"')
|
||||||
|
print(f'- Release summary:\n{update_info["summary"]}\n- Release URL: {update_info["gh_url"]}')
|
||||||
|
if update_info['critical']:
|
||||||
|
print('! This update is recommended as it fixes major issues.')
|
||||||
|
# prevent update message on close
|
||||||
|
self.core.update_available = False
|
||||||
|
|
||||||
def cleanup(self, args):
|
def cleanup(self, args):
|
||||||
before = self.core.lgd.get_dir_size()
|
before = self.core.lgd.get_dir_size()
|
||||||
|
@ -1413,6 +1425,15 @@ def main():
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info('Command was aborted via KeyboardInterrupt, cleaning up...')
|
logger.info('Command was aborted via KeyboardInterrupt, cleaning up...')
|
||||||
|
|
||||||
|
# show note if update is available
|
||||||
|
if cli.core.update_available:
|
||||||
|
if update_info := cli.core.get_update_info():
|
||||||
|
print(f'\nLegendary update available!')
|
||||||
|
print(f'- New version: {update_info["version"]} - "{update_info["name"]}"')
|
||||||
|
print(f'- Release summary:\n{update_info["summary"]}\n- Release URL: {update_info["gh_url"]}')
|
||||||
|
if update_info['critical']:
|
||||||
|
print('! This update is recommended as it fixes major issues.')
|
||||||
|
|
||||||
cli.core.exit()
|
cli.core.exit()
|
||||||
ql.stop()
|
ql.stop()
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
|
@ -16,7 +16,9 @@ from requests.exceptions import HTTPError
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from legendary import __version__
|
||||||
from legendary.api.egs import EPCAPI
|
from legendary.api.egs import EPCAPI
|
||||||
|
from legendary.api.lgd import LGDAPI
|
||||||
from legendary.downloader.mp.manager import DLManager
|
from legendary.downloader.mp.manager import DLManager
|
||||||
from legendary.lfs.egl import EPCLFS
|
from legendary.lfs.egl import EPCLFS
|
||||||
from legendary.lfs.lgndry import LGDLFS
|
from legendary.lfs.lgndry import LGDLFS
|
||||||
|
@ -50,6 +52,7 @@ class LegendaryCore:
|
||||||
self.egs = EPCAPI()
|
self.egs = EPCAPI()
|
||||||
self.lgd = LGDLFS()
|
self.lgd = LGDLFS()
|
||||||
self.egl = EPCLFS()
|
self.egl = EPCLFS()
|
||||||
|
self.lgdapi = LGDAPI()
|
||||||
|
|
||||||
# on non-Windows load the programdata path from config
|
# on non-Windows load the programdata path from config
|
||||||
if os.name != 'nt':
|
if os.name != 'nt':
|
||||||
|
@ -75,6 +78,8 @@ class LegendaryCore:
|
||||||
else:
|
else:
|
||||||
self.log.warning(f'Could not determine locale, falling back to en-US')
|
self.log.warning(f'Could not determine locale, falling back to en-US')
|
||||||
|
|
||||||
|
self.update_available = False
|
||||||
|
|
||||||
def auth(self, username, password):
|
def auth(self, username, password):
|
||||||
"""
|
"""
|
||||||
Attempts direct non-web login, raises CaptchaError if manual login is required
|
Attempts direct non-web login, raises CaptchaError if manual login is required
|
||||||
|
@ -153,6 +158,15 @@ class LegendaryCore:
|
||||||
if not self.lgd.userdata:
|
if not self.lgd.userdata:
|
||||||
raise ValueError('No saved credentials')
|
raise ValueError('No saved credentials')
|
||||||
|
|
||||||
|
# run update check
|
||||||
|
if self.update_check_enabled():
|
||||||
|
try:
|
||||||
|
self.check_for_updates()
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(f'Checking for Legendary updates failed: {e!r}')
|
||||||
|
else:
|
||||||
|
self.apply_lgd_config()
|
||||||
|
|
||||||
if self.lgd.userdata['expires_at']:
|
if self.lgd.userdata['expires_at']:
|
||||||
dt_exp = datetime.fromisoformat(self.lgd.userdata['expires_at'][:-1])
|
dt_exp = datetime.fromisoformat(self.lgd.userdata['expires_at'][:-1])
|
||||||
dt_now = datetime.utcnow()
|
dt_now = datetime.utcnow()
|
||||||
|
@ -185,6 +199,40 @@ class LegendaryCore:
|
||||||
self.lgd.userdata = userdata
|
self.lgd.userdata = userdata
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def update_check_enabled(self):
|
||||||
|
return self.lgd.config.getboolean('Legendary', 'enable_update_check',
|
||||||
|
fallback=os.name == 'nt')
|
||||||
|
|
||||||
|
def check_for_updates(self, force=False):
|
||||||
|
def version_tuple(v):
|
||||||
|
return tuple(map(int, (v.split('.'))))
|
||||||
|
|
||||||
|
cached = self.lgd.get_cached_version()
|
||||||
|
version_info = cached['data']
|
||||||
|
if force or not version_info or (datetime.now().timestamp() - cached['last_update']) > 24*3600:
|
||||||
|
version_info = self.lgdapi.get_version_information()
|
||||||
|
self.lgd.set_cached_version(version_info)
|
||||||
|
|
||||||
|
web_version = version_info['release_info']['version']
|
||||||
|
self.update_available = version_tuple(web_version) > version_tuple(__version__)
|
||||||
|
self.apply_lgd_config(version_info)
|
||||||
|
|
||||||
|
def apply_lgd_config(self, version_info=None):
|
||||||
|
"""Applies configuration options returned by update API"""
|
||||||
|
if not version_info:
|
||||||
|
version_info = self.lgd.get_cached_version()['data']
|
||||||
|
# if cached data is invalid
|
||||||
|
if not version_info:
|
||||||
|
self.log.debug('No cached legendary config to apply.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'egl_config' in version_info:
|
||||||
|
self.egs.update_egs_params(version_info['egl_config'])
|
||||||
|
# todo update sid auth/downloader UA and game overrides
|
||||||
|
|
||||||
|
def get_update_info(self):
|
||||||
|
return self.lgd.get_cached_version()['data'].get('release_info')
|
||||||
|
|
||||||
def get_assets(self, update_assets=False, platform_override=None) -> List[GameAsset]:
|
def get_assets(self, update_assets=False, platform_override=None) -> List[GameAsset]:
|
||||||
# do not save and always fetch list when platform is overridden
|
# do not save and always fetch list when platform is overridden
|
||||||
if platform_override:
|
if platform_override:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from time import time
|
||||||
|
|
||||||
from legendary.models.game import *
|
from legendary.models.game import *
|
||||||
from legendary.utils.config import LGDConf
|
from legendary.utils.config import LGDConf
|
||||||
|
@ -28,6 +29,8 @@ class LGDLFS:
|
||||||
self._assets = None
|
self._assets = None
|
||||||
# EGS metadata
|
# EGS metadata
|
||||||
self._game_metadata = dict()
|
self._game_metadata = dict()
|
||||||
|
# Legendary update check info
|
||||||
|
self._update_info = None
|
||||||
# Config with game specific settings (e.g. start parameters, env variables)
|
# Config with game specific settings (e.g. start parameters, env variables)
|
||||||
self.config = LGDConf(comment_prefixes='/', allow_no_value=True)
|
self.config = LGDConf(comment_prefixes='/', allow_no_value=True)
|
||||||
self.config.optionxform = str
|
self.config.optionxform = str
|
||||||
|
@ -285,3 +288,18 @@ class LGDLFS:
|
||||||
|
|
||||||
def get_dir_size(self):
|
def get_dir_size(self):
|
||||||
return sum(f.stat().st_size for f in Path(self.path).glob('**/*') if f.is_file())
|
return sum(f.stat().st_size for f in Path(self.path).glob('**/*') if f.is_file())
|
||||||
|
|
||||||
|
def get_cached_version(self):
|
||||||
|
try:
|
||||||
|
self._update_info = json.load(open(os.path.join(self.path, 'version.json')))
|
||||||
|
return self._update_info
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug(f'Failed to load cached update data: {e!r}')
|
||||||
|
return dict(last_update=0, data=None)
|
||||||
|
|
||||||
|
def set_cached_version(self, version_data):
|
||||||
|
if not version_data:
|
||||||
|
return
|
||||||
|
self._update_info = dict(last_update=time(), data=version_data)
|
||||||
|
json.dump(self._update_info, open(os.path.join(self.path, 'version.json'), 'w'),
|
||||||
|
indent=2, sort_keys=True)
|
||||||
|
|
Loading…
Reference in a new issue