From 9970086901ef504339ca631a6d3ce423f2d6189d Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 18 Jan 2026 02:03:31 +0100 Subject: [PATCH 1/5] WIP: add pause/resume button in downloads tab --- core/interface/gui.py | 88 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index db55274..8e05dfd 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -1,5 +1,5 @@ from PySide6 import QtWidgets -from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal +from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent from PySide6.QtWidgets import ( QLineEdit, QTableView, @@ -16,6 +16,10 @@ from PySide6.QtWidgets import ( QMessageBox, QTableWidget, QTextEdit, + QStyle, + QStyleOptionButton, + QStyledItemDelegate, + QApplication, ) from PySide6.QtGui import QIcon, QAction, QCloseEvent, QImage, QPixmap @@ -54,11 +58,11 @@ def download_update(latest_version): time.sleep(0.5) msg = QMessageBox() - msg.setIcon(QMessageBox.Information) + msg.setIcon(QMessageBox.Icon.Information) msg.setWindowTitle("New Version") msg.setText("New version installed.") msg.setInformativeText("Please remove the old exe.") - msg.setStandardButtons(QMessageBox.Ok) + msg.setStandardButtons(QMessageBox.StandardButton.Ok) sys.exit(0) @@ -109,14 +113,14 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): if assets: msg = QMessageBox() - msg.setIcon(QMessageBox.Information) + msg.setIcon(QMessageBox.Icon.Information) msg.setWindowTitle("Update Available") msg.setText("A new version is available.") msg.setInformativeText("Press Ok to download the update.") - msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Ignore) + msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Ignore) response = msg.exec_() - if response == QMessageBox.Ok: + if response == QMessageBox.StandardButton.Ok: download_update(latest_version) self.setWindowTitle("Software Manager") @@ -175,7 +179,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): class DownloadModel(QAbstractTableModel): def __init__(self): super().__init__() - self.headers = ["Name", "Status", "Progress", "Speed", "Size", "Total Size"] + self.headers = ["Action", "Name", "Status", "Progress", "Speed", "Size", "Total Size"] def rowCount(self, parent=QModelIndex()): return len(state.downloads) @@ -189,24 +193,65 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): return None def data(self, index, role=Qt.DisplayRole): + col = index.column() + download = state.downloads[index.row()] if role == Qt.DisplayRole: - download = state.downloads[index.row()] col = index.column() if col == 0: - return download.name + return "Resume" if download.is_paused else "Pause" elif col == 1: - return getattr(download, 'status', 'Downloading') + return download.name elif col == 2: - return f"{int(download.progress)}%" + return getattr(download, 'status', 'Downloading') elif col == 3: - return f"{download.download_speed_string()}" + return f"{int(download.progress)}%" elif col == 4: - pass + return f"{download.download_speed_string()}" elif col == 5: + pass + elif col == 6: return f"{download.total_length_string()}" + + if role == Qt.UserRole and col == 0: + return download.is_paused + return None + def toggle_pause_resume(self, row): + download = state.downloads[row] + if download.is_paused: + download.resume() + else: + download.pause() + idx = self.index(row, 0) + self.dataChanged.emit(idx, idx, [Qt.DisplayRole, Qt.UserRole]) + + class PauseResumeDelegate(QStyledItemDelegate): + clicked = Signal(int) + + def paint(self, painter, option, index): + if index.column() != 0: + super().paint(painter, option, index) + return + + paused = index.data(Qt.UserRole) + + button = QStyleOptionButton() + button.rect = option.rect + button.text = "Resume" if paused else "Pause" + button.state = QStyle.State_Enabled + + QApplication.style().drawControl( + QStyle.CE_PushButton, button, painter + ) + + def editorEvent(self, event, model, option, index): + if index.column() == 0 and event.type() == QEvent.Type.MouseButtonPress: + self.clicked.emit(index.row()) + return True + return False + self.download_model = DownloadModel() self.downloadList.setModel(self.download_model) self.downloadList.horizontalHeader().setStretchLastSection(False) @@ -215,6 +260,21 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.downloadList.setAttribute(Qt.WA_TranslucentBackground) self.downloadList.viewport().setAttribute(Qt.WA_TranslucentBackground) + download_model = self.download_model + + def on_pause_resume_clicked(row): + download_model.toggle_pause_resume(row) + + download = state.downloads[row] + if download.is_paused: + download.pause() + else: + download.resume() + + delegate = PauseResumeDelegate(self.downloadList) + self.downloadList.setItemDelegateForColumn(0, delegate) + delegate.clicked.connect(on_pause_resume_clicked) + # download button triggers self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.qtablewidget.currentItem(), state.posts, state.post_titles)))) @@ -336,4 +396,4 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): - \ No newline at end of file + From 606c70efcc2220dbf95f138fda960a72a595f37f Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 18 Jan 2026 21:39:40 +0100 Subject: [PATCH 2/5] add unicode icons for pause/resume and fix action column size --- core/interface/gui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 8e05dfd..ddc43dc 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -199,7 +199,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): col = index.column() if col == 0: - return "Resume" if download.is_paused else "Pause" + return "▶︎" if download.is_paused else "⏸︎" elif col == 1: return download.name elif col == 2: @@ -239,7 +239,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): button = QStyleOptionButton() button.rect = option.rect - button.text = "Resume" if paused else "Pause" + button.text = "▶︎" if paused else "⏸︎" button.state = QStyle.State_Enabled QApplication.style().drawControl( @@ -255,7 +255,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.download_model = DownloadModel() self.downloadList.setModel(self.download_model) self.downloadList.horizontalHeader().setStretchLastSection(False) - self.downloadList.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) + self.downloadList.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.downloadList.setSelectionBehavior(QTableView.SelectRows) self.downloadList.setAttribute(Qt.WA_TranslucentBackground) self.downloadList.viewport().setAttribute(Qt.WA_TranslucentBackground) From 83adbb027fba5fdc98234361db16361cbf8af41a Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Mon, 19 Jan 2026 13:49:20 +0100 Subject: [PATCH 3/5] fix aria2 connection refused errors caused by autoresume --- core/network/aria2_integration.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/network/aria2_integration.py b/core/network/aria2_integration.py index 9f0aba3..77fa6eb 100644 --- a/core/network/aria2_integration.py +++ b/core/network/aria2_integration.py @@ -4,6 +4,7 @@ from core.utils.data.state import state from core.utils.general.logs import update_download_completed_by_hash from core.utils.general.logs import consoleLog from plyer import notification +import socket import time import sys @@ -42,6 +43,21 @@ def aria2server(): stderr=subprocess.DEVNULL, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0 ) + + max_checks = 50 + for check in range(max_checks): + try: + sock = socket.socket() + sock.settimeout(0.1) + sock.connect(("localhost", 6800)) + sock.close() + break + except(ConnectionRefusedError, socket.timeout): + time.sleep(0.1) + else: + raise RuntimeError("Aria2 Server failed to start") + + consoleLog("Aria2 Server started") return aria2server def dlprogress(): From 83ff2720c8267876286501b11a129120ad478cbc Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Tue, 20 Jan 2026 10:43:02 +0100 Subject: [PATCH 4/5] add error handling for magnet uri being none --- core/network/aria2_wrapper.py | 7 +++++-- core/utils/general/loghandler.py | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/network/aria2_wrapper.py b/core/network/aria2_wrapper.py index fd1afbd..46361da 100644 --- a/core/network/aria2_wrapper.py +++ b/core/network/aria2_wrapper.py @@ -8,8 +8,11 @@ def start_client(): consoleLog("Started Aria2p") def add_magnet(uri): - aria2.add_magnet(uri) - consoleLog("Magnet URI added to Aria2") + if uri is not None and uri.startswith("magnet:?"): + aria2.add_magnet(uri) + consoleLog("Magnet URI added to Aria2") + else: + consoleLog(f"Invalid Magnet Link: {uri}") diff --git a/core/utils/general/loghandler.py b/core/utils/general/loghandler.py index adf5e71..f064e00 100644 --- a/core/utils/general/loghandler.py +++ b/core/utils/general/loghandler.py @@ -1,4 +1,3 @@ -from core.utils.data.state import state from core.utils.general.logs import consoleLog from core.utils.network.download import run_download_direct From d82008c9fe03577444e61da4fe243584d0a8236d Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Tue, 20 Jan 2026 11:26:49 +0100 Subject: [PATCH 5/5] fix no item in downloads text --- core/interface/gui.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index ddc43dc..50192b5 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -147,6 +147,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.downloadList = QTableView() self.emptyLibrary = QLabel("No items in library.") self.emptyDownload = QLabel("No items in downloads.") + self.emptyDownload.setAlignment(Qt.AlignCenter) self.consoleLog = QTextEdit() self.progressbar = QProgressBar() @@ -183,7 +184,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): def rowCount(self, parent=QModelIndex()): return len(state.downloads) - + def columnCount(self, parent=QModelIndex()): return len(self.headers) @@ -345,6 +346,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.download_timer.timeout.connect(lambda: run_thread(threading.Thread(target=self.download_list_update))) self.download_timer.start(500) + self.active_timer = QTimer() + self.active_timer.timeout.connect(self.show_empty_downloads) + self.active_timer.start(500) + @staticmethod def add_log(text): if MainWindow._instance: @@ -367,6 +372,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): if self.download_model: self.download_model.layoutAboutToBeChanged.emit() self.download_model.layoutChanged.emit() + def closeEvent(self, event: QCloseEvent): closehelper() @@ -384,8 +390,17 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.qtablewidget.hide() self.emptyResults.show() else: - self.emptyResults.hide() self.qtablewidget.show() + self.emptyResults.hide() + + def show_empty_downloads(self): + if len(state.downloads) > 0: + self.emptyDownload.hide() + self.downloadList.show() + else: + self.emptyDownload.show() + self.downloadList.hide() + # thank you claude