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