Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.
| 1 | import requests |
| 2 | import json |
| 3 | import os |
| 4 | import re |
| 5 | from PyQt5.QtWidgets import QMessageBox, QCheckBox |
| 6 | from PyQt5.QtCore import QThread, pyqtSignal |
| 7 | import webbrowser |
| 8 | |
| 9 | class UpdateChecker(QThread): |
| 10 | update_available = pyqtSignal(str, str) # version, download_url |
| 11 | error_occurred = pyqtSignal(str) |
| 12 | |
| 13 | CURRENT_VERSION = "1.4.03" # Versione corrente |
| 14 | GITHUB_API_URL = "https://api.github.com/repos/seregonwar/PkgToolBox/releases/latest" |
| 15 | REQUEST_HEADERS = { |
| 16 | "Accept": "application/vnd.github+json", |
| 17 | "User-Agent": "PkgToolBox/" + CURRENT_VERSION, |
| 18 | } |
| 19 | |
| 20 | def __init__(self, parent=None): |
| 21 | super().__init__(parent) |
| 22 | self.parent = parent |
| 23 | |
| 24 | def run(self): |
| 25 | """Check for updates in background""" |
| 26 | try: |
| 27 | response = requests.get(self.GITHUB_API_URL, headers=self.REQUEST_HEADERS, timeout=10) |
| 28 | response.raise_for_status() |
| 29 | |
| 30 | release_info = response.json() |
| 31 | tag = str(release_info.get('tag_name', '') or '') |
| 32 | latest_version = self._normalize_version(tag) |
| 33 | |
| 34 | if not latest_version: |
| 35 | raise ValueError("Invalid tag_name in release response") |
| 36 | |
| 37 | if self._compare_versions(latest_version, self.CURRENT_VERSION) > 0: |
| 38 | download_url = release_info.get('html_url') or "https://github.com/seregonwar/PkgToolBox/releases" |
| 39 | self.update_available.emit(latest_version, download_url) |
| 40 | except Exception as e: |
| 41 | self.error_occurred.emit(str(e)) |
| 42 | |
| 43 | def _normalize_version(self, tag: str) -> str: |
| 44 | """Normalize a Git tag (e.g. 'v1.4.3' or '1.4.3-beta') to numeric '1.4.3'.""" |
| 45 | tag = tag.strip() |
| 46 | if tag.lower().startswith('v'): |
| 47 | tag = tag[1:] |
| 48 | # Keep only digits and dots at the start: 1.2.3 from 1.2.3-beta |
| 49 | m = re.match(r"(\d+(?:\.\d+){0,3})", tag) |
| 50 | return m.group(1) if m else "" |
| 51 | |
| 52 | def _compare_versions(self, version1, version2): |
| 53 | """Compare version strings like '1.4.3'. Returns 1, 0, -1.""" |
| 54 | def parts(v): |
| 55 | return [int(p) for p in v.split('.') if p.isdigit() or p.isnumeric()] |
| 56 | |
| 57 | v1_parts = parts(version1) |
| 58 | v2_parts = parts(version2) |
| 59 | |
| 60 | max_len = max(len(v1_parts), len(v2_parts)) |
| 61 | v1_parts += [0] * (max_len - len(v1_parts)) |
| 62 | v2_parts += [0] * (max_len - len(v2_parts)) |
| 63 | |
| 64 | for a, b in zip(v1_parts, v2_parts): |
| 65 | if a > b: |
| 66 | return 1 |
| 67 | if a < b: |
| 68 | return -1 |
| 69 | return 0 |
| 70 | |
| 71 | class UpdateDialog(QMessageBox): |
| 72 | def __init__(self, version, download_url, parent=None): |
| 73 | super().__init__(parent) |
| 74 | self.download_url = download_url |
| 75 | |
| 76 | self.setWindowTitle("Update Available") |
| 77 | self.setText(f"A new version ({version}) is available!") |
| 78 | self.setInformativeText("Would you like to download it now?") |
| 79 | self.setStandardButtons(QMessageBox.Yes | QMessageBox.No) |
| 80 | self.setDefaultButton(QMessageBox.Yes) |
| 81 | |
| 82 | # 'Don't show again' checkbox |
| 83 | dont_show_cb = QCheckBox("Don't show this again") |
| 84 | self.setCheckBox(dont_show_cb) |
| 85 | |
| 86 | self.buttonClicked.connect(self.handle_click) |
| 87 | |
| 88 | def handle_click(self, button): |
| 89 | if button == self.button(QMessageBox.Yes): |
| 90 | webbrowser.open(self.download_url) |
| 91 | |
| 92 | # Salva la preferenza se selezionata |
| 93 | if self.checkBox().isChecked(): |
| 94 | self.save_preference() |
| 95 | |
| 96 | def save_preference(self): |
| 97 | """Save user preference to not show update dialog""" |
| 98 | config_dir = os.path.join(os.path.expanduser("~"), ".pkgtoolbox") |
| 99 | config_file = os.path.join(config_dir, "update_preferences.json") |
| 100 | |
| 101 | os.makedirs(config_dir, exist_ok=True) |
| 102 | |
| 103 | with open(config_file, 'w') as f: |
| 104 | json.dump({"skip_updates": True}, f) |