From 56a2314e40348a48bb0baa2add9aaf350d476d7e Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 23 Aug 2024 16:01:55 +0200 Subject: [PATCH] [core/models] Implement Sidecar config and epicdeploymentid parameter --- legendary/core.py | 30 ++++++++++++++++++++++++------ legendary/models/game.py | 26 +++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/legendary/core.py b/legendary/core.py index 5ba55aa..04344e3 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -431,26 +431,40 @@ class LegendaryCore: continue game = self.lgd.get_game_meta(app_name) - asset_updated = False + asset_updated = sidecar_updated = False if game: asset_updated = any(game.app_version(_p) != app_assets[_p].build_version for _p in app_assets.keys()) + # assuming sidecar data is the same for all platforms, just check the baseline (Windows) for updates. + sidecar_updated = (app_assets['Windows'].sidecar_rev > 0 and + (not game.sidecar or game.sidecar.rev != app_assets['Windows'].sidecar_rev)) games[app_name] = game - if update_assets and (not game or force_refresh or (game and asset_updated)): + if update_assets and (not game or force_refresh or (game and (asset_updated or sidecar_updated))): self.log.debug(f'Scheduling metadata update for {app_name}') # namespace/catalog item are the same for all platforms, so we can just use the first one _ga = next(iter(app_assets.values())) - fetch_list.append((app_name, _ga.namespace, _ga.catalog_item_id)) + fetch_list.append((app_name, _ga.namespace, _ga.catalog_item_id, sidecar_updated)) meta_updated = True def fetch_game_meta(args): - app_name, namespace, catalog_item_id = args + app_name, namespace, catalog_item_id, update_sidecar = args eg_meta = self.egs.get_game_info(namespace, catalog_item_id, timeout=10.0) if not eg_meta: self.log.warning(f'App {app_name} does not have any metadata!') eg_meta = dict(title='Unknown') - game = Game(app_name=app_name, app_title=eg_meta['title'], metadata=eg_meta, asset_infos=assets[app_name]) + sidecar = None + if update_sidecar: + self.log.debug(f'Updating sidecar information for {app_name}...') + manifest_api_response = self.egs.get_game_manifest(namespace, catalog_item_id, app_name) + # sidecar data is a JSON object encoded as a string for some reason + manifest_info = manifest_api_response['elements'][0] + if 'sidecar' in manifest_info: + sidecar_json = json.loads(manifest_info['sidecar']['config']) + sidecar = Sidecar(config=sidecar_json, rev=manifest_info['sidecar']['rvn']) + + game = Game(app_name=app_name, app_title=eg_meta['title'], metadata=eg_meta, asset_infos=assets[app_name], + sidecar=sidecar) self.lgd.set_game_meta(game.app_name, game) games[app_name] = game try: @@ -477,7 +491,7 @@ class LegendaryCore: if use_threads: self.log.warning(f'Fetching metadata for {app_name} failed, retrying') _ga = next(iter(app_assets.values())) - fetch_game_meta((app_name, _ga.namespace, _ga.catalog_item_id)) + fetch_game_meta((app_name, _ga.namespace, _ga.catalog_item_id, True)) game = games[app_name] if game.is_dlc and platform in app_assets: @@ -784,6 +798,10 @@ class LegendaryCore: f'-epicsandboxid={game.namespace}' ]) + if sidecar := game.sidecar: + if deployment_id := sidecar.config.get('deploymentId', None): + params.egl_parameters.append(f'-epicdeploymentid={deployment_id}') + if extra_args: params.user_parameters.extend(extra_args) diff --git a/legendary/models/game.py b/legendary/models/game.py index 8faa5a2..5a7eec6 100644 --- a/legendary/models/game.py +++ b/legendary/models/game.py @@ -18,6 +18,7 @@ class GameAsset: label_name: str = '' namespace: str = '' metadata: Dict = field(default_factory=dict) + sidecar_rev: int = 0 @classmethod def from_egs_json(cls, json): @@ -29,6 +30,7 @@ class GameAsset: tmp.label_name = json.get('labelName', '') tmp.namespace = json.get('namespace', '') tmp.metadata = json.get('metadata', {}) + tmp.sidecar_rev = json.get('sidecarRvn', 0) return tmp @classmethod @@ -41,9 +43,26 @@ class GameAsset: tmp.label_name = json.get('label_name', '') tmp.namespace = json.get('namespace', '') tmp.metadata = json.get('metadata', {}) + tmp.sidecar_rev = json.get('sidecar_rev', 0) return tmp +@dataclass +class Sidecar: + """ + App sidecar data + """ + config: Dict + rev: int + + @classmethod + def from_json(cls, json): + return cls( + config=json.get('config', {}), + rev=json.get('rev', 0) + ) + + @dataclass class Game: """ @@ -55,6 +74,7 @@ class Game: asset_infos: Dict[str, GameAsset] = field(default_factory=dict) base_urls: List[str] = field(default_factory=list) metadata: Dict = field(default_factory=dict) + sidecar: Optional[Sidecar] = None def app_version(self, platform='Windows'): if platform not in self.asset_infos: @@ -132,6 +152,9 @@ class Game: # Migrate old asset_info to new asset_infos tmp.asset_infos['Windows'] = GameAsset.from_json(json.get('asset_info', dict())) + if sidecar := json.get('sidecar', None): + tmp.sidecar = Sidecar.from_json(sidecar) + tmp.base_urls = json.get('base_urls', list()) return tmp @@ -139,8 +162,9 @@ class Game: def __dict__(self): """This is just here so asset_infos gets turned into a dict as well""" assets_dictified = {k: v.__dict__ for k, v in self.asset_infos.items()} + sidecar_dictified = self.sidecar.__dict__ if self.sidecar else None return dict(metadata=self.metadata, asset_infos=assets_dictified, app_name=self.app_name, - app_title=self.app_title, base_urls=self.base_urls) + app_title=self.app_title, base_urls=self.base_urls, sidecar=sidecar_dictified) @dataclass