1
0
Fork 0
mirror of synced 2024-06-28 11:11:15 +12:00

Move all download preparations inside InstallDialog.

InstallDialog now returns a InstallQueueItemModel ready
to be downloaded or queued.

Renamed a few model attributes to match legendary's names.

InstallDialog can be run silently for auto-downloads.
This commit is contained in:
Stelios Tsampas 2021-05-23 15:15:36 +03:00
parent 376d3e0eba
commit b0ec5c5fcb
5 changed files with 100 additions and 108 deletions

View file

@ -1,4 +1,5 @@
import os import os
from multiprocessing import Queue as MPQueue
from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox
@ -7,21 +8,22 @@ from custom_legendary.core import LegendaryCore
from custom_legendary.utils.selective_dl import games from custom_legendary.utils.selective_dl import games
from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog
from rare.utils.extra_widgets import PathEdit from rare.utils.extra_widgets import PathEdit
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallDownloadModel, InstallQueueItemModel
from rare.utils.utils import get_size from rare.utils.utils import get_size
class InstallDialog(QDialog, Ui_InstallDialog): class InstallDialog(QDialog, Ui_InstallDialog):
options = False
def __init__(self, app_name, core: LegendaryCore, update=False, parent=None): def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, parent=None):
super(InstallDialog, self).__init__(parent) super(InstallDialog, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose, True) self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setupUi(self) self.setupUi(self)
self.core = core self.core = core
self.app_name = app_name self.dl_item = dl_item
self.game = self.core.get_game(app_name) self.dl_item.status_q = MPQueue()
self.app_name = self.dl_item.options.app_name
self.game = self.core.get_game(self.app_name)
self.update_game = update self.update_game = update
self.threadpool = QThreadPool(self) self.threadpool = QThreadPool(self)
@ -54,12 +56,11 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.max_workers_spin.setValue(int(max_workers)) self.max_workers_spin.setValue(int(max_workers))
self.sdl_list_checks = list() self.sdl_list_checks = list()
self.tags = ['']
try: try:
for key, info in games[app_name].items(): for key, info in games[self.app_name].items():
cb = QDataCheckBox(info['name'], info['tags']) cb = QDataCheckBox(info['name'], info['tags'])
if key == '__required': if key == '__required':
self.tags.extend(info['tags']) self.dl_item.options.sdl_list.extend(info['tags'])
cb.setChecked(True) cb.setChecked(True)
cb.setDisabled(True) cb.setDisabled(True)
self.sdl_list_layout.addWidget(cb) self.sdl_list_layout.addWidget(cb)
@ -71,7 +72,8 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.sdl_list_frame.setVisible(False) self.sdl_list_frame.setVisible(False)
self.sdl_list_label.setVisible(False) self.sdl_list_label.setVisible(False)
self.get_install_info(app_name, default_path, self.tags) self.get_options()
self.get_download_info()
self.install_button.clicked.connect(self.on_install_button_clicked) self.install_button.clicked.connect(self.on_install_button_clicked)
self.cancel_button.clicked.connect(self.on_cancel_button_clicked) self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
@ -79,13 +81,27 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.resize(self.minimumSize()) self.resize(self.minimumSize())
self.setFixedSize(self.size()) self.setFixedSize(self.size())
def get_install_options(self, path=None): def get_options(self):
self.dl_item.options.path = self.install_dir_edit.text() if not self.update_game else None
self.dl_item.options.max_workers = self.max_workers_spin.value()
self.dl_item.options.force = self.force_download_check.isChecked()
self.dl_item.options.ignore_space_req = self.ignore_space_check.isChecked()
self.dl_item.options.dl_only = self.download_only_check.isChecked()
self.dl_item.options.sdl_list = ['']
for cb in self.sdl_list_checks:
if data := cb.isChecked():
self.dl_item.options.sdl_list.extend(data)
def get_download_item(self, path=None, silent=False):
if path: if path:
self.install_dir_edit.setText(path) self.install_dir_edit.setText(path)
self.exec_() if silent:
return self.options self.threadpool.waitForDone()
else:
self.exec_()
return self.dl_item
def get_install_info(self, app_name, path, tags): def get_download_info(self):
message = self.tr("Updating...") message = self.tr("Updating...")
self.download_size_info_label.setText(message) self.download_size_info_label.setText(message)
self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal")
@ -93,44 +109,36 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal")
self.install_button.setEnabled(False) self.install_button.setEnabled(False)
self.sdl_list_frame.setEnabled(False) self.sdl_list_frame.setEnabled(False)
info_worker = InstallInfoWorker(app_name, path, tags, self.core) info_worker = InstallInfoWorker(self.core, self.dl_item)
info_worker.setAutoDelete(True) info_worker.setAutoDelete(True)
info_worker.signals.finished.connect(self.on_worker_finished) info_worker.signals.finished.connect(self.on_worker_finished)
self.threadpool.start(info_worker) self.threadpool.start(info_worker)
def on_sdl_checkbox_changed(self): def on_sdl_checkbox_changed(self):
self.tags = [''] self.get_options()
for cb in self.sdl_list_checks: self.get_download_info()
if data := cb.isChecked():
self.tags.extend(data)
self.get_install_info(self.app_name, self.install_dir_edit.text(), self.tags)
def on_install_dir_text_changed(self, path: str): def on_install_dir_text_changed(self):
self.get_install_info(self.app_name, path, self.tags) self.get_options()
self.get_download_info()
def on_install_button_clicked(self): def on_install_button_clicked(self):
self.options = InstallOptionsModel(
app_name=self.app_name,
path=self.install_dir_edit.text() if not self.update_game else None,
max_workers=self.max_workers_spin.value(),
force=self.force_download_check.isChecked(),
ignore_free_space=self.ignore_space_check.isChecked(),
dl_only=self.download_only_check.isChecked(),
sdl_list=self.tags
)
self.threadpool.clear() self.threadpool.clear()
self.close() self.close()
def on_cancel_button_clicked(self): def on_cancel_button_clicked(self):
self.dl_item = False
self.threadpool.clear() self.threadpool.clear()
self.close() self.close()
def on_worker_finished(self, info: tuple): def on_worker_finished(self, dl_item: InstallQueueItemModel):
download_size, install_size = info
# TODO: Check available size and act accordingly # TODO: Check available size and act accordingly
# TODO: (show message in label | color it | disable install unless ignore) # TODO: (show message in label | color it | disable install unless ignore)
# TODO: Find a way to get the installation size delta and show it # TODO: Find a way to get the installation size delta and show it
if download_size is not None and install_size is not None: if dl_item:
self.dl_item = dl_item
download_size = self.dl_item.download.analysis.dl_size
install_size = self.dl_item.download.analysis.install_size
if download_size: if download_size:
self.download_size_info_label.setText("{}".format(get_size(download_size))) self.download_size_info_label.setText("{}".format(get_size(download_size)))
self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold")
@ -147,37 +155,58 @@ class InstallDialog(QDialog, Ui_InstallDialog):
class InstallInfoWorkerSignals(QObject): class InstallInfoWorkerSignals(QObject):
finished = pyqtSignal(tuple) finished = pyqtSignal(InstallQueueItemModel)
class InstallInfoWorker(QRunnable): class InstallInfoWorker(QRunnable):
def __init__(self, app_name, path, tags, core: LegendaryCore): def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel):
super(InstallInfoWorker, self).__init__() super(InstallInfoWorker, self).__init__()
self.signals = InstallInfoWorkerSignals() self.signals = InstallInfoWorkerSignals()
self.core = core self.core = core
self.app_name = app_name self.dl_item = dl_item
self.path = path
self.tags = tags
@pyqtSlot() @pyqtSlot()
def run(self): def run(self):
try: try:
dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( download = InstallDownloadModel(*self.core.prepare_download(
app_name=self.app_name, app_name=self.dl_item.options.app_name,
base_path=self.path, base_path=self.dl_item.options.path,
sdl_prompt=lambda app_name, title: self.tags force=self.dl_item.options.force,
) no_install=self.dl_item.options.dl_only,
self.signals.finished.emit((analysis.dl_size, analysis.install_size)) status_q=self.dl_item.status_q,
# max_shm=,
max_workers=self.dl_item.options.max_workers,
# game_folder=,
# disable_patching=,
# override_manifest=,
# override_old_manifest=,
# override_base_url=,
# platform_override=,
# file_prefix_filter=,
# file_exclude_filter=,
# file_install_tag=,
# dl_optimizations=,
# dl_timeout=,
repair=self.dl_item.options.repair,
# repair_use_latest=,
ignore_space_req=self.dl_item.options.ignore_space_req,
# disable_delta=,
# override_delta_manifest=,
# reset_sdl=,
sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list
))
self.dl_item.download = download
except: except:
self.signals.finished.emit((None, None)) self.dl_item.download = None
self.signals.finished.emit(self.dl_item)
return return
class QDataCheckBox(QCheckBox): class QDataCheckBox(QCheckBox):
def __init__(self, text, data=None): def __init__(self, text, data=None, parent=None):
super(QDataCheckBox, self).__init__() super(QDataCheckBox, self).__init__(parent)
self.setText(text) self.setText(text)
self.data = data self.data = data

