1
0
Fork 0
mirror of synced 2024-06-16 17:44:44 +12:00

Sync Saves Update

This commit is contained in:
Dummerle 2021-01-14 13:01:03 +01:00
parent 145c86938d
commit 88e8022b9e
8 changed files with 328 additions and 162 deletions

View file

@ -43,6 +43,7 @@ Using the exe-file could cause an error with the stylesheets
- Translations (English and German)
- Design (Pretty Stylesheets, Icons, Icon view for installed Games)
- Download Progressbar
- Offline mode
## Images

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -9,7 +9,7 @@ from legendary.core import LegendaryCore
from Rare.Tabs.GamesInstalled.GameWidget import GameWidget
from Rare.utils import legendaryUtils
from Rare.utils.Dialogs.SyncSavesDialog import SyncSavesDialog
from Rare.utils.Dialogs.SyncSaves.SyncSavesDialog import SyncSavesDialog
logger = getLogger("InstalledList")

View file

@ -0,0 +1,48 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QApplication
from Rare.ext.QtExtensions import CustomQLabel, PathEdit
class DLG(QDialog):
def __init__(self):
super(DLG, self).__init__()
print("lol")
class PathInputDialog(QDialog):
def __init__(self, title_text, text):
super().__init__()
self.path = ""
self.setWindowTitle(title_text)
self.info_label = CustomQLabel(text)
self.info_label.setWordWrap(True)
self.input = PathEdit("Select directory", QFileDialog.DirectoryOnly, )
self.layout = QVBoxLayout()
self.layout.addWidget(self.info_label)
self.layout.addWidget(self.input)
self.child_layout = QHBoxLayout()
self.ok_button = QPushButton("Ok")
self.ok_button.clicked.connect(self.ok)
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(self.cancel)
self.child_layout.addStretch()
self.child_layout.addWidget(self.ok_button)
self.child_layout.addWidget(self.cancel_button)
self.layout.addLayout(self.child_layout)
self.setLayout(self.layout)
def get_path(self):
self.exec_()
return self.path
def cancel(self):
self.path = ""
self.close()
def ok(self):
self.path = self.input.text()
self.close()

View file

@ -0,0 +1,111 @@
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, QObjectCleanupHandler
from PyQt5.QtWidgets import *
from legendary.core import LegendaryCore
from legendary.models.game import SaveGameStatus
from Rare.ext.QtExtensions import CustomQLabel, WaitingSpinner
from Rare.utils.Dialogs.PathInputDialog import PathInputDialog
from Rare.utils.Dialogs.SyncSaves.SyncWidget import SyncWidget
logger = getLogger("Sync Saves")
class UploadThread(QThread):
signal = pyqtSignal(str)
def __init__(self, args):
super(UploadThread, self).__init__()
self.args = args
def run(self):
logger.info("Uplouding saves")
res, dt_remote, dt_local, save_path, latest_save = self.status[app_name]
self.core.upload_save(app_name, save_path, dt_local)
class LoadThread(QThread):
signal = pyqtSignal(list)
def __init__(self, core: LegendaryCore):
super(LoadThread, self).__init__()
self.core = core
def run(self) -> None:
saves = self.core.get_save_games()
self.signal.emit(saves)
class SyncGame(QWidget):
def __init__(self):
super(SyncGame, self).__init__()
# noinspection PyAttributeOutsideInit
class SyncSavesDialog(QDialog):
def __init__(self, core: LegendaryCore):
super(SyncSavesDialog, self).__init__()
self.core = core
layout = QVBoxLayout()
layout.addWidget(WaitingSpinner())
layout.addWidget(QLabel("<h4>Loading Cloud Saves</h4>"))
self.setLayout(layout)
self.start_thread = LoadThread(self.core)
self.start_thread.signal.connect(self.setup_ui)
self.start_thread.start()
self.igames = self.core.get_installed_list()
def setup_ui(self, saves: list):
self.start_thread.disconnect()
QObjectCleanupHandler().add(self.layout())
self.main_layout = QVBoxLayout()
self.title = CustomQLabel("<h1>Cloud Saves</h1>\nFound Saves for folowing Games")
self.sync_all_button = QPushButton("Sync all games")
self.sync_all_button.clicked.connect(self.sync_all)
self.main_layout.addWidget(self.title)
self.main_layout.addWidget(self.sync_all_button)
latest_save = {}
for i in saves:
latest_save[i.app_name] = i
logger.info(f'Got {len(latest_save)} remote save game(s)')
if len(latest_save) == 0:
QMessageBox.information("No Games Found", "Your games don't support cloud save")
self.close()
return
self.widgets = []
for igame in self.igames:
game = self.core.get_game(igame.app_name)
if not game.supports_cloud_saves:
continue
sync_widget = SyncWidget(igame, latest_save[igame.app_name], self.core)
self.main_layout.addWidget(sync_widget)
self.widgets.append(sync_widget)
self.ok_button = QPushButton("Ok")
self.ok_button.clicked.connect(lambda: self.close())
self.main_layout.addWidget(self.ok_button)
self.setLayout(self.main_layout)
def sync_all(self):
for w in self.widgets:
if not w.igame.save_path:
save_path = self.core.get_save_path(w.igame.app_name)
if '%' in save_path or '{' in save_path:
self.logger.info("Could not find save_path")
save_path = PathInputDialog("Found no savepath",
"No save path was found. Please select path or skip")
if save_path == "":
continue
else:
w.igame.save_path = save_path
if w.res == SaveGameStatus.SAME_AGE:
continue
if w.res == SaveGameStatus.REMOTE_NEWER:
w.download()
elif w.res == SaveGameStatus.LOCAL_NEWER:
w.upload()

