Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package legendary for openSUSE:Factory checked in at 2026-01-28 15:12:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/legendary (Old) and /work/SRC/openSUSE:Factory/.legendary.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "legendary" Wed Jan 28 15:12:51 2026 rev:2 rq:1329573 version:0.20.39 Changes: -------- --- /work/SRC/openSUSE:Factory/legendary/legendary.changes 2025-11-28 16:52:55.630272987 +0100 +++ /work/SRC/openSUSE:Factory/.legendary.new.1928/legendary.changes 2026-01-28 15:15:39.175239484 +0100 @@ -1,0 +2,5 @@ +Tue Jan 27 22:02:59 UTC 2026 - Jonatas Gonçalves <[email protected]> + +- Update to version 0.20.39 - This Vortal Coil (Heroic) + +------------------------------------------------------------------- Old: ---- legendary-0.20.38.tar.gz New: ---- legendary-0.20.39.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ legendary.spec ++++++ --- /var/tmp/diff_new_pack.vLdDuE/_old 2026-01-28 15:15:41.731345875 +0100 +++ /var/tmp/diff_new_pack.vLdDuE/_new 2026-01-28 15:15:41.731345875 +0100 @@ -16,7 +16,7 @@ # Name: legendary -Version: 0.20.38 +Version: 0.20.39 Release: 0 Summary: An Epic Games Launcher alternative License: GPL-3.0-only ++++++ legendary-0.20.38.tar.gz -> legendary-0.20.39.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/.github/workflows/python.yml new/legendary-0.20.39/.github/workflows/python.yml --- old/legendary-0.20.38/.github/workflows/python.yml 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/.github/workflows/python.yml 2026-01-21 15:51:43.000000000 +0100 @@ -12,25 +12,25 @@ strategy: matrix: os: [ - 'ubuntu-22.04', 'ubuntu-22.04-arm', - 'windows-latest', 'windows-11-arm', - 'macos-13', 'macos-14' + 'ubuntu-24.04', 'ubuntu-24.04-arm', + 'windows-2025', 'windows-11-arm', + 'macos-15-intel', 'macos-15' ] fail-fast: false - max-parallel: 3 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.13' - name: Legendary dependencies and build tools run: pip3 install --upgrade setuptools pyinstaller requests + requests_futures filelock - name: Optional dependencies (WebView) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/legendary/__init__.py new/legendary-0.20.39/legendary/__init__.py --- old/legendary-0.20.38/legendary/__init__.py 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/legendary/__init__.py 2026-01-21 15:51:43.000000000 +0100 @@ -1,4 +1,4 @@ """Legendary!""" -__version__ = '0.20.38' -__codename__ = 'To The White Forest (Heroic)' +__version__ = '0.20.39' +__codename__ = 'This Vortal Coil (Heroic)' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/legendary/api/egs.py new/legendary-0.20.39/legendary/api/egs.py --- old/legendary-0.20.38/legendary/api/egs.py 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/legendary/api/egs.py 2026-01-21 15:51:43.000000000 +0100 @@ -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 @@ -43,9 +46,22 @@ # 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 @@ -68,6 +84,7 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/legendary/cli.py new/legendary-0.20.39/legendary/cli.py --- old/legendary-0.20.38/legendary/cli.py 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/legendary/cli.py 2026-01-21 15:51:43.000000000 +0100 @@ -845,7 +845,7 @@ logger.warning(f'Pre-launch command failed: {e!r}') logger.debug(f'Opening Origin URI with command: {shlex.join(command)}') - subprocess.Popen(command, env=full_env) + subprocess.Popen(command, env=full_env, cwd=full_env.get('WINEPREFIX', None)) def install_game(self, args): if not self.core.lgd.lock_installed(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/legendary/core.py new/legendary-0.20.39/legendary/core.py --- old/legendary-0.20.38/legendary/core.py 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/legendary/core.py 2026-01-21 15:51:43.000000000 +0100 @@ -450,7 +450,7 @@ def fetch_game_meta(args): app_name, namespace, catalog_item_id, update_sidecar = args - eg_meta = self.egs.get_game_info(namespace, catalog_item_id, timeout=10.0) + eg_meta = self.egs.get_game_info(namespace, catalog_item_id, timeout=30.0) if not eg_meta: self.log.warning(f'App {app_name} does not have any metadata!') eg_meta = dict(title='Unknown') @@ -555,7 +555,7 @@ game = self.lgd.get_game_meta(libitem['appName']) if not game or force_refresh: - eg_meta = self.egs.get_game_info(libitem['namespace'], libitem['catalogItemId']) + eg_meta = self.egs.get_game_info(libitem['namespace'], libitem['catalogItemId'], timeout=30) game = Game(app_name=libitem['appName'], app_title=eg_meta['title'], metadata=eg_meta) self.lgd.set_game_meta(game.app_name, game) @@ -1043,11 +1043,9 @@ 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: @@ -1088,17 +1086,26 @@ m = self.load_manifest(r.content) # download chunks required for extraction - chunks = dict() - 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 = list(chunk.path for chunk in m.chunk_data_list.elements) + 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/requirements.txt new/legendary-0.20.39/requirements.txt --- old/legendary-0.20.38/requirements.txt 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/requirements.txt 2026-01-21 15:51:43.000000000 +0100 @@ -1,2 +1,3 @@ requests<3.0 +requests_futures filelock diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/legendary-0.20.38/setup.py new/legendary-0.20.39/setup.py --- old/legendary-0.20.38/setup.py 2025-09-13 02:12:19.000000000 +0200 +++ new/legendary-0.20.39/setup.py 2026-01-21 15:51:43.000000000 +0100 @@ -36,6 +36,7 @@ ), install_requires=[ 'requests<3.0', + 'requests_futures', 'setuptools', 'wheel', 'filelock'
