[api/cli/core] Make API timeout configurable

This commit is contained in:
derrod 2021-12-30 12:03:43 +01:00
parent dd099c0afd
commit 03ef95923d
3 changed files with 49 additions and 28 deletions

View file

@ -29,7 +29,7 @@ class EPCAPI:
_store_gql_host = 'store-launcher.epicgames.com'
_artifact_service_host = 'artifact-public-service-prod.beee.live.use1a.on.epicgames.com'
def __init__(self, lc='en', cc='US'):
def __init__(self, lc='en', cc='US', timeout=10.0):
self.log = logging.getLogger('EPCAPI')
self.session = requests.session()
@ -47,6 +47,7 @@ class EPCAPI:
self.language_code = lc
self.country_code = cc
self.request_timeout = timeout
def update_egs_params(self, egs_params):
# update user-agent
@ -66,7 +67,8 @@ class EPCAPI:
def resume_session(self, session):
self.session.headers['Authorization'] = f'bearer {session["access_token"]}'
r = self.session.get(f'https://{self._oauth_host}/account/api/oauth/verify')
r = self.session.get(f'https://{self._oauth_host}/account/api/oauth/verify',
timeout=self.request_timeout)
if r.status_code >= 500:
r.raise_for_status()
@ -97,7 +99,8 @@ class EPCAPI:
raise ValueError('At least one token type must be specified!')
r = self.session.post(f'https://{self._oauth_host}/account/api/oauth/token',
data=params, auth=self._oauth_basic)
data=params, auth=self._oauth_basic,
timeout=self.request_timeout)
# Only raise HTTP exceptions on server errors
if r.status_code >= 500:
r.raise_for_status()
@ -115,10 +118,12 @@ class EPCAPI:
return j
def invalidate_session(self): # unused
r = self.session.delete(f'https://{self._oauth_host}/account/api/oauth/sessions/kill/{self.access_token}')
_ = self.session.delete(f'https://{self._oauth_host}/account/api/oauth/sessions/kill/{self.access_token}',
timeout=self.request_timeout)
def get_game_token(self):
r = self.session.get(f'https://{self._oauth_host}/account/api/oauth/exchange')
r = self.session.get(f'https://{self._oauth_host}/account/api/oauth/exchange',
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
@ -126,32 +131,35 @@ class EPCAPI:
user_id = self.user.get('account_id')
r = self.session.post(f'https://{self._ecommerce_host}/ecommerceintegration/api/public/'
f'platforms/EPIC/identities/{user_id}/ownershipToken',
data=dict(nsCatalogItemId=f'{namespace}:{catalog_item_id}'))
data=dict(nsCatalogItemId=f'{namespace}:{catalog_item_id}'),
timeout=self.request_timeout)
r.raise_for_status()
return r.content
def get_external_auths(self):
user_id = self.user.get('account_id')
r = self.session.get(f'https://{self._oauth_host}/account/api/public/account/{user_id}/externalAuths')
r = self.session.get(f'https://{self._oauth_host}/account/api/public/account/{user_id}/externalAuths',
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
def get_game_assets(self, platform='Windows', label='Live'):
r = self.session.get(f'https://{self._launcher_host}/launcher/api/public/assets/{platform}',
params=dict(label=label))
params=dict(label=label), timeout=self.request_timeout)
r.raise_for_status()
return r.json()
def get_game_manifest(self, namespace, catalog_item_id, app_name, platform='Windows', label='Live'):
r = self.session.get(f'https://{self._launcher_host}/launcher/api/public/assets/v2/platform'
f'/{platform}/namespace/{namespace}/catalogItem/{catalog_item_id}/app'
f'/{app_name}/label/{label}')
f'/{app_name}/label/{label}',
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
def get_launcher_manifests(self, platform='Windows', label=None):
r = self.session.get(f'https://{self._launcher_host}/launcher/api/public/assets/v2/platform/'
f'{platform}/launcher',
f'{platform}/launcher', timeout=self.request_timeout,
params=dict(label=label if label else self._label))
r.raise_for_status()
return r.json()
@ -159,7 +167,7 @@ class EPCAPI:
def get_user_entitlements(self):
user_id = self.user.get('account_id')
r = self.session.get(f'https://{self._entitlements_host}/entitlement/api/account/{user_id}/entitlements',
params=dict(start=0, count=5000))
params=dict(start=0, count=5000), timeout=self.request_timeout)
r.raise_for_status()
return r.json()
@ -167,7 +175,7 @@ class EPCAPI:
r = self.session.get(f'https://{self._catalog_host}/catalog/api/shared/namespace/{namespace}/bulk/items',
params=dict(id=catalog_item_id, includeDLCDetails=True, includeMainGameDetails=True,
country=self.country_code, locale=self.language_code),
timeout=timeout)
timeout=timeout or self.request_timeout)
r.raise_for_status()
return r.json().get(catalog_item_id, None)
@ -177,7 +185,8 @@ class EPCAPI:
r = self.session.post(f'https://{self._artifact_service_host}/artifact-service/api/public/v1/dependency/'
f'sandbox/{sandbox_id}/artifact/{artifact_id}/ticket',
json=dict(label=label, expiresInSeconds=300, platform=platform),
params=dict(useSandboxAwareLabel='false'))
params=dict(useSandboxAwareLabel='false'),
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
@ -185,14 +194,16 @@ class EPCAPI:
# Untested as get_artifact_service_ticket is not working yet either
r = self.session.post(f'https://{self._launcher_host}/launcher/api/public/assets/v2/'
f'by-ticket/app/{artifact_id}',
headers=dict(authorization=f'bearer {ticket["signedTicket"]}'))
headers=dict(authorization=f'bearer {ticket["signedTicket"]}'),
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
def get_library_items(self, include_metadata=True):
records = []
r = self.session.get(f'https://{self._library_host}/library/api/public/items',
params=dict(includeMetadata=include_metadata))
params=dict(includeMetadata=include_metadata),
timeout=self.request_timeout)
r.raise_for_status()
j = r.json()
records.extend(j['records'])
@ -200,7 +211,8 @@ class EPCAPI:
# Fetch remaining library entries as long as there is a cursor
while cursor := j['responseMetadata'].get('nextCursor', None):
r = self.session.get(f'https://{self._library_host}/library/api/public/items',
params=dict(includeMetadata=include_metadata, cursor=cursor))
params=dict(includeMetadata=include_metadata, cursor=cursor),
timeout=self.request_timeout)
r.raise_for_status()
j = r.json()
records.extend(j['records'])
@ -217,19 +229,22 @@ class EPCAPI:
if filenames:
r = self.session.post(f'https://{self._datastorage_host}/api/v1/access/egstore/savesync/'
f'{user_id}/{app_name}', json=dict(files=filenames))
f'{user_id}/{app_name}',
json=dict(files=filenames),
timeout=self.request_timeout)
else:
r = self.session.get(f'https://{self._datastorage_host}/api/v1/access/egstore/savesync/'
f'{user_id}/{app_name}')
f'{user_id}/{app_name}',
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
def create_game_cloud_saves(self, app_name, filenames):
return self.get_user_cloud_saves(app_name, filenames=filenames)
def delete_game_cloud_save_file(self, path):
url = f'https://{self._datastorage_host}/api/v1/data/egstore/{path}'
r = self.session.delete(url)
r = self.session.delete(url, timeout=self.request_timeout)
r.raise_for_status()
def store_get_uplay_codes(self):
@ -237,7 +252,8 @@ class EPCAPI:
r = self.session.post(f'https://{self._store_gql_host}/graphql',
headers={'user-agent': self._store_user_agent},
json=dict(query=uplay_codes_query,
variables=dict(accountId=user_id)))
variables=dict(accountId=user_id)),
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
@ -248,7 +264,8 @@ class EPCAPI:
json=dict(query=uplay_claim_query,
variables=dict(accountId=user_id,
uplayAccountId=uplay_id,
gameId=game_id)))
gameId=game_id)),
timeout=self.request_timeout)
r.raise_for_status()
return r.json()
@ -258,6 +275,7 @@ class EPCAPI:
headers={'user-agent': self._store_user_agent},
json=dict(query=uplay_redeem_query,
variables=dict(accountId=user_id,
uplayAccountId=uplay_id)))
uplayAccountId=uplay_id)),
timeout=self.request_timeout)
r.raise_for_status()
return r.json()

