diff --git a/rare/components/tabs/settings/legendary.py b/rare/components/tabs/settings/legendary.py index c4254608..2b08a01b 100644 --- a/rare/components/tabs/settings/legendary.py +++ b/rare/components/tabs/settings/legendary.py @@ -116,6 +116,19 @@ class LegendarySettings(QWidget, Ui_LegendarySettings): lambda: self.settings.setValue("unreal_meta", self.fetch_unreal_check.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.refresh_metadata_button.clicked.connect(self.refresh_metadata) # FIXME: Disable the button for now because it interferes with RareCore self.refresh_metadata_button.setEnabled(False) diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index 31a80f06..3582304a 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -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, @@ -50,7 +51,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) @@ -310,12 +311,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) @@ -323,10 +324,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] @@ -337,26 +338,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) @@ -368,7 +371,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 diff --git a/rare/shared/workers/fetch.py b/rare/shared/workers/fetch.py index b18ea83f..a1173ad5 100644 --- a/rare/shared/workers/fetch.py +++ b/rare/shared/workers/fetch.py @@ -1,13 +1,13 @@ import platform -import time from argparse import Namespace from enum import IntEnum from logging import getLogger from PyQt5.QtCore import QObject, pyqtSignal, QSettings -from requests.exceptions import ConnectionError, HTTPError +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") @@ -32,13 +32,11 @@ class FetchWorker(Worker): def run_real(self): # Fetch regular EGL games with assets - start_time = time.time() - want_unreal = self.settings.value("unreal_meta", False, bool) or self.args.debug want_win32 = self.settings.value("win32_meta", False, bool) want_macos = self.settings.value("macos_meta", False, bool) need_macos = platform.system() == "Darwin" - need_windows = not any([want_win32, want_macos, need_macos, self.args.debug]) + need_windows = not any([want_win32, want_macos, need_macos, self.args.debug]) and not self.args.offline if want_win32 or self.args.debug: logger.info( @@ -47,9 +45,10 @@ class FetchWorker(Worker): "with" if want_unreal else "without" ) self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows")) - self.core.get_game_and_dlc_list( - update_assets=not self.args.offline, platform="Win32", skip_ue=not want_unreal - ) + with timelogger(logger, "Request Win32 games"): + self.core.get_game_and_dlc_list( + update_assets=not self.args.offline, platform="Win32", skip_ue=not want_unreal + ) if need_macos or want_macos or self.args.debug: logger.info( @@ -58,9 +57,10 @@ class FetchWorker(Worker): "with" if want_unreal else "without" ) self.signals.progress.emit(15, self.signals.tr("Updating game metadata for macOS")) - self.core.get_game_and_dlc_list( - update_assets=not self.args.offline, platform="Mac", skip_ue=not want_unreal - ) + with timelogger(logger, "Request macOS games"): + self.core.get_game_and_dlc_list( + update_assets=not self.args.offline, platform="Mac", skip_ue=not want_unreal + ) if need_windows: self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows")) @@ -68,39 +68,53 @@ class FetchWorker(Worker): "Requesting Windows metadata, %s Unreal engine", "with" if want_unreal else "without" ) - games, dlc_dict = self.core.get_game_and_dlc_list( - update_assets=need_windows, platform="Windows", skip_ue=not want_unreal - ) - logger.debug(f"Games {len(games)}, games with DLCs {len(dlc_dict)}") - logger.debug(f"Request games: {time.time() - start_time} seconds") + with timelogger(logger, "Request Windows games"): + games, dlc_dict = self.core.get_game_and_dlc_list( + update_assets=need_windows, platform="Windows", skip_ue=not want_unreal + ) + logger.info(f"Games: %s. Games with DLCs: %s", len(games), len(dlc_dict)) + + want_non_asset = not self.settings.value("exclude_non_asset", False, bool) + want_entitlements = not self.settings.value("exclude_entitlements", False, bool) # Fetch non-asset games - self.signals.progress.emit(30, self.signals.tr("Updating non-asset game 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 want_non_asset: + self.signals.progress.emit(30, self.signals.tr("Updating non-asset game 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("Network 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("General exception while fetching non asset games from EGS.") + logger.error(e) + na_games, na_dlc_dict = ([], {}) + logger.info("Non-asset: %s. Non-asset with DLCs: %s", len(na_games), 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 want_entitlements: + # Get entitlements, Ubisoft integration also uses them + self.signals.progress.emit(40, self.signals.tr("Updating entitlements")) + with timelogger(logger, "Request entitlements"): + entitlements = self.core.egs.get_user_entitlements() + self.core.lgd.entitlements = entitlements + logger.info(f"Entitlements: {len(list(entitlements))}") + + self.signals.progress.emit(50, self.signals.tr("Preparing games")) self.signals.result.emit((games, dlc_dict), FetchWorker.Result.COMBINED) diff --git a/rare/ui/components/tabs/settings/legendary.py b/rare/ui/components/tabs/settings/legendary.py index 829a60ab..a8306397 100644 --- a/rare/ui/components/tabs/settings/legendary.py +++ b/rare/ui/components/tabs/settings/legendary.py @@ -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.10 +# Created by: PyQt5 UI code generator 5.15.7 # # 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. @@ -139,6 +139,12 @@ class Ui_LegendarySettings(object): self.fetch_unreal_check = QtWidgets.QCheckBox(self.metadata_group) self.fetch_unreal_check.setObjectName("fetch_unreal_check") self.verticalLayout_2.addWidget(self.fetch_unreal_check) + self.exclude_non_asset_check = QtWidgets.QCheckBox(self.meta_group) + self.exclude_non_asset_check.setObjectName("exclude_non_asset_check") + self.verticalLayout_2.addWidget(self.exclude_non_asset_check) + self.exclude_entitlements_check = QtWidgets.QCheckBox(self.meta_group) + self.exclude_entitlements_check.setObjectName("exclude_entitlements_check") + self.verticalLayout_2.addWidget(self.exclude_entitlements_check) self.metadata_info = QtWidgets.QLabel(self.metadata_group) font = QtGui.QFont() font.setItalic(True) @@ -175,6 +181,14 @@ class Ui_LegendarySettings(object): self.fetch_win32_check.setText(_translate("LegendarySettings", "Include Win32 games")) self.fetch_macos_check.setText(_translate("LegendarySettings", "Include macOS games")) self.fetch_unreal_check.setText(_translate("LegendarySettings", "Include Unreal engine")) + 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.metadata_info.setText(_translate("LegendarySettings", "Restart Rare to apply")) self.refresh_metadata_button.setText(_translate("LegendarySettings", "Refresh metadata")) diff --git a/rare/ui/components/tabs/settings/legendary.ui b/rare/ui/components/tabs/settings/legendary.ui index 65e69f1c..9f1a9c4c 100644 --- a/rare/ui/components/tabs/settings/legendary.ui +++ b/rare/ui/components/tabs/settings/legendary.ui @@ -231,7 +231,7 @@ Platforms - + @@ -253,6 +253,30 @@ + + + + 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. + + + Exclude non-asset games + + + + + + + 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. + + + Exclude entitlements + + +