From 925579b9e03f083c001bcb811efb943788f886af Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 7 Mar 2026 23:57:23 +0100 Subject: [PATCH 01/13] fix: switch updater to silent instead of verysilent and open new exe after installation --- src/core/interface/gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 332f153..3eb224c 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -144,7 +144,8 @@ def download_update(latest_version): f.write(response.content) if not os.path.exists(new_filename): raise FileNotFoundError("Executable not found") - subprocess.run([new_filename, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/SP-"]) + subprocess.run([new_filename, "/SILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/RESTARTAPPLICATIONS", "/SP-"]) + subprocess.Popen(["SoftwareManager.exe"]) time.sleep(0.5) msg = QMessageBox() From dbd5e7967855a868c10378af03489b1d571763d7 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 8 Mar 2026 00:08:37 +0100 Subject: [PATCH 02/13] trigger github workflow to test updater From e47efab9011bbbd9894c12b0155125e6aeb0de3f Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 8 Mar 2026 00:25:32 +0100 Subject: [PATCH 03/13] fix: remove restartapplications flag in updater --- src/core/interface/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 3eb224c..3754ffc 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -144,7 +144,7 @@ def download_update(latest_version): f.write(response.content) if not os.path.exists(new_filename): raise FileNotFoundError("Executable not found") - subprocess.run([new_filename, "/SILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/RESTARTAPPLICATIONS", "/SP-"]) + subprocess.run([new_filename, "/SILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/SP-"]) subprocess.Popen(["SoftwareManager.exe"]) time.sleep(0.5) From 78a47e89d37d0a36240a6cccc9346d4ebb0839a5 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:29:10 +0100 Subject: [PATCH 04/13] fix: remove broken image path handling from settings dialog and paths tab --- src/core/interface/dialogs/settings.py | 34 +++++++++++++------------- src/core/interface/utils/tabhelper.py | 4 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/core/interface/dialogs/settings.py b/src/core/interface/dialogs/settings.py index bf2f0e8..d9b016e 100644 --- a/src/core/interface/dialogs/settings.py +++ b/src/core/interface/dialogs/settings.py @@ -121,24 +121,24 @@ def settings_dialog(self): # IMAGE PATH # ############### - image_path_container = QWidget() - image_path_layout = QHBoxLayout() + # image_path_container = QWidget() + # image_path_layout = QHBoxLayout() - image_path = QLineEdit() - image_path_layout.addWidget(QLabel("Image Path (requires restart):")) - image_path_layout.addWidget(image_path) - image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - image_path_container.setLayout(image_path_layout) - image_path.setText(state.image_path) + # image_path = QLineEdit() + # image_path_layout.addWidget(QLabel("Image Path (requires restart):")) + # image_path_layout.addWidget(image_path) + # image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + # image_path_container.setLayout(image_path_layout) + # image_path.setText(state.image_path) - def browse_image_path(): - file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0] - if file_path: - image_path.setText(file_path) + # def browse_image_path(): + # file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0] + # if file_path: + # image_path.setText(file_path) - browse_button = QPushButton("📁") - image_path_layout.addWidget(browse_button) - browse_button.clicked.connect(browse_image_path) + # browse_button = QPushButton("📁") + # image_path_layout.addWidget(browse_button) + # browse_button.clicked.connect(browse_image_path) ################## # SPEED LIMITING # @@ -246,7 +246,7 @@ def settings_dialog(self): download_path.text(), down_speed_limit.value(), up_speed_limit.value(), - image_path.text(), + # image_path.text(), autoresume_checkbox.isChecked(), max_connections.value(), max_downloads.value(), @@ -258,7 +258,7 @@ def settings_dialog(self): self.tabs = QTabWidget() self.tab1 = general_tab("General", autoresume_container, update_checkbox_container, transparent_window_container, self.tabs) - self.tab2 = paths_tab("Paths", download_path_container, image_path_container, self.tabs) + self.tab2 = paths_tab("Paths", download_path_container, self.tabs) self.tab3 = network_tab("Network", interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container, self.tabs) dialog.layout().addWidget(self.tabs) diff --git a/src/core/interface/utils/tabhelper.py b/src/core/interface/utils/tabhelper.py index 74c4dd1..2d6c53f 100644 --- a/src/core/interface/utils/tabhelper.py +++ b/src/core/interface/utils/tabhelper.py @@ -30,11 +30,11 @@ def general_tab(title, autoresume, update_checkbox, transparent_window, tabs): tabs.addTab(tab, title) return tab -def paths_tab(title, download_path, image_path, tabs): +def paths_tab(title, download_path, tabs): tab = QWidget() layout = QVBoxLayout() layout.addWidget(download_path) - layout.addWidget(image_path) + # layout.addWidget(image_path) layout.addStretch() tab.setLayout(layout) tabs.addTab(tab, title) From a5190f151c277fca36c0740e2674bb8147e806f0 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 8 Mar 2026 00:41:37 +0100 Subject: [PATCH 05/13] fix: temporarily remove subprocess.Popen to stop opening softwaremanager.exe before update finished --- src/core/interface/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 3754ffc..2855f84 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -145,7 +145,7 @@ def download_update(latest_version): if not os.path.exists(new_filename): raise FileNotFoundError("Executable not found") subprocess.run([new_filename, "/SILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/SP-"]) - subprocess.Popen(["SoftwareManager.exe"]) + # subprocess.Popen(["SoftwareManager.exe"]) time.sleep(0.5) msg = QMessageBox() From 891c5a86c1d69d9a2cdb57cd5d22c12403bb8e29 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 01:55:23 +0100 Subject: [PATCH 06/13] feat: enhance update process with progress dialog and install path detection --- installer.iss | 6 ++ src/core/interface/gui.py | 131 ++++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/installer.iss b/installer.iss index bf95032..60e6034 100644 --- a/installer.iss +++ b/installer.iss @@ -27,24 +27,30 @@ AppId={{8F2E4B6A-1C3D-4E5F-9A7B-0D8E6F2C4A1B} AppName={#MyAppName} AppVersion={#MyAppVersion} +AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL}/issues +AppUpdatesURL={#MyAppURL}/releases DefaultDirName={autopf}\{#MyAppName} DefaultGroupName={#MyAppName} AllowNoIcons=yes +LicenseFile=LICENSE OutputDir={#MyOutputDir} OutputBaseFilename={#MyOutputFilename} Compression=lzma2 SolidCompression=yes WizardStyle=modern +DisableWelcomePage=no PrivilegesRequired=lowest PrivilegesRequiredOverridesAllowed=dialog UninstallDisplayIcon={app}\{#MyAppExeName} +UninstallDisplayName={#MyAppName} ArchitecturesAllowed=x64compatible ArchitecturesInstallIn64BitMode=x64compatible CloseApplications=yes RestartApplications=yes +ShowLanguageDialog=auto [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 2855f84..485d3b1 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -27,10 +27,10 @@ import requests as r import os import subprocess import libtorrent as lt -import time import sys import json import base64 +import winreg from core.utils.logging.logs import consoleLog, remove_download_log, flush_log_buffer from core.utils.general.wrappers import run_thread from core.utils.data.state import state @@ -134,26 +134,80 @@ SVG_PAUSE = ' 0: + progress.setValue(int(downloaded * 100 / total)) + QtWidgets.QApplication.processEvents() + + with open(new_filename, "wb") as f: + for chunk in chunks: + f.write(chunk) + + if not os.path.exists(new_filename): + progress.close() + raise FileNotFoundError("Executable not found") + + progress.setLabelText("Installing update...") + progress.setValue(100) + QtWidgets.QApplication.processEvents() + + proc = subprocess.Popen([new_filename, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/SP-", "/CLOSEAPPLICATIONS", "/NORESTART"]) + while proc.poll() is None: + QtWidgets.QApplication.processEvents() + import time + time.sleep(0.1) + + progress.close() + + try: + os.remove(new_filename) + except OSError: + pass + + install_path = _find_install_path() + if not install_path: + raise FileNotFoundError("Could not find SoftwareManager install location in registry") + subprocess.Popen([os.path.join(install_path, "SoftwareManager.exe")]) sys.exit(0) @@ -163,11 +217,13 @@ def windowCloseHelper(): class MainWindow(QtWidgets.QMainWindow, QWidget): log_signal = Signal(str) # Thread-safe signal for logging + search_finished_signal = Signal() # Thread-safe signal for search completion def __init__(self): super().__init__() MainWindow._instance = self self.log_signal.connect(self._on_log_signal) + self.search_finished_signal.connect(self._on_search_finished) pixmap = QPixmap() image_data = base64.b64decode(logo_base64) @@ -217,7 +273,16 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.searchbar.setPlaceholderText("Search for software...") self.searchbar.setClearButtonEnabled(True) self.searchbar.setMinimumHeight(30) - self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter + self.searchbar.returnPressed.connect(self._start_search) + + self._spinner_label = QLabel() + self._spinner_label.setFixedSize(20, 20) + self._spinner_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._spinner_label.hide() + self._spinner_angle = 0 + self._spinner_timer = QTimer() + self._spinner_timer.setInterval(80) + self._spinner_timer.timeout.connect(self._update_spinner) self.dlbutton = QtWidgets.QPushButton("Download") self.dlbutton.setCursor(Qt.CursorShape.PointingHandCursor) @@ -300,7 +365,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): container = QWidget() containerLayout = QVBoxLayout() - containerLayout.addWidget(self.searchbar) + + search_row = QHBoxLayout() + search_row.addWidget(self.searchbar) + search_row.addWidget(self._spinner_label) + containerLayout.addLayout(search_row) containerLayout.addWidget(state.tracker_list[state.tracker]) class DownloadModel(QAbstractTableModel): @@ -663,6 +732,32 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.context_menu.addAction("Cancel Download", self.cancelDownloadAction) self.context_menu.addAction("Delete File", self.deleteFileAction) + def _start_search(self): + self._spinner_label.show() + self._spinner_angle = 0 + self._spinner_timer.start() + self._update_spinner() + self.searchbar.setEnabled(False) + def _search_thread(): + try: + return_pressed(self) + finally: + self.search_finished_signal.emit() + run_thread(threading.Thread(target=_search_thread)) + + def _on_search_finished(self): + self._spinner_timer.stop() + self._spinner_label.hide() + self.searchbar.setEnabled(True) + self.searchbar.setFocus() + + def _update_spinner(self): + frames = ["◐", "◓", "◑", "◒"] + idx = (self._spinner_angle // 1) % len(frames) + self._spinner_label.setText(frames[idx]) + self._spinner_label.setStyleSheet("font-size: 16px; color: gray;") + self._spinner_angle += 1 + @staticmethod def add_log(text): if MainWindow._instance: From ff0e1a07981fcab958dd08cb2660b680bb07f795 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 01:55:59 +0100 Subject: [PATCH 07/13] trigger workflow" From 5b9bdd2a2aa296cd77d5ba16a4df534f5fa6edd2 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 02:16:16 +0100 Subject: [PATCH 08/13] feat: update download process to use temporary installer path and improve silent installation --- installer.iss | 1 + src/core/interface/gui.py | 30 ++++++++++-------------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/installer.iss b/installer.iss index 60e6034..8d58c9e 100644 --- a/installer.iss +++ b/installer.iss @@ -68,3 +68,4 @@ Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: de [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent +Filename: "{app}\{#MyAppExeName}"; Flags: nowait skipifnotsilent diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 485d3b1..f8ebc80 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -153,8 +153,12 @@ def _find_install_path(): def download_update(latest_version): + import tempfile + import time + new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows-setup.exe" url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows-setup.exe" + installer_path = os.path.join(tempfile.gettempdir(), new_filename) progress = QtWidgets.QProgressDialog("Downloading installer...", None, 0, 0) progress.setWindowTitle("Updating") @@ -179,11 +183,11 @@ def download_update(latest_version): progress.setValue(int(downloaded * 100 / total)) QtWidgets.QApplication.processEvents() - with open(new_filename, "wb") as f: + with open(installer_path, "wb") as f: for chunk in chunks: f.write(chunk) - if not os.path.exists(new_filename): + if not os.path.exists(installer_path): progress.close() raise FileNotFoundError("Executable not found") @@ -191,24 +195,10 @@ def download_update(latest_version): progress.setValue(100) QtWidgets.QApplication.processEvents() - proc = subprocess.Popen([new_filename, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/SP-", "/CLOSEAPPLICATIONS", "/NORESTART"]) - while proc.poll() is None: - QtWidgets.QApplication.processEvents() - import time - time.sleep(0.1) - - progress.close() - - try: - os.remove(new_filename) - except OSError: - pass - - install_path = _find_install_path() - if not install_path: - raise FileNotFoundError("Could not find SoftwareManager install location in registry") - subprocess.Popen([os.path.join(install_path, "SoftwareManager.exe")]) - + # Launch installer and exit — the installer will close this process, + # install the update, and relaunch the app via its [Run] section. + subprocess.Popen([installer_path, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/SP-", "/CLOSEAPPLICATIONS"]) + time.sleep(1) sys.exit(0) From 9e0ee2a36d0bce0b54a29e1449ffb9faf45b22d4 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 02:16:23 +0100 Subject: [PATCH 09/13] trigger workflow From 12df32f72a11ac4cdc65ae580c7a4011853b9139 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:15:51 +0100 Subject: [PATCH 10/13] feat: remove unused install path detection function and clean up update process --- src/core/interface/gui.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index f8ebc80..47739e0 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -134,24 +134,6 @@ SVG_PAUSE = ' Date: Sun, 8 Mar 2026 13:19:39 +0100 Subject: [PATCH 11/13] feat: remove ugly spinner label and related functionality from search process --- src/core/interface/gui.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 47739e0..8634e4e 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -245,15 +245,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.searchbar.setMinimumHeight(30) self.searchbar.returnPressed.connect(self._start_search) - self._spinner_label = QLabel() - self._spinner_label.setFixedSize(20, 20) - self._spinner_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self._spinner_label.hide() - self._spinner_angle = 0 - self._spinner_timer = QTimer() - self._spinner_timer.setInterval(80) - self._spinner_timer.timeout.connect(self._update_spinner) - self.dlbutton = QtWidgets.QPushButton("Download") self.dlbutton.setCursor(Qt.CursorShape.PointingHandCursor) self.libraryList = QListWidget() @@ -338,7 +329,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): search_row = QHBoxLayout() search_row.addWidget(self.searchbar) - search_row.addWidget(self._spinner_label) containerLayout.addLayout(search_row) containerLayout.addWidget(state.tracker_list[state.tracker]) @@ -703,10 +693,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.context_menu.addAction("Delete File", self.deleteFileAction) def _start_search(self): - self._spinner_label.show() - self._spinner_angle = 0 - self._spinner_timer.start() - self._update_spinner() self.searchbar.setEnabled(False) def _search_thread(): try: @@ -716,18 +702,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): run_thread(threading.Thread(target=_search_thread)) def _on_search_finished(self): - self._spinner_timer.stop() - self._spinner_label.hide() self.searchbar.setEnabled(True) self.searchbar.setFocus() - def _update_spinner(self): - frames = ["◐", "◓", "◑", "◒"] - idx = (self._spinner_angle // 1) % len(frames) - self._spinner_label.setText(frames[idx]) - self._spinner_label.setStyleSheet("font-size: 16px; color: gray;") - self._spinner_angle += 1 - @staticmethod def add_log(text): if MainWindow._instance: From 9ba11bfa78296ba3dfab9e42e9f07e4fafa7c28a Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:32:30 +0100 Subject: [PATCH 12/13] feat: enhance error handling and logging in various modules; improve timeout settings for network requests / fix basic issues --- src/core/data/scrapers/uztracker.py | 4 +++- src/core/interface/dialogs/settings.py | 2 -- src/core/interface/gui.py | 15 +++++---------- src/core/interface/utils/searchhelper.py | 2 +- src/core/network/libtorrent_int.py | 22 ++++++++++++++-------- src/core/utils/data/tracker.py | 2 +- src/core/utils/logging/loghandler.py | 5 ++++- src/core/utils/logging/logs.py | 2 +- src/core/utils/network/update_checker.py | 4 ++-- src/main.py | 3 ++- 10 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/core/data/scrapers/uztracker.py b/src/core/data/scrapers/uztracker.py index 956629f..ac2aa23 100644 --- a/src/core/data/scrapers/uztracker.py +++ b/src/core/data/scrapers/uztracker.py @@ -16,7 +16,9 @@ def scrape_uztracker(query): links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_')) for link in links: - theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) + theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) + if not theme_link or not theme_link.b: + continue url = urljoin(base_url, theme_link['href']) title = theme_link.b.text author_link = link.find('a', class_="med") diff --git a/src/core/interface/dialogs/settings.py b/src/core/interface/dialogs/settings.py index d9b016e..550df37 100644 --- a/src/core/interface/dialogs/settings.py +++ b/src/core/interface/dialogs/settings.py @@ -224,8 +224,6 @@ def settings_dialog(self): else: interface_select.setCurrentIndex(0) - interface_select.setFixedWidth(180) - interface_select.setFixedHeight(30) interface_select.setFixedWidth(180) interface_select.setFixedHeight(30) interface_layout.addWidget(interface_select) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 8634e4e..bf82784 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -30,7 +30,6 @@ import libtorrent as lt import sys import json import base64 -import winreg from core.utils.logging.logs import consoleLog, remove_download_log, flush_log_buffer from core.utils.general.wrappers import run_thread from core.utils.data.state import state @@ -157,17 +156,13 @@ def download_update(latest_version): response = r.get(url, allow_redirects=True, stream=True) total = int(response.headers.get("content-length", 0)) downloaded = 0 - chunks = [] - for chunk in response.iter_content(chunk_size=65536): - chunks.append(chunk) - downloaded += len(chunk) - if total > 0: - progress.setValue(int(downloaded * 100 / total)) - QtWidgets.QApplication.processEvents() - with open(installer_path, "wb") as f: - for chunk in chunks: + for chunk in response.iter_content(chunk_size=65536): f.write(chunk) + downloaded += len(chunk) + if total > 0: + progress.setValue(int(downloaded * 100 / total)) + QtWidgets.QApplication.processEvents() if not os.path.exists(installer_path): progress.close() diff --git a/src/core/interface/utils/searchhelper.py b/src/core/interface/utils/searchhelper.py index 9dae1a5..eda868a 100644 --- a/src/core/interface/utils/searchhelper.py +++ b/src/core/interface/utils/searchhelper.py @@ -50,7 +50,7 @@ def return_pressed(self): elif state.tracker is not None: state.posts = scrapers[state.tracker](search_text) - if state.posts == []: + if not state.posts: consoleLog(f"No Results for {search_text}") state.tracker_list[state.tracker].clear() self.show_empty_results(True) diff --git a/src/core/network/libtorrent_int.py b/src/core/network/libtorrent_int.py index d19e4fb..9160b66 100644 --- a/src/core/network/libtorrent_int.py +++ b/src/core/network/libtorrent_int.py @@ -74,10 +74,13 @@ def add_download(magnet_uri, dl_path=state.download_path): consoleLog("Skipping Downloading, download already running... ") return False - magnetdl = lt.parse_magnet_uri(magnet_uri) - magnetdl.save_path = dl_path - - download = state.dl_session.add_torrent(magnetdl) + try: + magnetdl = lt.parse_magnet_uri(magnet_uri) + magnetdl.save_path = dl_path + download = state.dl_session.add_torrent(magnetdl) + except Exception as e: + consoleLog(f"Failed to add torrent: {e}") + return False state.active_downloads[magnet_uri] = download consoleLog(f"Added {magnet_uri} to downloads") @@ -95,10 +98,13 @@ def add_seed(magnet_uri, file_path): consoleLog("Already seeding this torrent") return False - magnetdl = lt.parse_magnet_uri(magnet_uri) - magnetdl.save_path = os.path.dirname(file_path) - - handle = state.dl_session.add_torrent(magnetdl) + try: + magnetdl = lt.parse_magnet_uri(magnet_uri) + magnetdl.save_path = os.path.dirname(file_path) + handle = state.dl_session.add_torrent(magnetdl) + except Exception as e: + consoleLog(f"Failed to add seed: {e}") + return False state.active_downloads[magnet_uri] = handle state.seeded_magnets.add(magnet_uri) return True diff --git a/src/core/utils/data/tracker.py b/src/core/utils/data/tracker.py index 962fa38..79ed98f 100644 --- a/src/core/utils/data/tracker.py +++ b/src/core/utils/data/tracker.py @@ -30,7 +30,7 @@ def get_item_url(item, posts, post_titles): # softwarelist currentitem, post lis def get_magnet_link(post_url): try: - response = requests.get(post_url) # eventually impl. cloudscraper + response = requests.get(post_url, timeout=15) # eventually impl. cloudscraper consoleLog("Sent Request to retrieve Magnet Link...") response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') diff --git a/src/core/utils/logging/loghandler.py b/src/core/utils/logging/loghandler.py index b48c81d..5d48d10 100644 --- a/src/core/utils/logging/loghandler.py +++ b/src/core/utils/logging/loghandler.py @@ -22,7 +22,10 @@ def check_downloads(downloads): for download in downloads: if os.path.exists(download.path): consoleLog(f"Existing Download: {download.title}") - seed_magnet(download.magnet_uri, download.path) + try: + seed_magnet(download.magnet_uri, download.path) + except Exception as e: + consoleLog(f"Failed to seed {download.title}: {e}") else: consoleLog(f"Inexistent Download: {download.title}") remove_download_log(download.magnet_uri) diff --git a/src/core/utils/logging/logs.py b/src/core/utils/logging/logs.py index b8186d2..c78fd7c 100644 --- a/src/core/utils/logging/logs.py +++ b/src/core/utils/logging/logs.py @@ -89,7 +89,7 @@ def _remove_download_log_inner(magnet_uri) -> DownloadList: magnet_link = (magnet_uri or "").strip() - title = next((getattr(d, 'title', 'Unknown') for d in downloads if (getattr(d, 'magnet_uri', None) or '').strip() == magnet_link or (getattr(d, 'url', None) or '').strip() == magnet_link)) + title = next((getattr(d, 'title', 'Unknown') for d in downloads if (getattr(d, 'magnet_uri', None) or '').strip() == magnet_link or (getattr(d, 'url', None) or '').strip() == magnet_link), 'Unknown') downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link] consoleLog(f"Removed {title} from Log File") diff --git a/src/core/utils/network/update_checker.py b/src/core/utils/network/update_checker.py index 31955a9..a250884 100644 --- a/src/core/utils/network/update_checker.py +++ b/src/core/utils/network/update_checker.py @@ -4,11 +4,11 @@ from core.utils.logging.logs import consoleLog def check_for_updates(): url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases" - response = requests.get(url) + response = requests.get(url, timeout=15) if response.status_code != 200: consoleLog(f"Failed to fetch releases: {response.status_code}") - exit(1) + return None, None releases = response.json() releases.sort(key=lambda r: r["published_at"], reverse=True) diff --git a/src/main.py b/src/main.py index 097f67d..8101f87 100644 --- a/src/main.py +++ b/src/main.py @@ -56,7 +56,8 @@ def main(): consoleLog("Started Thread: check_completed") run_thread(threading.Thread(target=check_deleted_files, args=(shutdown_event,), daemon=True)) consoleLog("Started Thread: check_deleted_files") - check_downloads(downloads) + run_thread(threading.Thread(target=check_downloads, args=(downloads,))) + consoleLog("Started Thread: check_downloads") elapsed = time.perf_counter() - start_time consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI") run_gui() From 66217ff9b685f9316a278fd580d06ba79b0071a2 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:58:23 +0100 Subject: [PATCH 13/13] feat: update settings dialog to handle image path as None; improve table item focus styles --- src/core/interface/dialogs/settings.py | 2 +- src/core/interface/gui.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/interface/dialogs/settings.py b/src/core/interface/dialogs/settings.py index 550df37..a50efdc 100644 --- a/src/core/interface/dialogs/settings.py +++ b/src/core/interface/dialogs/settings.py @@ -244,7 +244,7 @@ def settings_dialog(self): download_path.text(), down_speed_limit.value(), up_speed_limit.value(), - # image_path.text(), + None, autoresume_checkbox.isChecked(), max_connections.value(), max_downloads.value(), diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index bf82784..67941d2 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -84,10 +84,16 @@ def _table_stylesheet(view_type="QTableWidget"): {view_type}::item {{ border-bottom: 1px solid {c["border"]}; padding: 6px 14px; + outline: none; {color_rule} }} {view_type}::item:selected {{ background: {c["selected"]}; + outline: none; + }} + {view_type}::item:focus {{ + outline: none; + border: none; }} QHeaderView {{ background: transparent;