diff --git a/src/gui_qt/lib/utils.py b/src/gui_qt/lib/utils.py index 45f79c9..bafa3c6 100644 --- a/src/gui_qt/lib/utils.py +++ b/src/gui_qt/lib/utils.py @@ -1,9 +1,13 @@ -import sys +from __future__ import annotations + +from functools import lru_cache from os import getenv, stat +from os.path import exists import hashlib import subprocess from datetime import datetime from contextlib import contextmanager +from pathlib import Path import requests from numpy import linspace @@ -58,9 +62,7 @@ class RdBuCMap: class UpdateDialog(QtWidgets.QDialog): - host = 'mirror.infra.pkm' - bucket = 'nmreval' - version = 'NMReval-latest-x86_64' + startDownload = QtCore.pyqtSignal(list) def __init__(self, filename: str = None, parent=None): super().__init__(parent=parent) @@ -69,11 +71,20 @@ class UpdateDialog(QtWidgets.QDialog): if filename is None: filename = getenv('APPIMAGE') self._appfile = filename - self._url_zsync = f'http://{self.host}/{self.bucket}/{self.version}.AppImage.zsync' - self.process = QtCore.QProcess(self) + self.success = False + self.updater = Updater() - self.look_for_updates() + self.thread = QtCore.QThread(self) + self.thread.start() + self.helper = Downloader() + self.startDownload.connect(self.helper.run_download) + self.helper.progressChanged.connect(self.status.setText) + self.helper.finished.connect(self.finish_update) + self.helper.started.connect(self.status.show) + self.helper.moveToThread(self.thread) + + self.look_for_updates(self._appfile) def _init_ui(self): self.setWindowTitle('Updates') @@ -85,50 +96,135 @@ class UpdateDialog(QtWidgets.QDialog): layout.addSpacing(10) + self.status = QtWidgets.QLabel() + self.status.hide() + layout.addWidget(self.status) + layout.addSpacing(10) + self.dialog_button = QtWidgets.QDialogButtonBox() - self.dialog_button.accepted.connect(self.accept) - self.dialog_button.rejected.connect(self.reject) + self.dialog_button.accepted.connect(self.update_appimage) + self.dialog_button.rejected.connect(self.close) layout.addWidget(self.dialog_button) self.setLayout(layout) - def look_for_updates(self): + def look_for_updates(self, filename=None): # Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage - m_time_zsync, checksum_zsync = self.get_zsync() - m_time_file, checksum_file = self.get_appimage_info() + is_updateble, m_time_file, m_time_zsync = self.updater.get_update_information(filename) if m_time_zsync is None: label_text = '
Retrieval of version information failed.
' \ 'Please try later (or complain to people that it does not work).
' dialog_bttns = QtWidgets.QDialogButtonBox.Close else: - label_text = f'Found most recent update: {m_time_zsync.strftime("%d %B %Y %H:%M")}
' + label_text = f'Date of most recent AppImage: {m_time_zsync.strftime("%d %B %Y %H:%M")}
' if m_time_file is None: - label_text += 'No AppImage file found, press Ok to downlaod latest version.' + label_text += 'No AppImage file found, press Ok to download latest version.' dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Close else: label_text += f'Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}
' - if not ((checksum_file is not None) and (checksum_zsync is not None)): - label_text += 'Could not determine if this version is newer, please update manually (if necessary).' + if is_updateble is None: + self.status.setText('Could not determine if this version is newer, please update manually (if necessary).') dialog_bttns = QtWidgets.QDialogButtonBox.Close - elif checksum_file != checksum_zsync: - label_text += f'Newer version available. Update?
' + elif is_updateble: + self.status.setText(f'Newer version available. Press Ok to download new version, Cancel to ignore.') dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel else: - label_text += f'
Version is already the newest
' + self.status.setText(f'Version may be already up-to-date.') dialog_bttns = QtWidgets.QDialogButtonBox.Close self.label.setText(label_text) self.dialog_button.setStandardButtons(dialog_bttns) - def get_zsync(self): + @QtCore.pyqtSlot() + def update_appimage(self): + if self._appfile is None: + args = [self.updater.zsync_url] + else: + # this breaks the download for some reason + args = ['-i', self._appfile, self.updater.zsync_url] + + args = [self.updater.zsync_url] + + self.dialog_button.setEnabled(False) + + self.startDownload.emit(args) + self.status.show() + + @QtCore.pyqtSlot(int) + def finish_update(self, retcode: int): + # print('finished with', retcode) + self.success = retcode == 0 + if retcode == 0: + self.status.setText('Download complete.') + else: + self.status.setText(f'Download failed :( with return code {retcode}.') + self.dialog_button.setStandardButtons(QtWidgets.QDialogButtonBox.Close) + self.dialog_button.setEnabled(True) + + def closeEvent(self, evt): + self.thread.quit() + self.thread.wait() + + if self.success: + appname = self.updater.get_zsync()[2] + if self._appfile is not None: + appimage_path = appname + old_version = Path(self._appfile).rename(self._appfile+'.old') + appimage_path = Path(appimage_path).replace(self._appfile) + else: + appimage_path = Path().cwd() / appname + # rename to version-agnostic name + appimage_path = appimage_path.rename('NMReval.AppImage') + + _ = QtWidgets.QMessageBox.information(self, 'Complete', + f'New AppImage available at