Compare commits

...

6 Commits

Author SHA1 Message Date
Vxrtrauter d82008c9fe fix no item in downloads text 2026-01-20 11:26:49 +01:00
Vxrtrauter 83ff2720c8 add error handling for magnet uri being none 2026-01-20 10:43:02 +01:00
Vxrtrauter 83adbb027f fix aria2 connection refused errors caused by autoresume 2026-01-19 13:49:20 +01:00
KeksNino 606c70efcc add unicode icons for pause/resume and fix action column size 2026-01-18 21:39:40 +01:00
KeksNino 9970086901 WIP: add pause/resume button in downloads tab 2026-01-18 02:03:31 +01:00
Vxrtrauter 0d631e0ab8 fix assets not showing up in build 2026-01-17 15:26:08 +01:00
6 changed files with 134 additions and 31 deletions
+8
View File
@@ -103,9 +103,13 @@ jobs:
run: |
pyinstaller --onefile --windowed \
--add-data "build_info.json;." \
--add-data "build_info.json;core" \
--add-data "build_info.json;core/interface" \
--add-data "deps/aria2c.exe;." \
--add-data "core/interface/assets;core/interface/assets" \
--icon "core/interface/assets/logo.png" \
--exclude-module PyQt6 \
--exclude-module PyQt5 \
--hidden-import=backports.tarfile \
--hidden-import=backports \
--hidden-import=jaraco.context \
@@ -120,8 +124,12 @@ jobs:
run: |
pyinstaller --onefile --windowed \
--add-data "build_info.json:." \
--add-data "build_info.json:core" \
--add-data "build_info.json:core/interface" \
--add-data "core/interface/assets:core/interface/assets" \
--icon "core/interface/assets/logo.png" \
--exclude-module PyQt6 \
--exclude-module PyQt5 \
--hidden-import=backports.tarfile \
--hidden-import=backports \
--hidden-import=jaraco.context \
+4
View File
@@ -214,3 +214,7 @@ __marimo__/
*.sublime-project
*.code-workspace
*.swp
# Local build files
build-local.ps1
build-local.bat
+101 -28
View File
@@ -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)
@@ -72,18 +76,16 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.log_signal.connect(self._on_log_signal)
def get_asset_path(filename):
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(__file__)
asset_paths = [
os.path.join('core/interface/assets', filename),
os.path.join(os.path.dirname(__file__), '..', 'assets', filename),
os.path.join(os.path.dirname(sys.executable), 'core/interface/assets', filename),
]
for path in asset_paths:
if os.path.exists(path):
return path
return asset_paths[0]
asset_path = os.path.join(base_path, 'core', 'interface', 'assets', filename)
if os.path.exists(asset_path):
return asset_path
return os.path.join('core', 'interface', 'assets', filename)
self.setWindowIcon(QIcon(get_asset_path("logo.png")))
@@ -111,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")
@@ -145,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()
@@ -177,11 +180,11 @@ 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)
def columnCount(self, parent=QModelIndex()):
return len(self.headers)
@@ -191,32 +194,88 @@ 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 "▶︎" if download.is_paused else "⏸︎"
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 = "▶︎" if paused else "⏸︎"
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)
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)
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))))
@@ -287,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:
@@ -309,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()
@@ -326,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
@@ -338,4 +411,4 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
+16
View File
@@ -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():
+5 -2
View File
@@ -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}")
-1
View File
@@ -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