1
0
Fork 0
mirror of synced 2024-06-27 18:51:06 +12:00

update download statistics during download

This commit is contained in:
ChemicalXandco 2021-03-14 21:11:03 +00:00
parent a3d7fc4389
commit 507e7768f8
4 changed files with 102 additions and 81 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@
__pycache__ __pycache__
/.idea /.idea
/.vscode
/build /build
/dist /dist
/Rare.egg-info/ /Rare.egg-info/

View file

@ -1,13 +1,17 @@
import os import os
import queue
import subprocess import subprocess
import time import time
from logging import getLogger from logging import getLogger
from multiprocessing import Queue as MPQueue
from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtCore import QThread, pyqtSignal, Qt, QVariant
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, QListWidget
from notifypy import Notify
from legendary.core import LegendaryCore from legendary.core import LegendaryCore
from legendary.models.game import Game from legendary.models.game import Game
from notifypy import Notify from legendary.models.downloading import UIUpdate
from legendary.utils.selective_dl import games
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog
from Rare.utils.LegendaryApi import VerifyThread from Rare.utils.LegendaryApi import VerifyThread
@ -18,11 +22,13 @@ logger = getLogger("Download")
class DownloadThread(QThread): class DownloadThread(QThread):
status = pyqtSignal(str) status = pyqtSignal(str)
statistics = pyqtSignal(UIUpdate)
def __init__(self, dlm, core: LegendaryCore, igame, repair=False, repair_file=None): def __init__(self, dlm, core: LegendaryCore, status_queue: MPQueue, igame, repair=False, repair_file=None):
super(DownloadThread, self).__init__() super(DownloadThread, self).__init__()
self.dlm = dlm self.dlm = dlm
self.core = core self.core = core
self.status_queue = status_queue
self.igame = igame self.igame = igame
self.repair = repair self.repair = repair
self.repair_file = repair_file self.repair_file = repair_file
@ -32,9 +38,15 @@ class DownloadThread(QThread):
try: try:
self.dlm.start() self.dlm.start()
time.sleep(1)
while self.dlm.is_alive():
try:
self.statistics.emit(self.status_queue.get(timeout=0.1))
except queue.Empty:
pass
self.dlm.join() self.dlm.join()
except: except Exception as e:
logger.error(f"Installation failed after{time.time() - start_time:.02f} seconds.") logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
self.status.emit("error") self.status.emit("error")
return return
@ -104,9 +116,9 @@ class DownloadTab(QWidget):
self.active_game: Game = None self.active_game: Game = None
self.installing_game = QLabel(self.tr("No active Download")) self.installing_game = QLabel(self.tr("No active Download"))
self.dl_speed = QLabel(self.tr("Download speed") + ": 0MB/s") self.dl_speed = QLabel()
self.cache_used = QLabel(self.tr("Cache used") + ": 0MB") self.cache_used = QLabel()
self.downloaded = QLabel(self.tr("Downloaded") + ": 0MB") self.downloaded = QLabel()
self.info_layout = QGridLayout() self.info_layout = QGridLayout()
@ -114,16 +126,11 @@ class DownloadTab(QWidget):
self.info_layout.addWidget(self.dl_speed, 0, 1) self.info_layout.addWidget(self.dl_speed, 0, 1)
self.info_layout.addWidget(self.cache_used, 1, 0) self.info_layout.addWidget(self.cache_used, 1, 0)
self.info_layout.addWidget(self.downloaded, 1, 1) self.info_layout.addWidget(self.downloaded, 1, 1)
self.layout.addLayout(self.info_layout) self.layout.addLayout(self.info_layout)
self.prog_bar = QProgressBar() self.prog_bar = QProgressBar()
self.prog_bar.setMaximum(100)
self.layout.addWidget(self.prog_bar) self.layout.addWidget(self.prog_bar)
label = QLabel(
self.tr("<b>WARNING</b>: The progress bar is not implemented. It is normal, if there is no progress. The "
"progress is visible in console, because Legendary prints output to console. A pull request is "
"active to get output"))
label.setWordWrap(True)
self.layout.addWidget(label)
self.installing_game_widget = QLabel(self.tr("No active Download")) self.installing_game_widget = QLabel(self.tr("No active Download"))
self.layout.addWidget(self.installing_game_widget) self.layout.addWidget(self.installing_game_widget)
@ -150,55 +157,39 @@ class DownloadTab(QWidget):
def install_game(self, options: InstallOptions): def install_game(self, options: InstallOptions):
game = self.core.get_game(options.app_name, update_meta=True) game = self.core.get_game(options.app_name, update_meta=True)
if self.core.is_installed(options.app_name): status_queue = MPQueue()
igame = self.core.get_installed_game(options.app_name) try:
if igame.needs_verification and not options.repair: dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download(
options.repair = True app_name=options.app_name,
repair_file = None base_path=options.path,
if options.repair: force=False, # TODO allow overwrite
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair') no_install=options.download_only,
status_q=status_queue,
if not game: #max_shm=,
QMessageBox.warning(self, "Error", self.tr("Could not find Game in your library")) 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=,
#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 return
if game.is_dlc:
logger.info("DLCs are currently not supported")
return
if game.is_dlc:
logger.info('Install candidate is DLC')
app_name = game.metadata['mainGameItem']['releaseInfo'][0]['appId']
base_game = self.core.get_game(app_name)
# check if base_game is actually installed
if not self.core.is_installed(app_name):
# download mode doesn't care about whether or not something's installed
logger.error("Base Game is not installed")
return
else:
base_game = None
if options.repair:
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair')
if not self.core.is_installed(game.app_name):
return
if not os.path.exists(repair_file):
logger.info("Game has not been verified yet")
if QMessageBox.question(self, "Verify", self.tr("Game has not been verified yet. Do you want to verify first?"),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes:
self.verify_thread = VerifyThread(self.core, game.app_name)
self.verify_thread.finished.connect(
lambda: self.prepare_download(game, options, base_game, repair_file))
self.verify_thread.start()
return
self.prepare_download(game, options, base_game, repair_file)
def prepare_download(self, game, options, base_game, repair_file):
dlm, analysis, igame = self.core.prepare_download(
game=game,
base_path=options.path,
max_workers=options.max_workers, base_game=base_game, repair=options.repair)
if not analysis.dl_size: if not analysis.dl_size:
QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists")) QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists"))
return return
@ -206,26 +197,50 @@ class DownloadTab(QWidget):
if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept():
return return
self.installing_game_widget.setText("")
self.installing_game.setText(self.tr("Installing Game: ") + game.app_title)
res = self.core.check_installation_conditions(analysis=analysis, install=igame, game=game,
updating=self.core.is_installed(options.app_name),
)
if res.warnings:
for w in sorted(res.warnings):
logger.warning(w)
if res.failures:
for msg in sorted(res.failures):
logger.error(msg)
logger.error('Installation cannot proceed, exiting.')
QMessageBox.warning(self, "Installation failed",
self.tr("Installation failed. See logs for more information"))
return
self.active_game = game self.active_game = game
self.thread = DownloadThread(dlm, self.core, igame, options.repair, repair_file) self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file)
self.thread.status.connect(self.status) self.thread.status.connect(self.status)
self.thread.statistics.connect(self.statistics)
self.thread.start() self.thread.start()
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): def status(self, text):
if text == "dl_finished": if text == "dl_finished":
pass pass
@ -240,6 +255,12 @@ class DownloadTab(QWidget):
elif text == "error": elif text == "error":
QMessageBox.warning(self, "warn", "Download error") QMessageBox.warning(self, "warn", "Download error")
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")
def update_game(self, app_name: str): def update_game(self, app_name: str):
print("Update ", app_name) print("Update ", app_name)
self.install_game(InstallOptions(app_name)) self.install_game(InstallOptions(app_name))

View file

@ -98,7 +98,6 @@ class GameInfo(QWidget):
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
LegendaryApi.uninstall(self.game.app_name, self.core) LegendaryApi.uninstall(self.game.app_name, self.core)
self.update_list.emit() self.update_list.emit()
self.back_button.click()
def repair(self): def repair(self):
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair') repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair')

View file

@ -1,2 +1,2 @@
export PYTHONPATH=$PWD export PYTHONPATH=$PWD
python3 Rare python3 Rare