diff --git a/rare/components/__init__.py b/rare/components/__init__.py index e76dacf1..f4c9cce4 100644 --- a/rare/components/__init__.py +++ b/rare/components/__init__.py @@ -12,7 +12,7 @@ from requests import HTTPError from rare.components.dialogs.launch_dialog import LaunchDialog from rare.components.main_window import MainWindow from rare.shared import RareCore -from rare.utils import config_helper, paths +from rare.utils import paths from rare.utils.misc import ExitCodes from rare.widgets.rare_app import RareApp, RareAppException diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index 3582304a..9afcaac2 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -16,12 +16,15 @@ 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 rare.utils import config_helper from .image_manager import ImageManager from .workers import ( QueueWorker, VerifyWorker, MoveWorker, FetchWorker, + GamesDlcsWorker, + EntitlementsWorker, OriginWineWorker, ) from .workers.uninstall import uninstall_game @@ -70,6 +73,10 @@ class RareCore(QObject): self.__eos_overlay.signals.game.install.connect(self.__signals.game.install) self.__eos_overlay.signals.game.uninstall.connect(self.__signals.game.uninstall) + self.__fetch_progress: int = 0 + self.__fetched_games_dlcs: bool = False + self.__fetched_entitlements: bool = False + RareCore.__instance = self def enqueue_worker(self, rgame: RareGame, worker: QueueWorker): @@ -303,24 +310,45 @@ class RareCore(QObject): logger.info(f'Marking "{rgame.app_title}" as not installed because an exception has occurred...') logger.error(e) rgame.set_installed(False) - self.progress.emit(int(idx/length * 80) + 20, self.tr("Loaded {}").format(rgame.app_title)) + progress = int(idx/length * self.__fetch_progress) + (100 - self.__fetch_progress) + self.progress.emit(progress, self.tr("Loaded {}").format(rgame.app_title)) + + @pyqtSlot(int, str) + def __on_fetch_progress(self, increment: int, message: str): + self.__fetch_progress += increment + self.progress.emit(self.__fetch_progress, message) @pyqtSlot(object, int) - def __on_fetch_result(self, result: Tuple[List, Dict], res_type: int): - logger.info(f"Got API results for {FetchWorker.Result(res_type).name}") - 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.perf_counter() - self.__start_time} seconds") - QTimer.singleShot(100, self.__post_init) - self.completed.emit() + def __on_fetch_result(self, result: Tuple, result_type: int): + if result_type == FetchWorker.Result.GAMESDLCS: + self.__add_games_and_dlcs(*result) + self.__fetched_games_dlcs = True + + if result_type == FetchWorker.Result.ENTITLEMENTS: + self.__core.lgd.entitlements = result + self.__fetched_entitlements = True + + logger.info(f"Acquired data for {FetchWorker.Result(result_type).name}") + + if all([self.__fetched_games_dlcs, self.__fetched_entitlements]): + logger.debug(f"Fetch time {time.perf_counter() - self.__start_time} seconds") + self.progress.emit(100, self.tr("Launching Rare")) + self.completed.emit() + QTimer.singleShot(100, self.__post_init) def fetch(self): 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) - QThreadPool.globalInstance().start(fetch_worker) + + games_dlcs_worker = GamesDlcsWorker(self.__core, self.__args) + games_dlcs_worker.signals.progress.connect(self.__on_fetch_progress) + games_dlcs_worker.signals.result.connect(self.__on_fetch_result) + + entitlements_worker = EntitlementsWorker(self.__core, self.__args) + entitlements_worker.signals.progress.connect(self.__on_fetch_progress) + entitlements_worker.signals.result.connect(self.__on_fetch_result) + + QThreadPool.globalInstance().start(games_dlcs_worker) + QThreadPool.globalInstance().start(entitlements_worker) def fetch_saves(self): def __fetch() -> None: @@ -346,24 +374,6 @@ class RareCore(QObject): saves_worker = QRunnable.create(__fetch) QThreadPool.globalInstance().start(saves_worker) - def fetch_entitlements(self) -> None: - def __fetch() -> None: - try: - 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"Exception while fetching entitlements from EGS.") - logger.error(e) - return - logger.info(f"Entitlements: {len(list(entitlements))}") - - entitlements_worker = QRunnable.create(__fetch) - QThreadPool.globalInstance().start(entitlements_worker) - def resolve_origin(self) -> None: origin_worker = OriginWineWorker(self.__core, list(self.origin_games)) QThreadPool.globalInstance().start(origin_worker) @@ -371,7 +381,6 @@ class RareCore(QObject): def __post_init(self) -> None: if not self.__args.offline: self.fetch_saves() - # self.fetch_entitlements() self.resolve_origin() @property diff --git a/rare/shared/workers/__init__.py b/rare/shared/workers/__init__.py index b11df089..12d816c0 100644 --- a/rare/shared/workers/__init__.py +++ b/rare/shared/workers/__init__.py @@ -1,4 +1,4 @@ -from .fetch import FetchWorker +from .fetch import FetchWorker, GamesDlcsWorker, EntitlementsWorker from .install_info import InstallInfoWorker from .move import MoveWorker from .uninstall import UninstallWorker diff --git a/rare/shared/workers/fetch.py b/rare/shared/workers/fetch.py index a1173ad5..1d4fe761 100644 --- a/rare/shared/workers/fetch.py +++ b/rare/shared/workers/fetch.py @@ -15,9 +15,9 @@ logger = getLogger("FetchWorker") class FetchWorker(Worker): class Result(IntEnum): - GAMES = 1 - NON_ASSET = 2 - COMBINED = 3 + ERROR = 0 + GAMESDLCS = 1 + ENTITLEMENTS = 2 class Signals(QObject): progress = pyqtSignal(int, str) @@ -30,7 +30,33 @@ class FetchWorker(Worker): self.args = args self.settings = QSettings() + +class EntitlementsWorker(FetchWorker): + def __init__(self, core: LegendaryCore, args: Namespace): + super(EntitlementsWorker, self).__init__(core, args) + def run_real(self): + entitlements = () + want_entitlements = not self.settings.value("exclude_entitlements", False, bool) + if want_entitlements: + # Get entitlements, Ubisoft integration also uses them + self.signals.progress.emit(0, 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: %s", len(list(entitlements))) + self.signals.result.emit(entitlements, FetchWorker.Result.ENTITLEMENTS) + return + + +class GamesDlcsWorker(FetchWorker): + + def __init__(self, core: LegendaryCore, args: Namespace): + super(GamesDlcsWorker, self).__init__(core, args) + self.exclude_non_asset = QSettings().value("exclude_non_asset", False, bool) + + def run_real(self): + # Fetch regular EGL games with assets want_unreal = self.settings.value("unreal_meta", False, bool) or self.args.debug want_win32 = self.settings.value("win32_meta", False, bool) @@ -74,25 +100,19 @@ class FetchWorker(Worker): ) 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 + want_non_asset = not self.settings.value("exclude_non_asset", False, bool) 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(f"Network error while fetching non asset games") 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. + # NOTE: This is here because of broken appIds from Epic + # https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729 except Exception as e: logger.error("General exception while fetching non asset games from EGS.") logger.error(e) @@ -108,13 +128,5 @@ class FetchWorker(Worker): 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) + self.signals.progress.emit(40, self.signals.tr("Preparing library")) + self.signals.result.emit((games, dlc_dict), FetchWorker.Result.GAMESDLCS)