View file

@ -0,0 +1,167 @@
import os
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QHBoxLayout
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame, SaveGameStatus
from Rare.ext.QtExtensions import CustomQLabel
class _UploadThread(QThread):
signal = pyqtSignal()
def __init__(self, app_name, latest_save, save_path, core: LegendaryCore):
super(_UploadThread, self).__init__()
self.core = core
self.app_name = app_name
self.latest_save = latest_save
self.save_path = save_path
def run(self) -> None:
self.core.upload_save(self.app_name, self.latest_save.manifest_nam, self.save_path)
class _DownloadThread(QThread):
signal = pyqtSignal()
def __init__(self, app_name, latest_save, save_path, core: LegendaryCore):
super(_DownloadThread, self).__init__()
self.core = core
self.app_name = app_name
self.latest_save = latest_save
self.save_path = save_path
def run(self) -> None:
self.core.download_saves(self.app_name, self.latest_save.manifest_name, self.save_path, clean_dir=True)
class SyncWidget(QWidget):
def __init__(self, igame: InstalledGame, save, core: LegendaryCore):
super(SyncWidget, self).__init__()
self.layout = QVBoxLayout()
self.setObjectName("syncwidget")
self.setStyleSheet("""
QWidget#syncwidget{
border: 2px solid white;
}
""")
self.core = core
self.save = save
self.logger = getLogger("Sync " + igame.app_name)
self.game = self.core.get_game(igame.app_name)
self.igame = igame
self.has_save_path = True
if not igame.save_path:
save_path = self.core.get_save_path(igame.app_name)
if '%' in save_path or '{' in save_path:
status = "PathNotFound"
self.logger.info("Could not find save_path")
else:
igame.save_path = save_path
if not os.path.exists(igame.save_path):
os.makedirs(igame.save_path)
self.res, (dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path, save)
if self.res == SaveGameStatus.NO_SAVE:
self.logger.info('No cloud or local savegame found.')
return
game_title = CustomQLabel(f"<h2>{igame.title}</h2>")
if dt_local:
local_save_date = CustomQLabel(f"Local Save date: {dt_local.strftime('%Y-%m-%d %H:%M:%S')}")
else:
local_save_date = CustomQLabel("No Local Save files")
if dt_remote:
cloud_save_date = CustomQLabel(f"Cloud save date: {dt_remote.strftime('%Y-%m-%d %H:%M:%S')}")
else:
cloud_save_date = CustomQLabel(f"No Cloud saves")
if self.res == SaveGameStatus.SAME_AGE:
self.logger.info(f'Save game for "{igame.title}" is up to date')
status = "Game is up to date"
self.upload_button = QPushButton("Upload anyway")
self.download_button = QPushButton("Download anyway")
if self.res == SaveGameStatus.REMOTE_NEWER:
status = "Cloud save is newer"
self.download_button = QPushButton("Download Cloud saves")
self.download_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
self.upload_button = QPushButton("Upload Saves")
self.logger.info(f'Cloud save for "{igame.title}" is newer:')
self.logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
if dt_local:
self.logger.info(f'- Local save date: {dt_local.strftime("%Y-%m-%d %H:%M:%S")}')
else:
self.logger.info('- Local save date: N/A')
self.upload_button.setDisabled(True)
self.upload_button.setToolTip("No local save")
elif self.res == SaveGameStatus.LOCAL_NEWER:
status = "Local save is newer"
self.upload_button = QPushButton("Upload saves")
self.upload_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
self.download_button = QPushButton("Download saves")
self.logger.info(f'Local save for "{igame.title}" is newer')
if dt_remote:
self.logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
else:
self.logger.info('- Cloud save date: N/A')
self.download_button.setDisabled(True)
self.logger.info(f'- Local save date: {dt_local.strftime("%Y-%m-%d %H:%M:%S")}')
else:
self.logger.error("Error")
return
self.upload_button.clicked.connect(self.upload)
self.download_button.clicked.connect(self.download)
self.info_text = CustomQLabel(status)
self.layout.addWidget(game_title)
self.layout.addWidget(local_save_date)
self.layout.addWidget(cloud_save_date)
save_path_layout = QHBoxLayout()
self.save_path_text = CustomQLabel(igame.save_path)
self.change_save_path = QPushButton("Change path")
save_path_layout.addWidget(self.save_path_text)
save_path_layout.addWidget(self.change_save_path)
self.layout.addWidget(self.info_text)
button_layout = QHBoxLayout()
button_layout.addWidget(self.upload_button)
button_layout.addWidget(self.download_button)
self.layout.addLayout(button_layout)
self.setLayout(self.layout)
def upload(self):
self.logger.info("Uploading Saves for game " + self.igame.title)
self.info_text.setText("Uploading...")
self.upload_button.setDisabled(True)
self.download_button.setDisabled(True)
thr = _UploadThread(self.igame.app_name, self.save, self.igame.save_path, self.core)
thr.finished.connect(self.uploaded)
thr.start()
def uploaded(self):
self.info_text.setText("Upload finished")
def download(self):
if not os.path.exists(self.igame.save_path):
os.makedirs(self.igame.save_path)
self.upload_button.setDisabled(True)
self.download_button.setDisabled(True)
self.logger.info("Downloading Saves for game " + self.igame.title)
self.info_text.setText("Downloading...")
thr = _DownloadThread(self.igame.app_name, self.save, self.igame.save_path, self.core)
thr.finished.connect(self.downloaded)
thr.start()
def downloaded(self):
self.info_text.setText("Download finished")
self.upload_button.setDisabled(True)
self.download_button.setDisabled(True)
self.download_button.setStyleSheet("QPushButton{background-color: black}")