View file

@ -39,8 +39,8 @@ logger = logging.getLogger('cli')
class LegendaryCLI:
def __init__(self, override_config=None):
self.core = LegendaryCore(override_config)
def __init__(self, override_config=None, api_timeout=None):
self.core = LegendaryCore(override_config, timeout=api_timeout)
self.logger = logging.getLogger('cli')
self.logging_queue = None
@ -2142,6 +2142,9 @@ def main():
help='Pretty-print JSON')
parser.add_argument('-H', '--full-help', dest='full_help', action='store_true',
help='Show full help (including individual command help)')
parser.add_argument('-T', '--api-timeout', dest='api_timeout', action='store',
type=float, default=10, metavar='<seconds>',
help='API HTTP request timeout (default: 10 seconds)')
# all the commands
subparsers = parser.add_subparsers(title='Commands', dest='subparser_name', metavar='<command>')
@ -2477,7 +2480,7 @@ def main():
print(subparser.format_help())
return
cli = LegendaryCLI(override_config=args.config_file)
cli = LegendaryCLI(override_config=args.config_file, api_timeout=args.api_timeout)
ql = cli.setup_threaded_logging()
config_ll = cli.core.lgd.config.get('Legendary', 'log_level', fallback='info')

View file

@ -56,9 +56,9 @@ class LegendaryCore:
"""
_egl_version = '11.0.1-14907503+++Portal+Release-Live'
def __init__(self, override_config=None):
def __init__(self, override_config=None, timeout=10.0):
self.log = logging.getLogger('Core')
self.egs = EPCAPI()
self.egs = EPCAPI(timeout=timeout)
self.lgd = LGDLFS(config_file=override_config)
self.egl = EPCLFS()
self.lgdapi = LGDAPI()