From 86bf9b39d0be5c11c3b00d5159d14d603e748b34 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Sun, 27 Mar 2022 21:06:03 -0500 Subject: [PATCH] Add update check function and call from about - Implement a update check against the GitHub releases via the api - Add semantic-version dependency - Add automatic check when opening about dialog --- core/util.py | 37 ++++++++++++++++ pkg/debian/control | 2 +- qtlib/about_box.py | 98 ++++++++++++++++++------------------------ qtlib/locale/qtlib.pot | 18 ++++++-- requirements.txt | 3 +- setup.cfg | 1 + 6 files changed, 99 insertions(+), 60 deletions(-) diff --git a/core/util.py b/core/util.py index 7ae750ba..4c3feef3 100644 --- a/core/util.py +++ b/core/util.py @@ -7,6 +7,12 @@ import time import sys import os +import urllib.request +import urllib.error +import json +import semantic_version +import logging +from typing import Union from hscommon.util import format_time_decimal @@ -64,3 +70,34 @@ def fix_surrogate_encoding(s, encoding="utf-8"): def executable_folder(): return os.path.dirname(os.path.abspath(sys.argv[0])) + + +def check_for_update(current_version: str, include_prerelease: bool = False) -> Union[None, dict]: + request = urllib.request.Request( + "https://api.github.com/repos/arsenetar/dupeguru/releases", + headers={"Accept": "application/vnd.github.v3+json"}, + ) + try: + with urllib.request.urlopen(request) as response: + if response.status != 200: + logging.warn(f"Error retriving updates. Status: {response.status}") + return None + try: + response_json = json.loads(response.read()) + except json.JSONDecodeError as ex: + logging.warn(f"Error parsing updates. {ex.msg}") + return None + except urllib.error.URLError as ex: + logging.warn(f"Error retriving updates. {ex.reason}") + return None + new_version = semantic_version.Version(current_version) + new_url = None + for release in response_json: + release_version = semantic_version.Version(release["name"]) + if new_version < release_version and (include_prerelease or not release_version.prerelease): + new_version = release_version + new_url = release["html_url"] + if new_url is not None: + return {"version": new_version, "url": new_url} + else: + return None diff --git a/pkg/debian/control b/pkg/debian/control index 8370e7ae..acb75d1c 100644 --- a/pkg/debian/control +++ b/pkg/debian/control @@ -10,7 +10,7 @@ Vcs-Git: https://github.com/arsenetar/dupeguru.git Package: {pkgname} Architecture: {arch} -Depends: ${shlibs:Depends}, python3 (>=3.7), python3-pyqt5, python3-mutagen +Depends: ${shlibs:Depends}, python3 (>=3.7), python3-pyqt5, python3-mutagen, python3-sematic-version Provides: dupeguru-se, dupeguru-me, dupeguru-pe Replaces: dupeguru-se, dupeguru-me, dupeguru-pe Conflicts: dupeguru-se, dupeguru-me, dupeguru-pe diff --git a/qtlib/about_box.py b/qtlib/about_box.py index d9bf7163..1725f5e1 100644 --- a/qtlib/about_box.py +++ b/qtlib/about_box.py @@ -6,18 +6,11 @@ # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html -from PyQt5.QtCore import Qt, QCoreApplication +from PyQt5.QtCore import Qt, QCoreApplication, QTimer from PyQt5.QtGui import QPixmap, QFont -from PyQt5.QtWidgets import ( - QDialog, - QDialogButtonBox, - QSizePolicy, - QHBoxLayout, - QVBoxLayout, - QLabel, - QApplication, -) +from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy, QHBoxLayout, QVBoxLayout, QLabel +from core.util import check_for_update from qtlib.util import move_to_screen_center from hscommon.trans import trget @@ -31,61 +24,56 @@ class AboutBox(QDialog): self.app = app self._setupUi() - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.rejected.connect(self.reject) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) def _setupUi(self): self.setWindowTitle(tr("About {}").format(QCoreApplication.instance().applicationName())) - self.resize(400, 290) size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) - size_policy.setHorizontalStretch(0) - size_policy.setVerticalStretch(0) - size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(size_policy) - self.horizontalLayout = QHBoxLayout(self) - self.logoLabel = QLabel(self) - self.logoLabel.setPixmap(QPixmap(":/%s_big" % self.app.LOGO_NAME)) - self.horizontalLayout.addWidget(self.logoLabel) - self.verticalLayout = QVBoxLayout() - self.nameLabel = QLabel(self) + main_layout = QHBoxLayout(self) + logo_label = QLabel() + logo_label.setPixmap(QPixmap(":/%s_big" % self.app.LOGO_NAME)) + main_layout.addWidget(logo_label) + detail_layout = QVBoxLayout() + name_label = QLabel() font = QFont() font.setWeight(75) font.setBold(True) - self.nameLabel.setFont(font) - self.nameLabel.setText(QCoreApplication.instance().applicationName()) - self.verticalLayout.addWidget(self.nameLabel) - self.versionLabel = QLabel(self) - self.versionLabel.setText(tr("Version {}").format(QCoreApplication.instance().applicationVersion())) - self.verticalLayout.addWidget(self.versionLabel) - self.label_3 = QLabel(self) - self.verticalLayout.addWidget(self.label_3) - self.label_3.setText(tr("Licensed under GPLv3")) - self.label = QLabel(self) - font = QFont() - font.setWeight(75) - font.setBold(True) - self.label.setFont(font) - self.verticalLayout.addWidget(self.label) - self.buttonBox = QDialogButtonBox(self) - self.buttonBox.setOrientation(Qt.Horizontal) - self.buttonBox.setStandardButtons(QDialogButtonBox.Ok) - self.verticalLayout.addWidget(self.buttonBox) - self.horizontalLayout.addLayout(self.verticalLayout) + name_label.setFont(font) + name_label.setText(QCoreApplication.instance().applicationName()) + detail_layout.addWidget(name_label) + version_label = QLabel() + version_label.setText(tr("Version {}").format(QCoreApplication.instance().applicationVersion())) + detail_layout.addWidget(version_label) + self.update_label = QLabel(tr("Checking for updates...")) + self.update_label.setTextInteractionFlags(Qt.TextBrowserInteraction) + self.update_label.setOpenExternalLinks(True) + detail_layout.addWidget(self.update_label) + license_label = QLabel() + license_label.setText(tr("Licensed under GPLv3")) + detail_layout.addWidget(license_label) + spacer_label = QLabel() + spacer_label.setFont(font) + detail_layout.addWidget(spacer_label) + self.button_box = QDialogButtonBox() + self.button_box.setOrientation(Qt.Horizontal) + self.button_box.setStandardButtons(QDialogButtonBox.Ok) + detail_layout.addWidget(self.button_box) + main_layout.addLayout(detail_layout) + + def _check_for_update(self): + update = check_for_update(QCoreApplication.instance().applicationVersion(), include_prerelease=False) + if update is None: + self.update_label.setText(tr("No update available.")) + else: + self.update_label.setText( + tr('New version {} available, download here.').format(update["version"], update["url"]) + ) def showEvent(self, event): + self.update_label.setText(tr("Checking for updates...")) # have to do this here as the frameGeometry is not correct until shown move_to_screen_center(self) super().showEvent(event) - - -if __name__ == "__main__": - import sys - - app = QApplication([]) - QCoreApplication.setOrganizationName("Hardcoded Software") - QCoreApplication.setApplicationName("FooApp") - QCoreApplication.setApplicationVersion("1.2.3") - app.LOGO_NAME = "" - dialog = AboutBox(None, app) - dialog.show() - sys.exit(app.exec_()) + QTimer.singleShot(0, self._check_for_update) diff --git a/qtlib/locale/qtlib.pot b/qtlib/locale/qtlib.pot index 4e1cb93b..5ca7d6aa 100644 --- a/qtlib/locale/qtlib.pot +++ b/qtlib/locale/qtlib.pot @@ -4,18 +4,30 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" -#: qtlib\about_box.py:38 +#: qtlib\about_box.py:31 msgid "About {}" msgstr "" -#: qtlib\about_box.py:58 +#: qtlib\about_box.py:47 msgid "Version {}" msgstr "" -#: qtlib\about_box.py:62 +#: qtlib\about_box.py:49 qtlib\about_box.py:75 +msgid "Checking for updates..." +msgstr "" + +#: qtlib\about_box.py:54 msgid "Licensed under GPLv3" msgstr "" +#: qtlib\about_box.py:68 +msgid "No update available." +msgstr "" + +#: qtlib\about_box.py:71 +msgid "New version {} available, download here." +msgstr "" + #: qtlib\error_report_dialog.py:50 msgid "Error Report" msgstr "" diff --git a/requirements.txt b/requirements.txt index cbaedf95..51328385 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ polib>=1.1.0 mutagen>=1.44.0 distro>=1.5.0 PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' -pywin32>=228; sys_platform == 'win32' \ No newline at end of file +pywin32>=228; sys_platform == 'win32' +semantic-version>=2.0.0,<3.0.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 7b8aa758..9a1adaf8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,6 +34,7 @@ install_requires = distro>=1.5.0 PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' pywin32>=228; sys_platform == 'win32' + semantic-version>=2.0.0,<3.0.0 setup_requires = sphinx>=3.0.0 polib>=1.1.0