From e5c7b029ffc925574c9822efd7a2ba3032cd7e37 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sun, 31 Jul 2022 23:00:50 +0300 Subject: [PATCH] GameInfo: Offer to also update the game after a verification if there is one --- rare/components/dialogs/install_dialog.py | 3 +- .../tabs/games/game_info/game_info.py | 86 ++++++++++++------- .../game_widgets/installed_icon_widget.py | 2 +- .../game_widgets/uninstalled_icon_widget.py | 2 +- rare/utils/legendary_utils.py | 47 +++++++--- 5 files changed, 96 insertions(+), 44 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 7bd6d493..05f6763c 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -25,7 +25,7 @@ from rare.utils import config_helper class InstallDialog(QDialog, Ui_InstallDialog): result_ready = pyqtSignal(InstallQueueItemModel) - def __init__(self, dl_item: InstallQueueItemModel, update=False, silent=False, parent=None): + def __init__(self, dl_item: InstallQueueItemModel, update=False, repair=False, silent=False, parent=None): super(InstallDialog, self).__init__(parent) self.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose, True) @@ -44,6 +44,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.game_path = self.game.metadata.get("customAttributes", {}).get("FolderName", {}).get("value", "") self.update = update + self.repair = repair self.silent = silent self.options_changed = False diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index 1cbce502..9f8fa904 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -85,7 +85,7 @@ class GameInfo(QWidget, Ui_GameInfo): if self.args.offline: self.repair_button.setDisabled(True) else: - self.repair_button.clicked.connect(lambda: self.repair(self.igame)) + self.repair_button.clicked.connect(self.repair) self.install_button.clicked.connect(lambda: self.game_utils.launch_game(self.game.app_name)) @@ -117,46 +117,78 @@ class GameInfo(QWidget, Ui_GameInfo): self.game_utils.update_list.emit(self.game.app_name) self.uninstalled.emit(self.game.app_name) - @pyqtSlot(InstalledGame) - def repair(self, igame: InstalledGame): - game = self.core.get_game(igame.app_name) - repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{igame.app_name}.repair") + @pyqtSlot() + def repair(self): + """ This function is to be called from the button only """ + repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.igame.app_name}.repair") if not os.path.exists(repair_file): QMessageBox.warning( self, - "Warning", + self.tr("Error - {}").format(self.igame.title), self.tr( "Repair file does not exist or game does not need a repair. Please verify game first" ), ) return - update = igame.version != game.app_version(igame.platform) + self.repair_game(self.igame) + + def repair_game(self, igame: InstalledGame): + game = self.core.get_game(igame.app_name) + ans = False + if igame.version != game.app_version(igame.platform): + ans = QMessageBox.question( + self, + self.tr("Repair and update?"), + self.tr( + "There is an update for {} from {} to {}." + "Do you want to update the game while repairing it?" + ).format(igame.title, igame.version, game.app_version(igame.platform)), + ) == QMessageBox.Yes self.signals.install_game.emit( - InstallOptionsModel(app_name=self.game.app_name, repair_mode=True, repair_and_update=update, update=True) + InstallOptionsModel( + app_name=igame.app_name, repair_mode=True, repair_and_update=ans, update=True + ) ) + @pyqtSlot() def verify(self): + """ This function is to be called from the button only """ if not os.path.exists(self.igame.install_path): - logger.error("Path does not exist") + logger.error(f"Installation path {self.igame.install_path} for {self.igame.title} does not exist") QMessageBox.warning( self, - "Warning", - self.tr("Installation path of {} does not exist. Cannot verify").format(self.igame.title), + self.tr("Error - {}").format(self.igame.title), + self.tr("Installation path for {} does not exist. Cannot continue.").format(self.igame.title), ) return + self.verify_game(self.igame) + + def verify_game(self, igame: InstalledGame): self.verify_widget.setCurrentIndex(1) - verify_worker = VerifyWorker(self.game.app_name) + verify_worker = VerifyWorker(igame.app_name) verify_worker.signals.status.connect(self.verify_status) verify_worker.signals.result.connect(self.verify_result) verify_worker.signals.error.connect(self.verify_error) self.verify_progress.setValue(0) - self.verify_threads[self.game.app_name] = verify_worker + self.verify_threads[igame.app_name] = verify_worker self.verify_pool.start(verify_worker) self.move_button.setEnabled(False) + def verify_cleanup(self, app_name: str): + self.verify_widget.setCurrentIndex(0) + self.verify_threads.pop(app_name) + self.move_button.setEnabled(True) + self.verify_button.setEnabled(True) + @pyqtSlot(str, str) def verify_error(self, app_name, message): - pass + self.verify_cleanup(app_name) + igame = self.core.get_installed_game(app_name) + QMessageBox.warning( + self, + self.tr("Error - {}").format(igame.title), + message + ) @pyqtSlot(str, int, int, float, float) def verify_status(self, app_name, num, total, percentage, speed): @@ -166,36 +198,30 @@ class GameInfo(QWidget, Ui_GameInfo): @pyqtSlot(str, bool, int, int) def verify_result(self, app_name, success, failed, missing): + self.verify_cleanup(app_name) + self.repair_button.setDisabled(success) igame = self.core.get_installed_game(app_name) if success: QMessageBox.information( self, self.tr("Summary - {}").format(igame.title), - self.tr("Game has been verified successfully. No missing or corrupt files found").format(igame.title), + self.tr("{} has been verified successfully. " + "No missing or corrupt files found").format(igame.title), ) - if igame.needs_verification: - igame.needs_verification = False - self.core.lgd.set_installed_game(igame.app_name, igame) - self.verification_finished.emit(igame) - elif failed == missing == -1: - QMessageBox.warning(self, self.tr("Warning - {}").format(igame.title), self.tr("Something went wrong")) - + self.verification_finished.emit(igame) else: ans = QMessageBox.question( self, self.tr("Summary - {}").format(igame.title), self.tr( - "Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them?" + "Verification failed, {} file(s) corrupted, {} file(s) are missing. " + "Do you want to repair them?" ).format(failed, missing), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes, ) if ans == QMessageBox.Yes: - self.repair(igame) - self.verify_widget.setCurrentIndex(0) - self.verify_threads.pop(app_name) - self.move_button.setEnabled(True) - self.verify_button.setEnabled(True) + self.repair_game(igame) @pyqtSlot(str) def move_game(self, dest_path): @@ -326,7 +352,9 @@ class GameInfo(QWidget, Ui_GameInfo): self.uninstall_button.setDisabled(False) self.verify_button.setDisabled(False) if not self.args.offline: - self.repair_button.setDisabled(False) + self.repair_button.setDisabled( + not os.path.exists(os.path.join(self.core.lgd.get_tmp_path(), f"{self.igame.app_name}.repair")) + ) self.game_actions_stack.setCurrentIndex(0) try: diff --git a/rare/components/tabs/games/game_widgets/installed_icon_widget.py b/rare/components/tabs/games/game_widgets/installed_icon_widget.py index 9904084c..f6b84680 100644 --- a/rare/components/tabs/games/game_widgets/installed_icon_widget.py +++ b/rare/components/tabs/games/game_widgets/installed_icon_widget.py @@ -43,7 +43,7 @@ class InstalledIconWidget(BaseInstalledWidget): minilayout.setSpacing(0) miniwidget.setLayout(minilayout) - self.title_label = ElideLabel(f"

{self.game.app_title}

", parent=miniwidget) + self.title_label = ElideLabel(f"{self.game.app_title}", parent=miniwidget) self.title_label.setAlignment(Qt.AlignTop) self.title_label.setObjectName("game_widget") minilayout.addWidget(self.title_label, stretch=2) diff --git a/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py b/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py index f3daf240..c0c2e548 100644 --- a/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py +++ b/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py @@ -29,7 +29,7 @@ class UninstalledIconWidget(BaseUninstalledWidget): minilayout.setSpacing(0) miniwidget.setLayout(minilayout) - self.title_label = ElideLabel(f"

{game.app_title}

", parent=miniwidget) + self.title_label = ElideLabel(f"{game.app_title}", parent=miniwidget) self.title_label.setAlignment(Qt.AlignTop) self.title_label.setObjectName("game_widget") minilayout.addWidget(self.title_label, stretch=2) diff --git a/rare/utils/legendary_utils.py b/rare/utils/legendary_utils.py index 787e7b51..a3805a65 100644 --- a/rare/utils/legendary_utils.py +++ b/rare/utils/legendary_utils.py @@ -8,7 +8,7 @@ from legendary.core import LegendaryCore from rare.lgndr.api_monkeys import LgndrIndirectStatus from rare.lgndr.api_arguments import LgndrVerifyGameArgs, LgndrUninstallGameArgs from rare.lgndr.api_exception import LgndrException -from rare.shared import LegendaryCLISingleton, LegendaryCoreSingleton +from rare.shared import LegendaryCLISingleton, LegendaryCoreSingleton, ArgumentsSingleton from rare.utils import config_helper logger = getLogger("Legendary Utils") @@ -90,6 +90,7 @@ class VerifyWorker(QRunnable): self.setAutoDelete(True) self.cli = LegendaryCLISingleton() self.core = LegendaryCoreSingleton() + self.args = ArgumentsSingleton() self.app_name = app_name def status_callback(self, num: int, total: int, percentage: float, speed: float): @@ -100,15 +101,37 @@ class VerifyWorker(QRunnable): args = LgndrVerifyGameArgs(app_name=self.app_name, indirect_status=status, verify_stdout=self.status_callback) - # TODO: offer this as an alternative when manifest doesn't exist - # TODO: requires the client to be online. To do it this way, we need to - # TODO: somehow detect the error and offer a dialog in which case `verify_games` is - # TODO: re-run with `repair_mode` and `repair_online` - # FIXME: This will crash in offline mode. Offline mode needs a re-thinking in general. + + # lk: first pass, verify with the current manifest + repair_mode = False result = self.cli.verify_game( - args, print_command=False, repair_mode=True, repair_online=True) - # success, failed, missing = self.cli.verify_game(args, print_command=False) - if result: - self.signals.result.emit(self.app_name, not any(result), *result) - else: - self.signals.error.emit(self.app_name, status.message) + args, print_command=False, repair_mode=repair_mode, repair_online=not self.args.offline + ) + if result is None: + # lk: second pass with the latest manifest + # lk: this happens if the manifest was not found and repair_mode was not requested + # lk: we already have checked if the directory exists before starting the worker + try: + # lk: this try-except block handles the exception caused by a missing manifest + # lk: and is raised only in the case we are offline + repair_mode = True + result = self.cli.verify_game( + args, print_command=False, repair_mode=repair_mode, repair_online=not self.args.offline + ) + if result is None: + raise ValueError + except ValueError: + self.signals.error.emit(self.app_name, status.message) + return + + success = result is not None and not any(result) + if success: + # lk: if verification was successful we delete the repair file and run the clean procedure + # lk: this could probably be cut down to what is relevant for this use-case and skip the `cli` call + igame = self.core.get_installed_game(self.app_name) + game = self.core.get_game(self.app_name, platform=igame.platform) + repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.app_name}.repair') + self.cli.clean_post_install(game=game, igame=igame, repair=True, repair_file=repair_file) + + self.signals.result.emit(self.app_name, success, *result) +