View file

@ -14,7 +14,7 @@ from rare.components.tabs.downloads import DownloadTab
from rare.components.tabs.games import GameTab from rare.components.tabs.games import GameTab
from rare.components.tabs.settings import SettingsTab from rare.components.tabs.settings import SettingsTab
from rare.utils import legendary_utils from rare.utils import legendary_utils
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallQueueItemModel, InstallOptionsModel
class TabWidget(QTabWidget): class TabWidget(QTabWidget):
@ -96,11 +96,12 @@ class TabWidget(QTabWidget):
self.setIconSize(QSize(25, 25)) self.setIconSize(QSize(25, 25))
def install_game(self, app_name, disable_path=False): def install_game(self, app_name, disable_path=False):
dialog = InstallDialog(app_name, self.core, disable_path, parent=self) download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name))
options = dialog.get_install_options() dialog = InstallDialog(self.core, download_item, update=disable_path, parent=self)
if options: download_item = dialog.get_download_item()
if download_item:
self.setCurrentIndex(1) self.setCurrentIndex(1)
self.start_download(options) self.start_download(download_item)
def start_download(self, options): def start_download(self, options):
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1 downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1

View file

@ -1,6 +1,5 @@
import datetime import datetime
from logging import getLogger from logging import getLogger
from multiprocessing import Queue as MPQueue
from PyQt5.QtCore import QThread, pyqtSignal, QSettings from PyQt5.QtCore import QThread, pyqtSignal, QSettings
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, \ from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, \
@ -12,7 +11,7 @@ from custom_legendary.models.game import Game, InstalledGame
from rare.components.dialogs.install_dialog import InstallDialog from rare.components.dialogs.install_dialog import InstallDialog
from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget
from rare.components.tabs.downloads.download_thread import DownloadThread from rare.components.tabs.downloads.download_thread import DownloadThread
from rare.utils.models import InstallOptionsModel, InstallDownloadModel, InstallQueueItemModel from rare.utils.models import InstallOptionsModel, InstallQueueItemModel
from rare.utils.utils import get_size from rare.utils.utils import get_size
logger = getLogger("Download") logger = getLogger("Download")
@ -96,44 +95,7 @@ class DownloadTab(QWidget):
def stop_download(self): def stop_download(self):
self.thread.kill() self.thread.kill()
def install_game(self, options: InstallOptionsModel): def install_game(self, queue_item: InstallQueueItemModel):
status_queue = MPQueue()
try:
download = InstallDownloadModel(*self.core.prepare_download(
app_name=options.app_name,
base_path=options.path,
force=options.force,
no_install=options.dl_only,
status_q=status_queue,
# max_shm=,
max_workers=options.max_workers,
# game_folder=,
# disable_patching=,
# override_manifest=,
# override_old_manifest=,
# override_base_url=,
# platform_override=,
# file_prefix_filter=,
# file_exclude_filter=,
# file_install_tag=,
# dl_optimizations=,
# dl_timeout=,
repair=options.repair,
# repair_use_latest=,
ignore_space_req=options.ignore_free_space,
# disable_delta=,
# override_delta_manifest=,
# reset_sdl=,
sdl_prompt=lambda app_name, title: options.sdl_list
))
except Exception as e:
QMessageBox.warning(self, self.tr("Error preparing download"),
str(e))
return
queue_item = InstallQueueItemModel(status_queue, download, options)
if self.active_game is None: if self.active_game is None:
self.start_installation(queue_item) self.start_installation(queue_item)
else: else:
@ -231,14 +193,11 @@ class DownloadTab(QWidget):
def update_game(self, app_name: str, auto=False): def update_game(self, app_name: str, auto=False):
logger.info("Update " + app_name) logger.info("Update " + app_name)
if not auto: download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name))
dialog = InstallDialog(app_name, self.core, update=True, parent=self) dialog = InstallDialog(self.core, download_item, update=True, parent=self)
options = dialog.get_install_options() download_item = dialog.get_download_item(silent=auto)
else: if download_item:
self.install_game(InstallOptionsModel(app_name=app_name)) self.install_game(download_item)
return
if options:
self.install_game(options)
else: else:
self.update_widgets[app_name].update_button.setDisabled(False) self.update_widgets[app_name].update_button.setDisabled(False)
self.update_widgets[app_name].update_with_settings.setDisabled(False) self.update_widgets[app_name].update_with_settings.setDisabled(False)

