1
0
Fork 0
mirror of synced 2024-07-03 05:31:23 +12:00
Rare/rare/components/tabs/games/cloud_save_utils.py

331 lines
11 KiB
Python
Raw Normal View History

2021-10-24 12:47:49 +13:00
import datetime
import sys
2021-10-24 12:47:49 +13:00
from dataclasses import dataclass
from logging import getLogger
from typing import Union, List
2021-10-24 12:47:49 +13:00
from PyQt5.QtCore import QObject, pyqtSignal, QRunnable, QThreadPool, Qt, QSettings
from PyQt5.QtWidgets import QDialog, QMessageBox, QSizePolicy, QLayout, QApplication
2021-10-24 12:47:49 +13:00
from legendary.core import LegendaryCore
2021-10-24 12:47:49 +13:00
from legendary.models.game import SaveGameStatus, InstalledGame, SaveGameFile
from rare import shared
from rare.ui.components.dialogs.sync_save_dialog import Ui_SyncSaveDialog
from rare.utils.utils import icon
2021-10-24 12:47:49 +13:00
logger = getLogger("Cloud Saves")
@dataclass
class UploadModel:
app_name: str
date_time: datetime.datetime
path: str
@dataclass
class DownloadModel:
app_name: str
latest_save: SaveGameFile
path: str
class WorkerSignals(QObject):
finished = pyqtSignal(str, str)
class SaveWorker(QRunnable):
signals = WorkerSignals()
def __init__(self, model: Union[UploadModel, DownloadModel]):
super(SaveWorker, self).__init__()
self.model = model
self.setAutoDelete(True)
def run(self) -> None:
try:
if isinstance(self.model, DownloadModel):
2021-12-24 22:09:50 +13:00
shared.core.download_saves(
self.model.app_name,
self.model.latest_save.manifest_name,
self.model.path,
)
2021-10-24 12:47:49 +13:00
else:
2021-12-24 22:09:50 +13:00
shared.core.upload_save(
self.model.app_name, self.model.path, self.model.date_time
)
2021-10-24 12:47:49 +13:00
except Exception as e:
self.signals.finished.emit(str(e), self.model.app_name)
logger.error(str(e))
return
try:
if isinstance(self.model, UploadModel):
logger.info("Updating cloud saves...")
result = shared.core.get_save_games(self.model.app_name)
shared.api_results.saves = result
except Exception as e:
self.signals.finished.emit(str(e), self.model.app_name)
logger.error(str(e))
return
2021-10-24 12:47:49 +13:00
self.signals.finished.emit("", self.model.app_name)
class CloudSaveDialog(QDialog, Ui_SyncSaveDialog):
DOWNLOAD = 2
UPLOAD = 1
CANCEL = 0
2021-12-24 22:09:50 +13:00
def __init__(
self,
igame: InstalledGame,
dt_local: datetime.datetime,
dt_remote: datetime.datetime,
newer: str,
2021-12-24 22:09:50 +13:00
):
2021-10-24 12:47:49 +13:00
super(CloudSaveDialog, self).__init__()
self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
self.status = self.CANCEL
self.title_label.setText(self.title_label.text() + igame.title)
self.date_info_local.setText(dt_local.strftime("%A, %d. %B %Y %X"))
self.date_info_remote.setText(dt_remote.strftime("%A, %d. %B %Y %X"))
2021-10-24 12:47:49 +13:00
new_text = self.tr(" (newer)")
if newer == "remote":
self.cloud_gb.setTitle(self.cloud_gb.title() + new_text)
elif newer == "local":
self.local_gb.setTitle(self.local_gb.title() + new_text)
self.icon_local.setPixmap(icon("mdi.harddisk", "fa.desktop").pixmap(128, 128))
self.icon_remote.setPixmap(icon("mdi.cloud-outline", "ei.cloud").pixmap(128, 128))
2021-10-24 12:47:49 +13:00
self.upload_button.clicked.connect(lambda: self.btn_clicked(self.UPLOAD))
self.download_button.clicked.connect(lambda: self.btn_clicked(self.DOWNLOAD))
self.cancel_button.clicked.connect(self.close)
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.layout().setSizeConstraint(QLayout.SetFixedSize)
def get_action(self):
self.exec_()
return self.status
def btn_clicked(self, status):
self.status = status
self.close()
class CloudSaveUtils(QObject):
sync_finished = pyqtSignal(str)
2021-10-24 12:47:49 +13:00
def __init__(self):
super(CloudSaveUtils, self).__init__()
self.core = shared.core
saves = shared.api_results.saves
if not shared.args.offline:
self.latest_saves = self.get_latest_saves(saves)
else:
self.latest_saves = dict()
self.settings = QSettings()
self.thread_pool = QThreadPool.globalInstance()
def get_latest_saves(self, saves: List[SaveGameFile]) -> dict:
2021-10-24 12:47:49 +13:00
save_games = set()
for igame in self.core.get_installed_list():
game = self.core.get_game(igame.app_name)
if self.core.is_installed(igame.app_name) and game.supports_cloud_saves:
save_games.add(igame.app_name)
latest_saves = dict()
2021-10-24 12:47:49 +13:00
for s in sorted(saves, key=lambda a: a.datetime):
if s.app_name in save_games:
latest_saves[s.app_name] = s
return latest_saves
2021-10-24 12:47:49 +13:00
2021-11-17 10:54:23 +13:00
def sync_before_launch_game(self, app_name, ignore_settings=False) -> int:
if not ignore_settings:
default = self.settings.value("auto_sync_cloud", True, bool)
if not self.settings.value(f"{app_name}/auto_sync_cloud", default, bool):
return False
2021-10-24 12:47:49 +13:00
igame = self.core.get_installed_game(app_name)
2021-11-17 10:54:23 +13:00
if not igame.save_path:
try:
savepath = self.core.get_save_path(app_name)
except Exception as e:
logger.error(e)
savepath = ""
if savepath:
igame.save_path = savepath
self.core.lgd.set_installed_game(app_name, igame)
logger.info(f"Set save path of {igame.title} to {savepath}")
elif not ignore_settings: # sync on startup
2021-12-24 22:09:50 +13:00
if (
QMessageBox.question(
None,
"Warning",
self.tr(
"Could not compute cloud save path. Please set it in Game settings manually. \nDo you want to launch {} anyway?"
).format(igame.title),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
== QMessageBox.Yes
2021-12-24 22:09:50 +13:00
):
2021-11-17 10:54:23 +13:00
return False
else:
raise ValueError("No savepath detected")
else:
2021-12-24 22:09:50 +13:00
QMessageBox.warning(
None,
"Warning",
self.tr(
"No savepath found. Please set it in Game Settings manually"
),
)
2021-11-17 10:54:23 +13:00
return False
2021-12-24 22:09:50 +13:00
res, (dt_local, dt_remote) = self.core.check_savegame_state(
igame.save_path, self.latest_saves.get(app_name)
)
2021-10-24 12:47:49 +13:00
2021-11-17 10:54:23 +13:00
if res == SaveGameStatus.NO_SAVE:
return False
2021-10-24 12:47:49 +13:00
if res != SaveGameStatus.SAME_AGE:
2021-11-17 10:54:23 +13:00
newer = ""
2021-10-24 12:47:49 +13:00
if res == SaveGameStatus.REMOTE_NEWER:
newer = "remote"
elif res == SaveGameStatus.LOCAL_NEWER:
newer = "local"
if res == SaveGameStatus.REMOTE_NEWER and not dt_local:
self.download_saves(igame)
return True
elif res == SaveGameStatus.LOCAL_NEWER and not dt_remote:
self.upload_saves(igame, dt_local)
return True
result = CloudSaveDialog(igame, dt_local, dt_remote, newer).get_action()
if result == CloudSaveDialog.UPLOAD:
self.upload_saves(igame, dt_local)
elif result == CloudSaveDialog.DOWNLOAD:
self.download_saves(igame)
elif result == CloudSaveDialog.CANCEL:
2021-11-17 10:54:23 +13:00
raise AssertionError()
2021-10-24 12:47:49 +13:00
return True
return False
2021-11-17 10:54:23 +13:00
def game_finished(self, app_name, ignore_settings=False, always_ask: bool = False):
if not ignore_settings:
default = self.settings.value("auto_sync_cloud", True, bool)
if not self.settings.value(f"{app_name}/auto_sync_cloud", default, bool):
self.sync_finished.emit(app_name)
return
2021-10-24 12:47:49 +13:00
igame = self.core.get_installed_game(app_name)
2021-11-17 10:54:23 +13:00
if not igame.save_path:
try:
savepath = self.core.get_save_path(app_name)
except Exception as e:
logger.error(e)
savepath = ""
if savepath:
igame.save_path = savepath
self.core.lgd.set_installed_game(app_name, igame)
logger.info(f"Set save path of {igame.title} to {savepath}")
else:
2021-12-24 22:09:50 +13:00
QMessageBox.warning(
None, "Warning", self.tr("No savepath set. Skip syncing with cloud")
)
2021-11-17 10:54:23 +13:00
return False
2021-12-24 22:09:50 +13:00
res, (dt_local, dt_remote) = self.core.check_savegame_state(
igame.save_path, self.latest_saves.get(app_name)
)
2021-10-24 12:47:49 +13:00
2021-11-17 10:54:23 +13:00
if res == SaveGameStatus.LOCAL_NEWER and not always_ask:
2021-10-24 12:47:49 +13:00
self.upload_saves(igame, dt_local)
return
2021-11-17 10:54:23 +13:00
elif res == SaveGameStatus.NO_SAVE and not always_ask:
2021-12-24 22:09:50 +13:00
QMessageBox.warning(
None,
"No saves",
self.tr(
"There are no saves local and online. Maybe you have to change save path of {}"
).format(igame.title),
)
self.sync_finished.emit(app_name)
2021-10-24 12:47:49 +13:00
return
2021-11-17 10:54:23 +13:00
elif res == SaveGameStatus.SAME_AGE and not always_ask:
self.sync_finished.emit(app_name)
2021-10-24 12:47:49 +13:00
return
# Remote newer
2021-11-17 10:54:23 +13:00
newer = ""
2021-10-24 12:47:49 +13:00
if res == SaveGameStatus.REMOTE_NEWER:
newer = "remote"
2021-11-17 10:54:23 +13:00
elif res == SaveGameStatus.LOCAL_NEWER:
newer = "local"
2021-10-24 12:47:49 +13:00
result = CloudSaveDialog(igame, dt_local, dt_remote, newer).get_action()
if result == CloudSaveDialog.UPLOAD:
self.upload_saves(igame, dt_local)
elif result == CloudSaveDialog.DOWNLOAD:
self.download_saves(igame)
def upload_saves(self, igame: InstalledGame, dt_local):
logger.info("Uploading saves for " + igame.title)
w = SaveWorker(UploadModel(igame.app_name, dt_local, igame.save_path))
w.signals.finished.connect(self.worker_finished)
self.thread_pool.start(w)
def download_saves(self, igame):
logger.info("Downloading saves for " + igame.title)
2021-12-24 22:09:50 +13:00
w = SaveWorker(
DownloadModel(
igame.app_name, self.latest_saves.get(igame.app_name), igame.save_path
)
)
2021-10-24 12:47:49 +13:00
w.signals.finished.connect(self.worker_finished)
self.thread_pool.start(w)
def worker_finished(self, error_message: str, app_name: str):
if not error_message:
self.sync_finished.emit(app_name)
self.latest_saves = self.get_latest_saves(shared.api_results.saves)
2021-10-24 12:47:49 +13:00
else:
2021-12-24 22:09:50 +13:00
QMessageBox.warning(
None,
"Warning",
self.tr("Syncing with cloud failed: \n ") + error_message,
)
self.sync_finished.emit(app_name)
def test_dialog():
app = QApplication(sys.argv)
core = LegendaryCore()
dlg = CloudSaveDialog(core.get_installed_list()[0], datetime.datetime.now(),
datetime.datetime.strptime("2021,1", "%Y,%M"), "local")
dlg.show()
app.exec_()
if __name__ == '__main__':
test_dialog()