1
0
Fork 0
mirror of synced 2024-06-29 11:40:37 +12:00
Rare/rare/components/tabs/downloads/__init__.py

291 lines
11 KiB
Python

import datetime
from logging import getLogger
from multiprocessing import Queue as MPQueue
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \
QListWidget, QHBoxLayout, QGroupBox
from rare.components.dialogs.install_dialog import InstallInfoDialog, InstallDialog
from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget
from rare.components.tabs.downloads.download_thread import DownloadThread
from rare.utils.models import InstallOptions
from custom_legendary.core import LegendaryCore
from custom_legendary.models.downloading import UIUpdate
from custom_legendary.models.game import Game, InstalledGame
from custom_legendary.utils.selective_dl import games
logger = getLogger("Download")
class DownloadTab(QWidget):
finished = pyqtSignal(bool)
thread: QThread
dl_queue = []
def __init__(self, core: LegendaryCore, updates: list):
super(DownloadTab, self).__init__()
self.core = core
self.layout = QVBoxLayout()
self.active_game: Game = None
self.installing_game = QLabel(self.tr("No active Download"))
self.dl_speed = QLabel()
self.cache_used = QLabel()
self.downloaded = QLabel()
self.time_left = QLabel()
self.info_layout = QGridLayout()
self.info_layout.addWidget(self.installing_game, 0, 0)
self.info_layout.addWidget(self.dl_speed, 0, 1)
self.info_layout.addWidget(self.cache_used, 1, 0)
self.info_layout.addWidget(self.downloaded, 1, 1)
self.info_layout.addWidget(self.time_left, 2, 0)
self.layout.addLayout(self.info_layout)
self.mini_layout = QHBoxLayout()
self.prog_bar = QProgressBar()
self.prog_bar.setMaximum(100)
self.mini_layout.addWidget(self.prog_bar)
self.kill_button = QPushButton(self.tr("Stop Download"))
self.kill_button.setDisabled(True)
self.kill_button.clicked.connect(self.stop_download)
self.mini_layout.addWidget(self.kill_button)
self.layout.addLayout(self.mini_layout)
self.queue_widget = DlQueueWidget()
self.layout.addWidget(self.queue_widget)
self.queue_widget.update_list.connect(self.update_dl_queue)
self.updates = QGroupBox(self.tr("Updates"))
self.updates.setObjectName("group")
self.update_layout = QVBoxLayout()
self.update_widgets = {}
self.update_text = QLabel(self.tr("No updates available"))
self.update_text.setVisible(len(updates) == 0)
self.update_layout.addWidget(self.update_text)
for igame in updates:
widget = UpdateWidget(core, igame)
self.update_widgets[igame.app_name] = widget
self.update_layout.addWidget(widget)
widget.update.connect(self.update_game)
self.updates.setLayout(self.update_layout)
self.layout.addWidget(self.updates)
self.layout.addStretch(1)
self.setLayout(self.layout)
def update_dl_queue(self, dl_queue):
self.dl_queue = dl_queue
def stop_download(self):
self.thread.kill()
def install_game(self, options: InstallOptions):
status_queue = MPQueue()
try:
dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download(
app_name=options.app_name,
base_path=options.path,
force=options.force,
no_install=options.download_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=self.sdl_prompt)
except Exception as e:
QMessageBox.warning(self, self.tr("Error preparing download"),
str(e))
return
if not analysis.dl_size:
QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists"))
return
# Information
if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept():
return
if self.active_game is None:
self.start_installation(dlm, game, status_queue, igame, repair_file, options, analysis)
else:
self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options, analysis))
self.queue_widget.update_queue(self.dl_queue)
def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions, analysis):
print("start installation", game.app_title)
if self.dl_queue:
self.dl_queue.pop(0)
self.queue_widget.update_queue(self.dl_queue)
self.active_game = game
self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file)
self.thread.status.connect(self.status)
self.thread.statistics.connect(self.statistics)
self.thread.start()
self.kill_button.setDisabled(False)
self.installing_game.setText(self.tr("Installing Game: ") + self.active_game.app_title)
def sdl_prompt(self, app_name: str = '', title: str = '') -> list:
sdl = QDialog()
sdl.setWindowTitle('Select Additional Downloads')
layout = QVBoxLayout(sdl)
sdl.setLayout(layout)
pack_list = QListWidget()
layout.addWidget(pack_list)
done = QPushButton(text='Done')
done.clicked.connect(sdl.accept)
layout.addWidget(done)
tags = ['']
if '__required' in games[app_name]:
tags.extend(games[app_name]['__required']['tags'])
# add available additional downloads to list
pack_list.addItems([tag + ': ' + info['name'] for tag, info in games[app_name].items() if tag != '__required'])
# enable checkboxes
for i in range(len(pack_list)):
item = pack_list.item(i)
item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
item.setCheckState(Qt.Unchecked)
sdl.exec_()
# read checkboxes states
for i in range(len(pack_list)):
item = pack_list.item(i)
if item.checkState() == Qt.Checked:
tag = item.text().split(':')[0]
tags.extend(games[app_name][tag]['tags'])
return tags
def status(self, text):
if text == "dl_finished":
pass
elif text == "finish":
self.installing_game.setText(self.tr("Download finished. Reload library"))
if QSettings().value("notification", True, bool):
try:
from notifypy import Notify
except ModuleNotFoundError:
logger.warning("No Notification Module found")
else:
notification = Notify()
notification.title = self.tr("Installation finished")
notification.message = self.tr("Finished Download of game {}").format(self.active_game.app_title)
notification.send()
# QMessageBox.information(self, "Info", "Download finished")
logger.info("Download finished: " + self.active_game.app_title)
if self.dl_queue:
if self.dl_queue[0][1] == self.active_game.app_name:
self.dl_queue.pop(0)
self.queue_widget.update_queue(self.dl_queue)
if self.active_game.app_name in self.update_widgets.keys():
self.update_widgets[self.active_game.app_name].setVisible(False)
self.update_widgets.pop(self.active_game.app_name)
if len(self.update_widgets) == 0:
self.update_text.setVisible(True)
self.active_game = None
for i in self.update_widgets.values():
i.update_button.setDisabled(False)
self.finished.emit(True)
self.reset_infos()
if len(self.dl_queue) != 0:
self.start_installation(*self.dl_queue[0])
else:
self.queue_widget.update_queue(self.dl_queue)
elif text[:5] == "error":
QMessageBox.warning(self, "warn", "Download error: "+text[6:])
elif text == "stop":
self.reset_infos()
self.active_game = None
self.finished.emit(False)
if self.dl_queue:
self.start_installation(*self.dl_queue[0])
def reset_infos(self):
self.kill_button.setDisabled(True)
self.installing_game.setText(self.tr("Installing Game: No active download"))
self.prog_bar.setValue(0)
self.dl_speed.setText("")
self.time_left.setText("")
self.cache_used.setText("")
self.downloaded.setText("")
def statistics(self, ui_update: UIUpdate):
self.prog_bar.setValue(ui_update.progress)
self.dl_speed.setText(self.tr("Download speed") + f": {ui_update.download_speed / 1024 / 1024:.02f}MB/s")
self.cache_used.setText(self.tr("Cache used") + f": {ui_update.cache_usage / 1024 / 1024:.02f}MB")
self.downloaded.setText(self.tr("Downloaded") + f": {ui_update.total_downloaded / 1024 / 1024:.02f}MB")
self.time_left.setText(self.tr("Time left: ") + self.get_time(ui_update.estimated_time_left))
def get_time(self, seconds: int) -> str:
return str(datetime.timedelta(seconds=seconds))
def update_game(self, app_name: str):
logger.info("Update " + app_name)
infos = InstallDialog(app_name, self.core, True).get_information()
if infos != 0:
path, max_workers, force, ignore_free_space = infos
self.install_game(InstallOptions(app_name=app_name, max_workers=max_workers, path=path,
force=force, ignore_free_space=ignore_free_space))
class UpdateWidget(QWidget):
update = pyqtSignal(str)
def __init__(self, core: LegendaryCore, game: InstalledGame):
super(UpdateWidget, self).__init__()
self.core = core
self.game = game
self.layout = QVBoxLayout()
self.title = QLabel(self.game.title)
self.layout.addWidget(self.title)
self.update_button = QPushButton(self.tr("Update Game"))
self.update_button.clicked.connect(self.update_game)
self.layout.addWidget(self.update_button)
self.setLayout(self.layout)
def update_game(self):
self.update_button.setDisabled(True)
self.update.emit(self.game.app_name)