2023-02-05 04:43:52 +13:00
|
|
|
import os
|
2023-03-11 23:51:55 +13:00
|
|
|
import platform
|
2023-02-05 04:43:52 +13:00
|
|
|
from logging import getLogger
|
2023-03-11 23:51:55 +13:00
|
|
|
from typing import Tuple
|
2023-02-05 04:43:52 +13:00
|
|
|
|
|
|
|
from PyQt5.QtCore import QThreadPool, QSettings
|
|
|
|
from PyQt5.QtWidgets import (
|
|
|
|
QWidget,
|
|
|
|
QFileDialog,
|
|
|
|
QLabel,
|
|
|
|
QPushButton,
|
|
|
|
QSizePolicy,
|
|
|
|
QMessageBox,
|
|
|
|
QGroupBox,
|
|
|
|
QVBoxLayout,
|
2024-01-03 04:38:51 +13:00
|
|
|
QSpacerItem, QFormLayout,
|
2023-02-05 04:43:52 +13:00
|
|
|
)
|
2023-02-06 11:28:42 +13:00
|
|
|
from legendary.models.game import SaveGameStatus
|
2023-02-05 04:43:52 +13:00
|
|
|
|
|
|
|
from rare.models.game import RareGame
|
2023-03-11 23:51:55 +13:00
|
|
|
from rare.shared import RareCore
|
2023-02-05 04:43:52 +13:00
|
|
|
from rare.shared.workers.wine_resolver import WineResolver
|
2024-01-03 04:38:51 +13:00
|
|
|
from rare.ui.components.tabs.games.game_info.cloud_settings_widget import Ui_CloudSettingsWidget
|
|
|
|
from rare.ui.components.tabs.games.game_info.cloud_sync_widget import Ui_CloudSyncWidget
|
2023-02-05 04:43:52 +13:00
|
|
|
from rare.utils.misc import icon
|
|
|
|
from rare.widgets.indicator_edit import PathEdit, IndicatorReasonsCommon
|
2023-04-03 06:00:09 +12:00
|
|
|
from rare.widgets.loading_widget import LoadingWidget
|
2023-02-05 04:43:52 +13:00
|
|
|
from rare.widgets.side_tab import SideTabContents
|
|
|
|
|
|
|
|
logger = getLogger("CloudWidget")
|
|
|
|
|
|
|
|
|
|
|
|
class CloudSaves(QWidget, SideTabContents):
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
super(CloudSaves, self).__init__(parent=parent)
|
|
|
|
|
|
|
|
self.sync_widget = QWidget(self)
|
2024-01-03 04:38:51 +13:00
|
|
|
self.sync_ui = Ui_CloudSyncWidget()
|
2023-02-05 04:43:52 +13:00
|
|
|
self.sync_ui.setupUi(self.sync_widget)
|
|
|
|
|
|
|
|
self.info_label = QLabel(self.tr("<b>This game doesn't support cloud saves</b>"))
|
|
|
|
|
2023-03-11 23:51:55 +13:00
|
|
|
self.rcore = RareCore.instance()
|
|
|
|
self.core = RareCore.instance().core()
|
2023-02-05 04:43:52 +13:00
|
|
|
self.settings = QSettings()
|
|
|
|
|
|
|
|
self.sync_ui.icon_local.setPixmap(icon("mdi.harddisk", "fa.desktop").pixmap(128, 128))
|
|
|
|
self.sync_ui.icon_remote.setPixmap(icon("mdi.cloud-outline", "ei.cloud").pixmap(128, 128))
|
|
|
|
|
|
|
|
self.sync_ui.upload_button.clicked.connect(self.upload)
|
|
|
|
self.sync_ui.download_button.clicked.connect(self.download)
|
2023-04-03 06:00:09 +12:00
|
|
|
|
2023-04-05 10:00:01 +12:00
|
|
|
self.loading_widget = LoadingWidget(parent=self.sync_widget)
|
2023-04-03 06:00:09 +12:00
|
|
|
self.loading_widget.setVisible(False)
|
|
|
|
|
2023-02-05 04:43:52 +13:00
|
|
|
self.rgame: RareGame = None
|
|
|
|
|
|
|
|
self.cloud_widget = QGroupBox(self)
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui = Ui_CloudSettingsWidget()
|
2023-02-05 04:43:52 +13:00
|
|
|
self.cloud_ui.setupUi(self.cloud_widget)
|
|
|
|
|
|
|
|
self.cloud_save_path_edit = PathEdit(
|
|
|
|
"",
|
2023-03-16 09:49:18 +13:00
|
|
|
file_mode=QFileDialog.DirectoryOnly,
|
2023-03-11 13:05:41 +13:00
|
|
|
placeholder=self.tr('Use "Calculate path" or "Browse" ...'),
|
2023-03-11 23:51:55 +13:00
|
|
|
edit_func=self.edit_save_path,
|
2023-02-05 04:43:52 +13:00
|
|
|
save_func=self.save_save_path,
|
|
|
|
)
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui.main_layout.setWidget(
|
|
|
|
self.cloud_ui.main_layout.getWidgetPosition(self.cloud_ui.path_label)[0],
|
|
|
|
QFormLayout.FieldRole,
|
|
|
|
self.cloud_save_path_edit
|
|
|
|
)
|
2023-02-05 04:43:52 +13:00
|
|
|
|
2023-03-11 13:05:41 +13:00
|
|
|
self.compute_save_path_button = QPushButton(icon("fa.magic"), self.tr("Calculate path"))
|
2023-02-05 04:43:52 +13:00
|
|
|
self.compute_save_path_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
|
|
|
|
self.compute_save_path_button.clicked.connect(self.compute_save_path)
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui.main_layout.addRow(None, self.compute_save_path_button)
|
2023-02-05 04:43:52 +13:00
|
|
|
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui.sync_check.stateChanged.connect(
|
2023-02-05 04:43:52 +13:00
|
|
|
lambda: self.settings.setValue(
|
2024-01-03 04:38:51 +13:00
|
|
|
f"{self.rgame.app_name}/auto_sync_cloud", self.cloud_ui.sync_check.isChecked()
|
2023-02-05 04:43:52 +13:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
layout.addWidget(self.sync_widget)
|
|
|
|
layout.addWidget(self.cloud_widget)
|
|
|
|
layout.addWidget(self.info_label)
|
|
|
|
layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding))
|
|
|
|
|
2023-03-11 23:51:55 +13:00
|
|
|
def edit_save_path(self, text: str) -> Tuple[bool, str, int]:
|
|
|
|
if platform.system() != "Windows":
|
|
|
|
if os.path.exists(text):
|
|
|
|
return True, text, IndicatorReasonsCommon.VALID
|
|
|
|
else:
|
|
|
|
return False, text, IndicatorReasonsCommon.DIR_NOT_EXISTS
|
|
|
|
return True, text, IndicatorReasonsCommon.VALID
|
|
|
|
|
|
|
|
def save_save_path(self, text: str):
|
2023-03-16 23:38:33 +13:00
|
|
|
if text != self.rgame.save_path:
|
|
|
|
self.rgame.save_path = text
|
2023-03-11 23:51:55 +13:00
|
|
|
|
2023-02-05 04:43:52 +13:00
|
|
|
def upload(self):
|
2023-04-03 06:00:09 +12:00
|
|
|
self.sync_widget.setEnabled(False)
|
|
|
|
self.loading_widget.setVisible(True)
|
2023-02-06 05:28:02 +13:00
|
|
|
self.rgame.upload_saves()
|
2023-02-05 04:43:52 +13:00
|
|
|
|
|
|
|
def download(self):
|
2023-04-03 06:00:09 +12:00
|
|
|
self.sync_widget.setEnabled(False)
|
|
|
|
self.loading_widget.setVisible(True)
|
2023-02-06 05:28:02 +13:00
|
|
|
self.rgame.download_saves()
|
2023-02-05 04:43:52 +13:00
|
|
|
|
|
|
|
def compute_save_path(self):
|
|
|
|
if self.rgame.is_installed and self.rgame.game.supports_cloud_saves:
|
|
|
|
try:
|
|
|
|
new_path = self.core.get_save_path(self.rgame.app_name)
|
2023-03-11 23:51:55 +13:00
|
|
|
if platform.system() != "Windows" and not os.path.exists(new_path):
|
2023-02-05 04:43:52 +13:00
|
|
|
raise ValueError(f'Path "{new_path}" does not exist.')
|
|
|
|
except Exception as e:
|
|
|
|
logger.warning(str(e))
|
|
|
|
resolver = WineResolver(self.core, self.rgame.raw_save_path, self.rgame.app_name)
|
|
|
|
if not resolver.wine_env.get("WINEPREFIX"):
|
2023-12-06 11:09:44 +13:00
|
|
|
del resolver
|
2023-02-05 04:43:52 +13:00
|
|
|
self.cloud_save_path_edit.setText("")
|
|
|
|
QMessageBox.warning(self, "Warning", "No wine prefix selected. Please set it in settings")
|
|
|
|
return
|
2023-03-11 23:51:55 +13:00
|
|
|
self.cloud_save_path_edit.setText(self.tr("Loading..."))
|
2023-02-05 04:43:52 +13:00
|
|
|
self.cloud_save_path_edit.setDisabled(True)
|
|
|
|
self.compute_save_path_button.setDisabled(True)
|
|
|
|
|
2023-03-11 23:51:55 +13:00
|
|
|
app_name = self.rgame.app_name
|
2023-02-05 04:43:52 +13:00
|
|
|
resolver.signals.result_ready.connect(lambda x: self.wine_resolver_finished(x, app_name))
|
|
|
|
QThreadPool.globalInstance().start(resolver)
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
self.cloud_save_path_edit.setText(new_path)
|
|
|
|
|
|
|
|
def wine_resolver_finished(self, path, app_name):
|
|
|
|
logger.info(f"Wine resolver finished for {app_name}. Computed save path: {path}")
|
|
|
|
if app_name == self.rgame.app_name:
|
|
|
|
self.cloud_save_path_edit.setDisabled(False)
|
|
|
|
self.compute_save_path_button.setDisabled(False)
|
|
|
|
if path and not os.path.exists(path):
|
|
|
|
try:
|
2024-01-03 05:03:41 +13:00
|
|
|
os.makedirs(path, exist_ok=True)
|
2023-02-05 04:43:52 +13:00
|
|
|
except PermissionError:
|
|
|
|
self.cloud_save_path_edit.setText("")
|
|
|
|
QMessageBox.warning(
|
2023-03-13 11:59:24 +13:00
|
|
|
self,
|
2023-12-15 10:08:08 +13:00
|
|
|
self.tr("Error - {}").format(self.rgame.app_title),
|
2023-03-13 11:59:24 +13:00
|
|
|
self.tr(
|
2023-12-06 11:09:44 +13:00
|
|
|
"Error while calculating path for <b>{}</b>. Insufficient permissions to create <b>{}</b>"
|
2023-12-15 10:08:08 +13:00
|
|
|
).format(self.rgame.app_title, path),
|
2023-02-05 04:43:52 +13:00
|
|
|
)
|
|
|
|
return
|
|
|
|
if not path:
|
|
|
|
self.cloud_save_path_edit.setText("")
|
|
|
|
return
|
|
|
|
self.cloud_save_path_edit.setText(path)
|
|
|
|
elif path:
|
2023-03-11 23:51:55 +13:00
|
|
|
self.rcore.get_game(app_name).save_path = path
|
2023-02-05 04:43:52 +13:00
|
|
|
|
2023-02-09 09:44:42 +13:00
|
|
|
def __update_widget(self):
|
|
|
|
supports_saves = self.rgame.igame is not None and (
|
|
|
|
self.rgame.game.supports_cloud_saves or self.rgame.game.supports_mac_cloud_saves
|
|
|
|
)
|
2023-04-03 06:00:09 +12:00
|
|
|
|
|
|
|
self.sync_widget.setEnabled(
|
|
|
|
bool(supports_saves and self.rgame.save_path)) # and not self.rgame.is_save_up_to_date))
|
|
|
|
|
2023-02-05 04:43:52 +13:00
|
|
|
self.cloud_widget.setEnabled(supports_saves)
|
|
|
|
self.info_label.setVisible(not supports_saves)
|
|
|
|
if not supports_saves:
|
2023-03-11 13:05:41 +13:00
|
|
|
self.sync_ui.date_info_local.setText("None")
|
2023-03-13 01:15:43 +13:00
|
|
|
self.sync_ui.age_label_local.setText("None")
|
2023-03-11 13:05:41 +13:00
|
|
|
self.sync_ui.date_info_remote.setText("None")
|
2023-03-13 01:15:43 +13:00
|
|
|
self.sync_ui.age_label_remote.setText("None")
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui.sync_check.setChecked(False)
|
2023-03-11 13:05:41 +13:00
|
|
|
self.cloud_save_path_edit.setText("")
|
2023-02-05 04:43:52 +13:00
|
|
|
return
|
2023-02-08 09:21:29 +13:00
|
|
|
|
2023-02-09 09:44:42 +13:00
|
|
|
status, (dt_local, dt_remote) = self.rgame.save_game_state
|
2023-02-06 11:28:42 +13:00
|
|
|
|
2023-03-13 11:59:24 +13:00
|
|
|
self.sync_ui.date_info_local.setText(
|
|
|
|
dt_local.strftime("%A, %d. %B %Y %X") if dt_local and self.rgame.save_path else "None"
|
|
|
|
)
|
|
|
|
self.sync_ui.date_info_remote.setText(
|
|
|
|
dt_remote.strftime("%A, %d. %B %Y %X") if dt_remote and self.rgame.save_path else "None"
|
|
|
|
)
|
|
|
|
|
2023-03-11 13:05:41 +13:00
|
|
|
newer = self.tr("Newer")
|
|
|
|
self.sync_ui.age_label_local.setText(
|
|
|
|
f"<b>{newer}</b>" if status == SaveGameStatus.LOCAL_NEWER else " "
|
|
|
|
)
|
|
|
|
self.sync_ui.age_label_remote.setText(
|
|
|
|
f"<b>{newer}</b>" if status == SaveGameStatus.REMOTE_NEWER else " "
|
|
|
|
)
|
2023-02-05 04:43:52 +13:00
|
|
|
|
2023-03-13 11:59:24 +13:00
|
|
|
button_disabled = self.rgame.state in [RareGame.State.RUNNING, RareGame.State.SYNCING]
|
2023-04-03 06:00:09 +12:00
|
|
|
self.sync_widget.setDisabled(button_disabled)
|
2023-04-05 10:00:01 +12:00
|
|
|
if self.rgame.state == RareGame.State.SYNCING:
|
|
|
|
self.loading_widget.start()
|
|
|
|
else:
|
|
|
|
self.loading_widget.stop()
|
2023-04-03 06:00:09 +12:00
|
|
|
|
|
|
|
self.sync_ui.upload_button.setDisabled(not dt_local)
|
|
|
|
self.sync_ui.download_button.setDisabled(not dt_remote)
|
2023-03-13 11:59:24 +13:00
|
|
|
|
2024-01-03 04:38:51 +13:00
|
|
|
self.cloud_ui.sync_check.blockSignals(True)
|
|
|
|
self.cloud_ui.sync_check.setChecked(self.rgame.auto_sync_saves)
|
|
|
|
self.cloud_ui.sync_check.blockSignals(False)
|
2023-12-06 11:09:44 +13:00
|
|
|
|
2023-03-13 11:59:24 +13:00
|
|
|
self.cloud_save_path_edit.setText(self.rgame.save_path if self.rgame.save_path else "")
|
2023-12-06 11:09:44 +13:00
|
|
|
if platform.system() == "Windows" and not self.rgame.save_path:
|
|
|
|
self.compute_save_path()
|
2023-02-05 04:43:52 +13:00
|
|
|
|
2023-02-09 09:44:42 +13:00
|
|
|
def update_game(self, rgame: RareGame):
|
|
|
|
if self.rgame:
|
|
|
|
self.rgame.signals.widget.update.disconnect(self.__update_widget)
|
|
|
|
|
|
|
|
self.rgame = rgame
|
|
|
|
|
2023-12-15 10:08:08 +13:00
|
|
|
self.set_title.emit(rgame.app_title)
|
2023-02-09 09:44:42 +13:00
|
|
|
rgame.signals.widget.update.connect(self.__update_widget)
|
|
|
|
|
|
|
|
self.__update_widget()
|