View file

@ -23,10 +23,10 @@ class DownloadThread(QThread):
def __init__(self, core: LegendaryCore, queue_item: InstallQueueItemModel): def __init__(self, core: LegendaryCore, queue_item: InstallQueueItemModel):
super(DownloadThread, self).__init__() super(DownloadThread, self).__init__()
self.dlm = queue_item.download.dlmanager
self.core = core self.core = core
self.dlm = queue_item.download.dlmanager
self.dl_only = queue_item.options.dl_only self.dl_only = queue_item.options.dl_only
self.status_queue = queue_item.queue self.status_q = queue_item.status_q
self.igame = queue_item.download.igame self.igame = queue_item.download.igame
self.repair = queue_item.download.repair self.repair = queue_item.download.repair
self.repair_file = queue_item.download.repair_file self.repair_file = queue_item.download.repair_file
@ -103,7 +103,7 @@ class DownloadThread(QThread):
dl_stopped = True dl_stopped = True
try: try:
if not dl_stopped: if not dl_stopped:
self.statistics.emit(self.status_queue.get(timeout=1)) self.statistics.emit(self.status_q.get(timeout=1))
except queue.Empty: except queue.Empty:
pass pass

View file

@ -4,20 +4,20 @@ import os
class InstallOptionsModel: class InstallOptionsModel:
def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"),
max_workers: int = os.cpu_count() * 2, repair: bool = False, dl_only: bool = False, max_workers: int = os.cpu_count() * 2, repair: bool = False, dl_only: bool = False,
ignore_free_space: bool = False, force: bool = False, sdl_list: list = [''] ignore_space_req: bool = False, force: bool = False, sdl_list: list = ['']
): ):
self.app_name = app_name self.app_name = app_name
self.path = path self.path = path
self.max_workers = max_workers self.max_workers = max_workers
self.repair = repair self.repair = repair
self.dl_only = dl_only self.dl_only = dl_only
self.ignore_free_space = ignore_free_space self.ignore_space_req = ignore_space_req
self.force = force self.force = force
self.sdl_list = sdl_list self.sdl_list = sdl_list
class InstallDownloadModel: class InstallDownloadModel:
def __init__(self, dlmanager, analysis, game, igame, repair: bool = False, repair_file: str = None): def __init__(self, dlmanager, analysis, game, igame, repair: bool, repair_file: str):
self.dlmanager = dlmanager self.dlmanager = dlmanager
self.analysis = analysis self.analysis = analysis
self.game = game self.game = game
@ -27,7 +27,10 @@ class InstallDownloadModel:
class InstallQueueItemModel: class InstallQueueItemModel:
def __init__(self, queue, download: InstallDownloadModel, options: InstallOptionsModel): def __init__(self, status_q=None, download: InstallDownloadModel = None, options: InstallOptionsModel = None):
self.queue = queue self.status_q = status_q
self.download = download self.download = download
self.options = options self.options = options
def __bool__(self):
return (self.status_q is not None) and (self.download is not None) and (self.options is not None)