RareCore: Add options to exclude non-asset games and entitlements from startup
* Add options in settings to not request non-asset games and entitlements. This greatly improves startup time but reduces functionality a bit. Both options are disabled by default. * Remove the old options to exclude Win32 and Mac games as they were no longer relevant. * Add a performance context manager to log execution time when used. * Fixed an issue with UbisoftGroup where multiple threads would spawn.
This commit is contained in:
parent
9d02fec6f8
commit
5319da4bdb
|
@ -1,4 +1,3 @@
|
|||
import platform
|
||||
import re
|
||||
from logging import getLogger
|
||||
from typing import Tuple
|
||||
|
@ -82,20 +81,27 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
|
|||
)
|
||||
self.locale_layout.addWidget(self.locale_edit)
|
||||
|
||||
self.win32_cb.setChecked(self.settings.value("win32_meta", False, bool))
|
||||
self.win32_cb.stateChanged.connect(lambda: self.settings.setValue("win32_meta", self.win32_cb.isChecked()))
|
||||
self.exclude_non_asset_check.setChecked(
|
||||
self.settings.value("exclude_non_asset", False, bool)
|
||||
)
|
||||
self.exclude_non_asset_check.stateChanged.connect(
|
||||
lambda: self.settings.setValue("exclude_non_asset", self.exclude_non_asset_check.isChecked())
|
||||
)
|
||||
self.exclude_entitlements_check.setChecked(
|
||||
self.settings.value("exclude_entitlements", False, bool)
|
||||
)
|
||||
self.exclude_entitlements_check.stateChanged.connect(
|
||||
lambda: self.settings.setValue("exclude_entitlements", self.exclude_entitlements_check.isChecked())
|
||||
)
|
||||
|
||||
self.mac_cb.setChecked(self.settings.value("mac_meta", platform.system() == "Darwin", bool))
|
||||
self.mac_cb.stateChanged.connect(lambda: self.settings.setValue("mac_meta", self.mac_cb.isChecked()))
|
||||
|
||||
self.refresh_game_meta_btn.clicked.connect(self.refresh_game_meta)
|
||||
self.refresh_game_meta_button.clicked.connect(self.refresh_game_meta)
|
||||
|
||||
def refresh_game_meta(self):
|
||||
self.refresh_game_meta_btn.setDisabled(True)
|
||||
self.refresh_game_meta_btn.setText(self.tr("Loading"))
|
||||
self.refresh_game_meta_button.setDisabled(True)
|
||||
self.refresh_game_meta_button.setText(self.tr("Loading"))
|
||||
worker = RefreshGameMetaWorker()
|
||||
worker.signals.finished.connect(lambda: self.refresh_game_meta_btn.setDisabled(False))
|
||||
worker.signals.finished.connect(lambda: self.refresh_game_meta_btn.setText(self.tr("Refresh game meta")))
|
||||
worker.signals.finished.connect(lambda: self.refresh_game_meta_button.setDisabled(False))
|
||||
worker.signals.finished.connect(lambda: self.refresh_game_meta_button.setText(self.tr("Refresh game meta")))
|
||||
QThreadPool.globalInstance().start(worker)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -9,12 +9,13 @@ from typing import Dict, Iterator, Callable, Optional, List, Union, Iterable, Tu
|
|||
from PyQt5.QtCore import QObject, pyqtSignal, QSettings, pyqtSlot, QThreadPool, QRunnable, QTimer
|
||||
from legendary.lfs.eos import EOSOverlayApp
|
||||
from legendary.models.game import Game, SaveGameFile
|
||||
from requests import HTTPError
|
||||
from requests.exceptions import HTTPError, ConnectionError
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.models.base_game import RareSaveGame
|
||||
from rare.models.game import RareGame, RareEosOverlay
|
||||
from rare.models.signals import GlobalSignals
|
||||
from rare.utils.metrics import timelogger
|
||||
from .image_manager import ImageManager
|
||||
from .workers import (
|
||||
QueueWorker,
|
||||
|
@ -49,7 +50,7 @@ class RareCore(QObject):
|
|||
self.__core: Optional[LegendaryCore] = None
|
||||
self.__image_manager: Optional[ImageManager] = None
|
||||
|
||||
self.__start_time = time.time()
|
||||
self.__start_time = time.perf_counter()
|
||||
|
||||
self.args(args)
|
||||
self.signals(init=True)
|
||||
|
@ -273,12 +274,12 @@ class RareCore(QObject):
|
|||
self.progress.emit(15, self.tr("Preparing library"))
|
||||
self.__add_games_and_dlcs(*result)
|
||||
self.progress.emit(100, self.tr("Launching Rare"))
|
||||
logger.debug(f"Fetch time {time.time() - self.__start_time} seconds")
|
||||
logger.debug(f"Fetch time {time.perf_counter() - self.__start_time} seconds")
|
||||
QTimer.singleShot(100, self.__post_init)
|
||||
self.completed.emit()
|
||||
|
||||
def fetch(self):
|
||||
self.__start_time = time.time()
|
||||
self.__start_time = time.perf_counter()
|
||||
fetch_worker = FetchWorker(self.__core, self.__args)
|
||||
fetch_worker.signals.progress.connect(self.progress)
|
||||
fetch_worker.signals.result.connect(self.__on_fetch_result)
|
||||
|
@ -286,10 +287,10 @@ class RareCore(QObject):
|
|||
|
||||
def fetch_saves(self):
|
||||
def __fetch() -> None:
|
||||
start_time = time.time()
|
||||
saves_dict: Dict[str, List[SaveGameFile]] = {}
|
||||
try:
|
||||
saves_list = self.__core.get_save_games()
|
||||
with timelogger(logger, "Request saves"):
|
||||
saves_list = self.__core.get_save_games()
|
||||
for s in saves_list:
|
||||
if s.app_name not in saves_dict.keys():
|
||||
saves_dict[s.app_name] = [s]
|
||||
|
@ -300,26 +301,28 @@ class RareCore(QObject):
|
|||
continue
|
||||
self.__library[app_name].load_saves(saves)
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.error(f"Exception while fetching saves from EGS: {e}")
|
||||
logger.error(f"Exception while fetching saves from EGS.")
|
||||
logger.error(e)
|
||||
return
|
||||
logger.debug(f"Saves: {len(saves_dict)}")
|
||||
logger.debug(f"Request saves: {time.time() - start_time} seconds")
|
||||
logger.info(f"Saves: {len(saves_dict)}")
|
||||
|
||||
saves_worker = QRunnable.create(__fetch)
|
||||
QThreadPool.globalInstance().start(saves_worker)
|
||||
|
||||
def fetch_entitlements(self) -> None:
|
||||
def __fetch() -> None:
|
||||
start_time = time.time()
|
||||
try:
|
||||
entitlements = self.__core.egs.get_user_entitlements()
|
||||
if (entitlements := self.__core.lgd.entitlements) is None:
|
||||
with timelogger(logger, "Request entitlements"):
|
||||
entitlements = self.__core.egs.get_user_entitlements()
|
||||
self.__core.lgd.entitlements = entitlements
|
||||
for game in self.__library.values():
|
||||
game.grant_date()
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.error(f"Failed to retrieve user entitlements from EGS: {e}")
|
||||
logger.error(f"Exception while fetching entitlements from EGS.")
|
||||
logger.error(e)
|
||||
return
|
||||
logger.debug(f"Entitlements: {len(list(entitlements))}")
|
||||
logger.debug(f"Request Entitlements: {time.time() - start_time} seconds")
|
||||
logger.info(f"Entitlements: {len(list(entitlements))}")
|
||||
|
||||
entitlements_worker = QRunnable.create(__fetch)
|
||||
QThreadPool.globalInstance().start(entitlements_worker)
|
||||
|
@ -331,7 +334,7 @@ class RareCore(QObject):
|
|||
def __post_init(self) -> None:
|
||||
if not self.__args.offline:
|
||||
self.fetch_saves()
|
||||
self.fetch_entitlements()
|
||||
# self.fetch_entitlements()
|
||||
self.resolve_origin()
|
||||
|
||||
@property
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import time
|
||||
from argparse import Namespace
|
||||
from enum import IntEnum
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from requests.exceptions import ConnectionError, HTTPError
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QSettings
|
||||
from requests.exceptions import HTTPError, ConnectionError
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.utils.metrics import timelogger
|
||||
from .worker import Worker
|
||||
|
||||
logger = getLogger("FetchWorker")
|
||||
|
@ -27,44 +27,61 @@ class FetchWorker(Worker):
|
|||
self.signals = FetchWorker.Signals()
|
||||
self.core = core
|
||||
self.args = args
|
||||
self.exclude_non_asset = QSettings().value("exclude_non_asset", False, bool)
|
||||
self.exclude_entitlements = QSettings().value("exclude_entitlements", False, bool)
|
||||
|
||||
def run_real(self):
|
||||
progress = 0
|
||||
|
||||
# Fetch regular EGL games with assets
|
||||
self.signals.progress.emit(0, self.signals.tr("Updating game metadata"))
|
||||
start_time = time.time()
|
||||
games, dlc_dict = self.core.get_game_and_dlc_list(
|
||||
update_assets=not self.args.offline, platform="Windows", skip_ue=False
|
||||
)
|
||||
logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}")
|
||||
logger.debug(f"Request games: {time.time() - start_time} seconds")
|
||||
self.signals.progress.emit(progress, self.signals.tr("Updating game metadata"))
|
||||
with timelogger(logger, "Request games"):
|
||||
games, dlc_dict = self.core.get_game_and_dlc_list(
|
||||
update_assets=not self.args.offline, platform="Windows", skip_ue=False
|
||||
)
|
||||
progress += 10
|
||||
logger.info(f"Games: {len(games)}. Games with DLCs {len(dlc_dict)}")
|
||||
|
||||
# Fetch non-asset games
|
||||
self.signals.progress.emit(10, self.signals.tr("Updating non-asset metadata"))
|
||||
start_time = time.time()
|
||||
try:
|
||||
na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.warning(f"Exception while fetching non asset games from EGS: {e}")
|
||||
na_games, na_dlc_dict = ([], {})
|
||||
# FIXME:
|
||||
# This is here because of broken appIds from Epic:
|
||||
# https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729
|
||||
# There is a tab character in the appId of Fallout New Vegas: Honest Hearts DLC, this breaks metadata storage
|
||||
# on Windows as they can't handle tabs at the end of the filename (?)
|
||||
# Legendary and Heroic are also affected, but it completely breaks Rare, so dodge it for now pending a fix.
|
||||
except Exception as e:
|
||||
logger.error(f"Exception while fetching non asset games from EGS: {e}")
|
||||
na_games, na_dlc_dict = ([], {})
|
||||
logger.debug(f"Non-asset {len(na_games)}, games with non-asset DLCs {len(na_dlc_dict)}")
|
||||
logger.debug(f"Request non-asset: {time.time() - start_time} seconds")
|
||||
if not self.exclude_non_asset:
|
||||
self.signals.progress.emit(progress, self.signals.tr("Updating non-asset metadata"))
|
||||
try:
|
||||
with timelogger(logger, "Request non-asset"):
|
||||
na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.error(f"Exception while fetching non asset games from EGS.")
|
||||
logger.error(e)
|
||||
na_games, na_dlc_dict = ([], {})
|
||||
# FIXME:
|
||||
# This is here because of broken appIds from Epic:
|
||||
# https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729
|
||||
# There is a tab character in the appId of Fallout New Vegas: Honest Hearts DLC, this breaks metadata storage
|
||||
# on Windows as they can't handle tabs at the end of the filename (?)
|
||||
# Legendary and Heroic are also affected, but it completely breaks Rare, so dodge it for now pending a fix.
|
||||
except Exception as e:
|
||||
logger.error(f"Exception while fetching non asset games from EGS.")
|
||||
logger.error(e)
|
||||
na_games, na_dlc_dict = ([], {})
|
||||
progress += 10
|
||||
logger.info(f"Non-asset: {len(na_games)}. Non-asset with DLCs: {len(na_dlc_dict)}")
|
||||
|
||||
# Combine the two games lists and the two dlc dictionaries between regular and non-asset results
|
||||
games += na_games
|
||||
for catalog_id, dlcs in na_dlc_dict.items():
|
||||
if catalog_id in dlc_dict.keys():
|
||||
dlc_dict[catalog_id] += dlcs
|
||||
else:
|
||||
dlc_dict[catalog_id] = dlcs
|
||||
logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}")
|
||||
# Combine the two games lists and the two dlc dictionaries between regular and non-asset results
|
||||
games += na_games
|
||||
for catalog_id, dlcs in na_dlc_dict.items():
|
||||
if catalog_id in dlc_dict.keys():
|
||||
dlc_dict[catalog_id] += dlcs
|
||||
else:
|
||||
dlc_dict[catalog_id] = dlcs
|
||||
logger.info(f"Games: {len(games)}. Games with DLCs: {len(dlc_dict)}")
|
||||
|
||||
if not self.exclude_entitlements:
|
||||
# Get entitlements, Ubisoft integration also uses them
|
||||
self.signals.progress.emit(progress, self.signals.tr("Updating entitlements"))
|
||||
with timelogger(logger, "Request entitlements"):
|
||||
entitlements = self.core.egs.get_user_entitlements()
|
||||
self.core.lgd.entitlements = entitlements
|
||||
progress += 10
|
||||
logger.info(f"Entitlements: {len(list(entitlements))}")
|
||||
|
||||
self.signals.progress.emit(progress, self.signals.tr("Preparing games"))
|
||||
self.signals.result.emit((games, dlc_dict), FetchWorker.Result.COMBINED)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'rare/ui/components/tabs/settings/legendary.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.7
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
@ -128,17 +128,17 @@ class Ui_LegendarySettings(object):
|
|||
self.right_layout.addWidget(self.cleanup_group, 0, QtCore.Qt.AlignTop)
|
||||
self.meta_group = QtWidgets.QGroupBox(LegendarySettings)
|
||||
self.meta_group.setObjectName("meta_group")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.meta_group)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.win32_cb = QtWidgets.QCheckBox(self.meta_group)
|
||||
self.win32_cb.setObjectName("win32_cb")
|
||||
self.verticalLayout_2.addWidget(self.win32_cb)
|
||||
self.mac_cb = QtWidgets.QCheckBox(self.meta_group)
|
||||
self.mac_cb.setObjectName("mac_cb")
|
||||
self.verticalLayout_2.addWidget(self.mac_cb)
|
||||
self.refresh_game_meta_btn = QtWidgets.QPushButton(self.meta_group)
|
||||
self.refresh_game_meta_btn.setObjectName("refresh_game_meta_btn")
|
||||
self.verticalLayout_2.addWidget(self.refresh_game_meta_btn)
|
||||
self.meta_layout = QtWidgets.QVBoxLayout(self.meta_group)
|
||||
self.meta_layout.setObjectName("meta_layout")
|
||||
self.exclude_non_asset_check = QtWidgets.QCheckBox(self.meta_group)
|
||||
self.exclude_non_asset_check.setObjectName("exclude_non_asset_check")
|
||||
self.meta_layout.addWidget(self.exclude_non_asset_check)
|
||||
self.exclude_entitlements_check = QtWidgets.QCheckBox(self.meta_group)
|
||||
self.exclude_entitlements_check.setObjectName("exclude_entitlements_check")
|
||||
self.meta_layout.addWidget(self.exclude_entitlements_check)
|
||||
self.refresh_game_meta_button = QtWidgets.QPushButton(self.meta_group)
|
||||
self.refresh_game_meta_button.setObjectName("refresh_game_meta_button")
|
||||
self.meta_layout.addWidget(self.refresh_game_meta_button)
|
||||
self.right_layout.addWidget(self.meta_group)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.right_layout.addItem(spacerItem1)
|
||||
|
@ -163,9 +163,15 @@ class Ui_LegendarySettings(object):
|
|||
self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests"))
|
||||
self.clean_button.setText(_translate("LegendarySettings", "Remove everything"))
|
||||
self.meta_group.setTitle(_translate("LegendarySettings", "Game metadata"))
|
||||
self.win32_cb.setText(_translate("LegendarySettings", "Load 32bit data"))
|
||||
self.mac_cb.setText(_translate("LegendarySettings", "Load MacOS data"))
|
||||
self.refresh_game_meta_btn.setText(_translate("LegendarySettings", "Refresh game meta"))
|
||||
self.exclude_non_asset_check.setToolTip(_translate("LegendarySettings", "Do not load metadata for non-asset games (i.e. Origin games) on start-up.\n"
|
||||
"\n"
|
||||
"Disabling this greatly improves start-up time, but some games might not be visible in your library."))
|
||||
self.exclude_non_asset_check.setText(_translate("LegendarySettings", "Exclude non-asset games"))
|
||||
self.exclude_entitlements_check.setToolTip(_translate("LegendarySettings", "Do not load entitlement data (i.e game\'s date of purchase) on start-up.\n"
|
||||
"\n"
|
||||
"Disabling this greatly improves start-up time, but some library filters may not work."))
|
||||
self.exclude_entitlements_check.setText(_translate("LegendarySettings", "Exclude entitlements"))
|
||||
self.refresh_game_meta_button.setText(_translate("LegendarySettings", "Refresh game meta"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -231,23 +231,33 @@
|
|||
<property name="title">
|
||||
<string>Game metadata</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="meta_layout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="win32_cb">
|
||||
<widget class="QCheckBox" name="exclude_non_asset_check">
|
||||
<property name="toolTip">
|
||||
<string>Do not load metadata for non-asset games (i.e. Origin games) on start-up.
|
||||
|
||||
Disabling this greatly improves start-up time, but some games might not be visible in your library.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load 32bit data</string>
|
||||
<string>Exclude non-asset games</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="mac_cb">
|
||||
<widget class="QCheckBox" name="exclude_entitlements_check">
|
||||
<property name="toolTip">
|
||||
<string>Do not load entitlement data (i.e game's date of purchase) on start-up.
|
||||
|
||||
Disabling this greatly improves start-up time, but some library filters may not work.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load MacOS data</string>
|
||||
<string>Exclude entitlements</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="refresh_game_meta_btn">
|
||||
<widget class="QPushButton" name="refresh_game_meta_button">
|
||||
<property name="text">
|
||||
<string>Refresh game meta</string>
|
||||
</property>
|
||||
|
|
Loading…
Reference in a new issue