View file

View file

@ -1,161 +0,0 @@
import os
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, QObjectCleanupHandler
from PyQt5.QtWidgets import *
from legendary.core import LegendaryCore
from legendary.models.game import SaveGameStatus
from Rare.ext.QtExtensions import CustomQLabel, WaitingSpinner
logger = getLogger("Sync Saves")
class LoadThread(QThread):
signal = pyqtSignal(list)
def __init__(self, core: LegendaryCore):
super(LoadThread, self).__init__()
self.core = core
def run(self) -> None:
saves = self.core.get_save_games()
self.signal.emit(saves)
class SyncGame(QWidget):
def __init__(self):
super(SyncGame, self).__init__()
class SyncSavesDialog(QDialog):
def __init__(self, core: LegendaryCore):
super(SyncSavesDialog, self).__init__()
self.core = core
layout = QVBoxLayout()
layout.addWidget(WaitingSpinner())
layout.addWidget(QLabel("<h4>Loading Cloud Saves</h4>"))
self.setLayout(layout)
self.start_thread = LoadThread(self.core)
self.start_thread.signal.connect(self.setup_ui)
self.start_thread.start()
self.igames = self.core.get_installed_list()
# self.igames = self.core.get_installed_list()
# self.saves = self.core.get_save_games()
# latest_save = dict()
# for save in sorted(self.saves, key=lambda a: a.datetime):
# latest_save[save.app_name] = save
# self.setup_ui()
def setup_ui(self, saves: list):
self.start_thread.disconnect()
QObjectCleanupHandler().add(self.layout())
self.main_layout = QVBoxLayout()
self.title = CustomQLabel("<h1>Cloud Saves</h1>\nFound Saves for folowing Games")
self.sync_all_button = QPushButton("Sync all games")
self.main_layout.addWidget(self.title)
self.main_layout.addWidget(self.sync_all_button)
self.status = {}
latest_save = {}
for i in saves:
latest_save[i.app_name] = i
logger.info(f'Got {len(latest_save)} remote save game(s)')
if len(latest_save) == 0:
QMessageBox.information("No Games Found", "Your games don't support cloud save")
self.close()
return
for igame in self.igames:
game = self.core.get_game(igame.app_name)
if not game.supports_cloud_saves:
continue
logger.info(f'Checking "{igame.title}" ({igame.app_name})')
if not igame.save_path:
save_path = self.core.get_save_path(igame.app_name)
if '%' in save_path or '{' in save_path:
status = "PathNotFound"
logger.info("Could not find save_path")
else:
igame.save_path = save_path
if not os.path.exists(igame.save_path):
os.makedirs(igame.save_path)
res, (dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path,
latest_save.get(igame.app_name))
if res == SaveGameStatus.NO_SAVE:
logger.info('No cloud or local savegame found.')
continue
widget = QWidget()
layout = QVBoxLayout()
game_title = CustomQLabel(f"<h2>{igame.title}</h2>")
local_save_date = CustomQLabel(f"Local Save date: {dt_local.strftime('%Y-%m-%d %H:%M:%S')}")
cloud_save_date = CustomQLabel(f"Cloud save date: {dt_remote.strftime('%Y-%m-%d %H:%M:%S')}")
if res == SaveGameStatus.SAME_AGE:
logger.info(f'Save game for "{igame.title}" is up to date')
status = "Game is up to date"
upload_button = QPushButton("Upload anyway")
download_button = QPushButton("Download anyway")
if res == SaveGameStatus.REMOTE_NEWER:
status = "Cloud save is newer"
download_button = QPushButton("Download Cloud saves")
download_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
upload_button = QPushButton("Upload Saves")
logger.info(f'Cloud save for "{igame.title}" is newer:')
logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
if dt_local:
logger.info(f'- Local save date: {dt_local.strftime("%Y-%m-%d %H:%M:%S")}')
else:
logger.info('- Local save date: N/A')
upload_button.setDisabled(True)
upload_button.setToolTip("No local save")
elif res == SaveGameStatus.LOCAL_NEWER:
status = "local is newer"
upload_button = QPushButton("Upload saves")
upload_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
download_button = QPushButton("Download saves")
logger.info(f'Local save for "{igame.title}" is newer')
if dt_remote:
logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
else:
logger.info('- Cloud save date: N/A')
download_button.setDisabled(True)
logger.info(f'- Local save date: {dt_local.strftime("%Y-%m-%d %H:%M:%S")}')
upload_button.app_name = igame.app_name
download_button.app_name = igame.app_name
upload_button.clicked.connect(lambda: self.upload(upload_button.app_name))
download_button.clicked.connect(lambda: self.download_save(download_button.app_name))
mini_layout = QHBoxLayout()
mini_layout.addWidget(upload_button)
mini_layout.addWidget(download_button)
layout.addWidget(game_title)
layout.addWidget(local_save_date)
layout.addWidget(cloud_save_date)
layout.addWidget(CustomQLabel(status))
layout.addLayout(mini_layout)
self.main_layout.addLayout(layout)
self.status[igame.app_name] = res, dt_remote, dt_local, igame.save_path, latest_save[igame.app_name]
self.setLayout(self.main_layout)
def download_save(self, app_name):
logger.info('Downloading saves ')
res, dt_remote, dt_local, save_path, latest_save = self.status[app_name]
self.core.download_saves(app_name, latest_save.manifest_name, save_path)
# todo Threading
def upload(self, app_name):
logger.info("Uplouding saves")
res, dt_remote, dt_local, save_path, latest_save = self.status[app_name]
self.core.upload_save(app_name, save_path, dt_local)