Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
setuptools
pyinstaller
requests
requests_futures
filelock

- name: Optional dependencies (WebView)
Expand Down
17 changes: 17 additions & 0 deletions legendary/api/egs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
# coding: utf-8

import urllib.parse
from urllib3.util import Retry

import requests
import requests.adapters
import logging

from requests_futures.sessions import FuturesSession

from requests.auth import HTTPBasicAuth

from legendary.models.exceptions import InvalidCredentialsError
Expand Down Expand Up @@ -43,9 +46,22 @@ def __init__(self, lc='en', cc='US', timeout=10.0):
# increase maximum pool size for multithreaded metadata requests
self.session.mount('https://', requests.adapters.HTTPAdapter(pool_maxsize=16))

retries = Retry(
total=3,
backoff_factor=0.1,
status_forcelist=[500, 501, 502, 503, 504],
allowed_methods={'GET'}
)

self.unauth_session = requests.session()
self.unauth_session.headers['User-Agent'] = self._user_agent

self.unauth_session.mount('https://', requests.adapters.HTTPAdapter(pool_connections=16, pool_maxsize=16, max_retries=retries))

self.unauth_future_session = FuturesSession(session=self.unauth_session,max_workers=16)
self.unauth_future_session.headers['User-Agent'] = self._user_agent


self._oauth_basic = HTTPBasicAuth(self._user_basic, self._pw_basic)

self.access_token = None
Expand All @@ -68,6 +84,7 @@ def update_egs_params(self, egs_params):
self._store_user_agent = f'EpicGamesLauncher/{version}'
self.session.headers['User-Agent'] = self._user_agent
self.unauth_session.headers['User-Agent'] = self._user_agent
self.unauth_future_session.headers['User-Agent'] = self._user_agent
# update label
if label := egs_params['label']:
self._label = label
Expand Down
36 changes: 23 additions & 13 deletions legendary/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,11 +1043,9 @@ def download_saves(self, app_name='', manifest_name='', save_dir='', clean_dir=F
os.makedirs(save_path)

_save_dir = save_dir
savegames = self.egs.get_user_cloud_saves(app_name=app_name)
files = savegames['files']
manifests = self.egs.get_user_cloud_saves(app_name=app_name,manifests=True)
files = manifests["files"]
for fname, f in files.items():
if '.manifest' not in fname:
continue
f_parts = fname.split('/')

if manifest_name and f_parts[4] != manifest_name:
Expand Down Expand Up @@ -1088,17 +1086,29 @@ def download_saves(self, app_name='', manifest_name='', save_dir='', clean_dir=F
m = self.load_manifest(r.content)

# download chunks required for extraction
chunks = dict()
chunkPaths = []
for chunk in m.chunk_data_list.elements:
cpath_p = fname.split('/', 3)[:3]
cpath_p.append(chunk.path)
cpath = '/'.join(cpath_p)
if cpath not in files:
self.log.warning(f'Chunk {cpath} not in file list, save data may be incomplete!')
continue
chunkPaths.append(chunk.path)
Comment thread
danez marked this conversation as resolved.
Outdated

chunkSaves = self.egs.get_user_cloud_saves(app_name=app_name, filenames=chunkPaths, manifests=False)
chunkFiles = chunkSaves["files"]

if len(chunkFiles) != len(chunkPaths):
self.log.warning(f'Expected {len(chunkPaths)} chunks, but only found {len(chunkFiles)}! Save may be incomplete.')
continue

self.log.debug(f'Downloading chunk "{cpath}"')
r = self.egs.unauth_session.get(files[cpath]['readLink'])
chunkLinks = [chunkFiles[c]['readLink'] for c in chunkFiles]
self.log.info(f'Downloading {len(chunkLinks)} chunks...')

def log_download(r, *args, **kwargs):
self.log.debug(f'Downloaded chunk {'/'.join(urlparse(r.url).path.split('/')[5:])} successfully.')

# map chunkLinks to self.egs.unauth_future_session.get
futures = [self.egs.unauth_future_session.get(link, hooks={'response': log_download}) for link in chunkLinks]

chunks = dict()
for future in futures:
r = future.result()
if r.status_code != 200:
self.log.error(f'Download failed, status code: {r.status_code}')
break
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
requests<3.0
requests_futures
filelock
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
),
install_requires=[
'requests<3.0',
'requests_futures',
'setuptools',
'wheel',
'filelock'
Expand Down
Loading