From 5afe4d875f1de625f70cdd289770b6d4b9fa8f9e Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Fri, 15 Oct 2021 01:08:18 +0300 Subject: [PATCH] Change workflow in EGLSync. EGLSync is enabled when a correct manifest or wine prefix folder is specified. If the path is removed, sync is disabled. (--unlink) Automatic sync is controlled by a chechbox. It doesn't sync on first activation for now. `Sync once` is removed because it can be serviced by `Select all` -> `Import`/`Export` Still work in progress. --- .../tabs/games/import_sync/egl_sync_widget.py | 228 +++++++++--------- .../tabs/games/import_sync/import_widget.py | 4 +- rare/components/tabs/tab_widget.py | 2 +- .../tabs/games/import_sync/egl_sync_widget.py | 52 ++-- .../tabs/games/import_sync/egl_sync_widget.ui | 88 +++---- 5 files changed, 179 insertions(+), 195 deletions(-) diff --git a/rare/components/tabs/games/import_sync/egl_sync_widget.py b/rare/components/tabs/games/import_sync/egl_sync_widget.py index 676955f9..6e914ac9 100644 --- a/rare/components/tabs/games/import_sync/egl_sync_widget.py +++ b/rare/components/tabs/games/import_sync/egl_sync_widget.py @@ -1,6 +1,7 @@ import os import platform from glob import glob +from typing import Tuple from logging import getLogger from PyQt5.QtCore import Qt, QThread @@ -11,14 +12,10 @@ import rare.shared as shared from rare.ui.components.tabs.games.import_sync.egl_sync_widget import Ui_EGLSyncGroup from rare.utils.extra_widgets import PathEdit from rare.utils.utils import WineResolver +from rare.utils.models import PathSpec logger = getLogger("EGLSync") -appdata_path_spec = \ - r'%LOCALAPPDATA%\EpicGamesLauncher\Saved\Config\Windows' -programdata_path_spec = \ - r'%PROGRAMDATA%\Epic\EpicGamesLauncher\Data\Manifests' - class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): importable_items = list() @@ -31,52 +28,58 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): self.export_list.setProperty("noBorder", 1) self.import_list.setProperty("noBorder", 1) + self.core = shared.core + if platform.system() == "Windows": - estimated_path = os.path.expandvars(programdata_path_spec) + estimated_path = os.path.expandvars(PathSpec.egl_programdata) else: estimated_path = str() - self.wine_resolver = WineResolver(programdata_path_spec, 'default', shared.legendary_core) + self.wine_resolver = WineResolver(PathSpec.egl_programdata, 'default', shared.core) self.wine_resolver.result_ready.connect(self.egl_path_info.setText) self.wine_resolver.finished.connect(self.wine_resolver.quit) self.wine_resolver.finished.connect(self.wine_resolver.deleteLater) self.wine_resolver.start() self.egl_path_info.setText(estimated_path) - egl_path = os.path.expanduser("~/") - if egl_path := shared.legendary_core.egl.programdata_path: - pass - elif egl_path := shared.legendary_core.lgd.config.get("default", "wine_prefix", fallback=""): - egl_data_path = os.path.join( - shared.legendary_core.lgd.config.get("default", "wine_prefix", fallback=""), - 'drive_c/ProgramData/Epic/EpicGamesLauncher/Data') - egl_path = os.path.join(egl_data_path, 'Manifests') - else: - possible_wine_prefixes = [os.path.expanduser("~/.wine"), - os.path.expanduser("~/Games/epic-games-store")] - for i in possible_wine_prefixes: - if os.path.exists(p := os.path.join(i, "drive_c/ProgramData/Epic/EpicGamesLauncher/Data/Manifests")): - egl_path = p + # if shared.core.egl.programdata_path: + + # if platform.system() != "Windows": + # shared.core.lgd.config.set('Legendary', 'egl_programdata', egl_path) + # shared.core.egl.programdata_path = egl_path + # + # egl_path = os.path.expanduser("~/") + # if egl_path := shared.core.egl.programdata_path: + # pass + # elif egl_path := shared.core.lgd.config.get("default", "wine_prefix", fallback=""): + # egl_data_path = os.path.join( + # shared.core.lgd.config.get("default", "wine_prefix", fallback=""), + # 'drive_c/ProgramData/Epic/EpicGamesLauncher/Data') + # egl_path = os.path.join(egl_data_path, 'Manifests') + # else: + # possible_wine_prefixes = [os.path.expanduser("~/.wine"), + # os.path.expanduser("~/Games/epic-games-store")] + # for i in possible_wine_prefixes: + # if os.path.exists(p := os.path.join(i, "drive_c/ProgramData/Epic/EpicGamesLauncher/Data/Manifests")): + # egl_path = p - self.egl_path_edit = PathEdit( - path=egl_path, - file_type=QFileDialog.DirectoryOnly, - edit_func=self.egl_path_edit_cb, - save_func=self.egl_path_save_cb, - parent=self - ) - self.egl_path_layout.addWidget(self.egl_path_edit) if platform.system() != "Windows": - shared.legendary_core.lgd.config.set('Legendary', 'egl_programdata', egl_path) - shared.legendary_core.egl.programdata_path = egl_path - - if shared.legendary_core.egl_sync_enabled: - self.refresh_button.setText(self.tr("Disable sync")) + egl_path = self.core.egl.programdata_path + if egl_path is None: + egl_path = str() + self.egl_path_edit = PathEdit( + path=egl_path, + file_type=QFileDialog.DirectoryOnly, + edit_func=self.egl_path_edit_cb, + save_func=self.egl_path_save_cb, + parent=self + ) + self.egl_path_edit.textChanged.connect(self.egl_path_changed) + self.egl_path_layout.addWidget(self.egl_path_edit) else: - self.refresh_button.setText(self.tr("Enable Sync")) - self.refresh_button.clicked.connect(self.sync) + self.egl_path_label.setVisible(False) - # self.enable_sync_button.clicked.connect(self.enable_sync) - # self.sync_once_button.clicked.connect(shared.lgd_core.egl_sync) + self.egl_sync_check.setChecked(shared.core.egl_sync_enabled) + self.egl_sync_check.stateChanged.connect(self.egl_sync_changed) self.export_list.itemDoubleClicked.connect( lambda item: @@ -97,73 +100,89 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): self.export_button.clicked.connect(self.export_selected) self.import_button.clicked.connect(self.import_selected) - def egl_path_edit_cb(self, path): + self.update_lists() + + @staticmethod + def egl_path_edit_cb(path) -> Tuple[bool, str]: + if not path: + return True, path if platform.system() != "Windows": if os.path.exists(os.path.join(path, "system.reg")) and os.path.exists(os.path.join(path, "dosdevices/c:")): # path is a wine prefix path = os.path.join(path, "dosdevices/c:", "ProgramData/Epic/EpicGamesLauncher/Data/Manifests") - if os.path.exists(path) and glob(f"{path}/*.item"): + if os.path.exists(path): return True, path return False, path def egl_path_save_cb(self, path): - shared.legendary_core.lgd.config.set("Legendary", "egl_programdata", path) - shared.legendary_core.egl.programdata_path = path - shared.legendary_core.lgd.save_config() - self.update_lists() + if not path: + # This is the same as "--unlink" + self.core.egl.programdata_path = None + self.core.lgd.config.remove_option('Legendary', 'egl_programdata') + self.core.lgd.config.remove_option('Legendary', 'egl_sync') + # remove EGL GUIDs from all games, DO NOT remove .egstore folders because that would fuck things up. + for igame in self.core.get_installed_list(): + igame.egl_guid = '' + self.core.install_game(igame) + else: + self.core.egl.programdata_path = path + self.core.lgd.config.set("Legendary", "egl_programdata", path) + self.core.lgd.save_config() - def update_export_list(self): - self.export_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) - self.export_select_all_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) - self.export_select_none_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) + def egl_path_changed(self, path): + if self.egl_path_edit.is_valid: + self.egl_sync_check.setEnabled(bool(path)) + self.egl_sync_check.setCheckState(Qt.Unchecked) + self.update_lists() - self.export_list.clear() - self.exportable_items.clear() - exportable_games = shared.legendary_core.egl_get_exportable() - for igame in exportable_games: - ew = EGLSyncItem(igame, True, self.export_list) - self.exportable_items.append(ew) - self.export_list.addItem(ew) - self.export_group.setEnabled(bool(exportable_games)) - self.export_button.setEnabled(bool(exportable_games)) - self.export_label.setVisible(not bool(exportable_games)) - - def update_import_list(self): - self.import_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) - self.import_select_all_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) - self.import_select_none_button.setDisabled(not bool(shared.legendary_core.egl.programdata_path)) - - self.import_list.clear() - self.importable_items.clear() - importable_games = shared.legendary_core.egl_get_importable() - for game in importable_games: - iw = EGLSyncItem(game, False, self.import_list) - self.importable_items.append(iw) - self.import_list.addItem(iw) - self.import_group.setEnabled(bool(importable_games)) - self.import_button.setEnabled(bool(importable_games)) - self.import_label.setVisible(not bool(importable_games)) + def egl_sync_changed(self, state): + if state == Qt.Unchecked: + self.core.lgd.config.remove_option('Legendary', 'egl_sync') + else: + self.core.lgd.config.set('Legendary', 'egl_sync', str(True)) + self.core.egl_sync() + self.update_lists() + self.core.lgd.save_config() def update_lists(self): - self.export_list.setVisible(bool(shared.legendary_core.egl.programdata_path)) - self.import_list.setVisible(bool(shared.legendary_core.egl.programdata_path)) - if not shared.legendary_core.egl.programdata_path: + self.export_list.setVisible(bool(shared.core.egl.programdata_path)) + self.import_list.setVisible(bool(shared.core.egl.programdata_path)) + if not shared.core.egl.programdata_path: return self.update_export_list() self.update_import_list() - def enable_sync(self): - if not shared.legendary_core.egl.programdata_path: - if os.path.exists(path := self.egl_path_edit.text()): - shared.legendary_core.lgd.config.set("Legendary", "egl_programdata", path) - shared.legendary_core.lgd.save_config() - shared.legendary_core.egl.programdata_path = path + def update_export_list(self): + self.export_button.setDisabled(not bool(shared.core.egl.programdata_path)) + self.export_select_all_button.setDisabled(not bool(shared.core.egl.programdata_path)) + self.export_select_none_button.setDisabled(not bool(shared.core.egl.programdata_path)) - shared.legendary_core.lgd.config.set('Legendary', 'egl_sync', "true") - shared.legendary_core.egl_sync() - shared.legendary_core.lgd.save_config() - self.refresh_button.setText(self.tr("Disable Sync")) - self.enable_sync_button.setDisabled(True) + self.export_list.clear() + self.exportable_items.clear() + exportable_games = shared.core.egl_get_exportable() + for igame in exportable_games: + ew = EGLSyncItem(igame, True, self.export_list) + self.exportable_items.append(ew) + self.export_list.addItem(ew) + for btn in self.export_buttons_layout.children(): + btn.setEnabled(bool(exportable_games)) + self.export_label.setVisible(not bool(exportable_games)) + + def update_import_list(self): + self.import_button.setDisabled(not bool(shared.core.egl.programdata_path)) + self.import_select_all_button.setDisabled(not bool(shared.core.egl.programdata_path)) + self.import_select_none_button.setDisabled(not bool(shared.core.egl.programdata_path)) + + self.import_list.clear() + self.importable_items.clear() + importable_games = shared.core.egl_get_importable() + for game in importable_games: + iw = EGLSyncItem(game, False, self.import_list) + self.importable_items.append(iw) + self.import_list.addItem(iw) + for btn in self.import_buttons_layout.children(): + btn.setEnabled(bool(importable_games)) + self.import_label.setVisible(not bool(importable_games)) @staticmethod def select_items(item_list, state): @@ -184,27 +203,6 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): self.import_list.takeItem(self.import_list.row(iw)) self.update_import_list() - def sync(self): - if shared.legendary_core.egl_sync_enabled: - # disable sync - info = DisableSyncDialog().get_information() - if info[0] == 0: - if info[1]: - shared.legendary_core.lgd.config.remove_option('Legendary', 'egl_sync') - else: - shared.legendary_core.lgd.config.remove_option('Legendary', 'egl_programdata') - shared.legendary_core.lgd.config.remove_option('Legendary', 'egl_sync') - # remove EGL GUIDs from all games, DO NOT remove .egstore folders because that would fuck things up. - for igame in shared.legendary_core.get_installed_list(): - igame.egl_guid = '' - shared.legendary_core.install_game(igame) - shared.legendary_core.lgd.save_config() - self.refresh_button.setText(self.tr("Enable Sync")) - else: - # enable sync - # self.enable_sync_button.setDisabled(False) - self.update_lists() - class EGLSyncItem(QListWidgetItem): def __init__(self, game, export: bool, parent=None): @@ -216,16 +214,16 @@ class EGLSyncItem(QListWidgetItem): if export: self.setText(game.title) else: - self.setText(shared.legendary_core.get_game(game.app_name).app_title) + self.setText(shared.core.get_game(game.app_name).app_title) def is_checked(self): return True if self.checkState() == Qt.Checked else False def export_game(self): - shared.legendary_core.egl_export(self.game.app_name) + shared.core.egl_export(self.game.app_name) def import_game(self): - shared.legendary_core.egl_import(self.game.app_name) + shared.core.egl_import(self.game.app_name) class DisableSyncDialog(QDialog): @@ -279,7 +277,7 @@ class EGLSyncItemWidget(QGroupBox): if export: self.app_title_label = QLabel(game.title) else: - title = shared.legendary_core.get_game(game.app_name).app_title + title = shared.core.get_game(game.app_name).app_title self.app_title_label = QLabel(title) self.layout.addWidget(self.app_title_label) self.button = QPushButton(self.tr("Export") if export else self.tr("Import")) @@ -293,13 +291,13 @@ class EGLSyncItemWidget(QGroupBox): self.setLayout(self.layout) def export_game(self): - shared.legendary_core.egl_export(self.game.app_name) + shared.core.egl_export(self.game.app_name) # FIXME: on update_egl_widget this is going to crash because # FIXME: the item is not removed from the list in the python's side self.deleteLater() def import_game(self): - shared.legendary_core.egl_import(self.game.app_name) + shared.core.egl_import(self.game.app_name) # FIXME: on update_egl_widget this is going to crash because # FIXME: the item is not removed from the list in the python's side self.deleteLater() diff --git a/rare/components/tabs/games/import_sync/import_widget.py b/rare/components/tabs/games/import_sync/import_widget.py index f23107c6..ef29d873 100644 --- a/rare/components/tabs/games/import_sync/import_widget.py +++ b/rare/components/tabs/games/import_sync/import_widget.py @@ -17,7 +17,7 @@ class ImportGroup(QGroupBox, Ui_ImportGroup): def __init__(self, parent=None): super(ImportGroup, self).__init__(parent=parent) self.setupUi(self) - self.core = shared.legendary_core + self.core = shared.core self.game_list = [i.app_name for i in self.core.get_game_list()] self.path_edit = PathEdit( @@ -95,7 +95,7 @@ class ImportGroup(QGroupBox, Ui_ImportGroup): self.core.get_installed_game(app_name).title)) self.app_name.setText("") - shared.signals.update_game_list.emit(app_name) + shared.signals.update_gamelist.emit(app_name) else: logger.warning("Failed to import" + app_name) self.info_label.setText(self.tr("Failed to import {}").format(app_name)) diff --git a/rare/components/tabs/tab_widget.py b/rare/components/tabs/tab_widget.py index 58728415..20095efc 100644 --- a/rare/components/tabs/tab_widget.py +++ b/rare/components/tabs/tab_widget.py @@ -66,7 +66,7 @@ class TabWidget(QTabWidget): # update dl tab text self.signals.update_download_tab_text.connect(self.update_dl_tab_text) # imported - shared.signals.update_game_list.connect(self.game_imported) + shared.signals.update_gamelist.connect(self.game_imported) if not shared.args.offline: # install dlc diff --git a/rare/ui/components/tabs/games/import_sync/egl_sync_widget.py b/rare/ui/components/tabs/games/import_sync/egl_sync_widget.py index ebac0679..846490d8 100644 --- a/rare/ui/components/tabs/games/import_sync/egl_sync_widget.py +++ b/rare/ui/components/tabs/games/import_sync/egl_sync_widget.py @@ -14,10 +14,28 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_EGLSyncGroup(object): def setupUi(self, EGLSyncGroup): EGLSyncGroup.setObjectName("EGLSyncGroup") - EGLSyncGroup.resize(680, 430) + EGLSyncGroup.resize(772, 368) + EGLSyncGroup.setCheckable(False) + EGLSyncGroup.setChecked(False) self.egl_sync_layout = QtWidgets.QFormLayout(EGLSyncGroup) self.egl_sync_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.egl_sync_layout.setObjectName("egl_sync_layout") + self.egl_path_label = QtWidgets.QLabel(EGLSyncGroup) + self.egl_path_label.setObjectName("egl_path_label") + self.egl_sync_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.egl_path_label) + self.egl_path_layout = QtWidgets.QVBoxLayout() + self.egl_path_layout.setObjectName("egl_path_layout") + self.egl_sync_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.egl_path_layout) + self.egl_path_info_label = QtWidgets.QLabel(EGLSyncGroup) + self.egl_path_info_label.setObjectName("egl_path_info_label") + self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.egl_path_info_label) + self.egl_path_info = QtWidgets.QLabel(EGLSyncGroup) + self.egl_path_info.setText("") + self.egl_path_info.setObjectName("egl_path_info") + self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.egl_path_info) + self.egl_sync_check = QtWidgets.QCheckBox(EGLSyncGroup) + self.egl_sync_check.setObjectName("egl_sync_check") + self.egl_sync_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.egl_sync_check) self.export_import_layout = QtWidgets.QHBoxLayout() self.export_import_layout.setObjectName("export_import_layout") self.export_group = QtWidgets.QGroupBox(EGLSyncGroup) @@ -72,30 +90,7 @@ class Ui_EGLSyncGroup(object): self.import_buttons_layout.addWidget(self.import_button) self.import_layout.addLayout(self.import_buttons_layout) self.export_import_layout.addWidget(self.import_group) - self.egl_sync_layout.setLayout(4, QtWidgets.QFormLayout.SpanningRole, self.export_import_layout) - self.egl_path_layout = QtWidgets.QVBoxLayout() - self.egl_path_layout.setObjectName("egl_path_layout") - self.egl_sync_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.egl_path_layout) - self.egl_path_label = QtWidgets.QLabel(EGLSyncGroup) - self.egl_path_label.setObjectName("egl_path_label") - self.egl_sync_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.egl_path_label) - self.egl_path_info_label = QtWidgets.QLabel(EGLSyncGroup) - self.egl_path_info_label.setObjectName("egl_path_info_label") - self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.egl_path_info_label) - self.egl_path_info = QtWidgets.QLabel(EGLSyncGroup) - self.egl_path_info.setText("") - self.egl_path_info.setObjectName("egl_path_info") - self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.egl_path_info) - self.egl_auto_check = QtWidgets.QCheckBox(EGLSyncGroup) - self.egl_auto_check.setText("") - self.egl_auto_check.setObjectName("egl_auto_check") - self.egl_sync_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.egl_auto_check) - self.refresh_button = QtWidgets.QPushButton(EGLSyncGroup) - self.refresh_button.setObjectName("refresh_button") - self.egl_sync_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.refresh_button) - self.egl_auto_check_label = QtWidgets.QLabel(EGLSyncGroup) - self.egl_auto_check_label.setObjectName("egl_auto_check_label") - self.egl_sync_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.egl_auto_check_label) + self.egl_sync_layout.setLayout(3, QtWidgets.QFormLayout.SpanningRole, self.export_import_layout) self.retranslateUi(EGLSyncGroup) QtCore.QMetaObject.connectSlotsByName(EGLSyncGroup) @@ -104,6 +99,9 @@ class Ui_EGLSyncGroup(object): _translate = QtCore.QCoreApplication.translate EGLSyncGroup.setWindowTitle(_translate("EGLSyncGroup", "EGLSyncGroup")) EGLSyncGroup.setTitle(_translate("EGLSyncGroup", "Sync with Epic Games Launcher")) + self.egl_path_label.setText(_translate("EGLSyncGroup", "Prefix/Manifest path")) + self.egl_path_info_label.setText(_translate("EGLSyncGroup", "Estimated path")) + self.egl_sync_check.setText(_translate("EGLSyncGroup", "Enable automatic sync")) self.export_group.setTitle(_translate("EGLSyncGroup", "Exportable games")) self.export_label.setText(_translate("EGLSyncGroup", "No games to export to EGS")) self.export_list.setSortingEnabled(True) @@ -116,10 +114,6 @@ class Ui_EGLSyncGroup(object): self.import_select_all_button.setText(_translate("EGLSyncGroup", "Select all")) self.import_select_none_button.setText(_translate("EGLSyncGroup", "Select none")) self.import_button.setText(_translate("EGLSyncGroup", "Import")) - self.egl_path_label.setText(_translate("EGLSyncGroup", "Manifest path")) - self.egl_path_info_label.setText(_translate("EGLSyncGroup", "Estimated path")) - self.refresh_button.setText(_translate("EGLSyncGroup", "Refresh")) - self.egl_auto_check_label.setText(_translate("EGLSyncGroup", "Enable automatic sync")) if __name__ == "__main__": diff --git a/rare/ui/components/tabs/games/import_sync/egl_sync_widget.ui b/rare/ui/components/tabs/games/import_sync/egl_sync_widget.ui index 9940bda5..a27c4981 100644 --- a/rare/ui/components/tabs/games/import_sync/egl_sync_widget.ui +++ b/rare/ui/components/tabs/games/import_sync/egl_sync_widget.ui @@ -6,8 +6,8 @@ 0 0 - 680 - 430 + 772 + 368 @@ -16,11 +16,48 @@ Sync with Epic Games Launcher + + false + + + false + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + Prefix/Manifest path + + + + + + + + + + Estimated path + + + + + + + + + + + + + + Enable automatic sync + + + + @@ -152,51 +189,6 @@ - - - - - - - Manifest path - - - - - - - Estimated path - - - - - - - - - - - - - - - - - - - - - Refresh - - - - - - - Enable automatic sync - - -