From e95ad11672bc5fa11a2dbbcb24a89f1f5a4bac1a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Wed, 4 Feb 2026 18:56:03 +0100 Subject: [PATCH] add context menu to cancel downloads, open folder and copy magnet link --- core/interface/gui.py | 76 +++++++++++++++++++++++++++++++++++--- core/utils/general/logs.py | 26 ++++++++++++- main.py | 2 +- 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 8ac034b..4179cf2 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -1,4 +1,3 @@ -from ctypes import alignment from PySide6 import QtWidgets from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent, QSize from PySide6.QtWidgets import ( @@ -19,7 +18,7 @@ from PySide6.QtWidgets import ( QStyledItemDelegate, ) -from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap +from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent import darkdetect import threading import platform @@ -30,7 +29,7 @@ import libtorrent as lt import time import sys import json -from core.utils.general.logs import consoleLog +from core.utils.general.logs import consoleLog, remove_download_log from core.utils.general.wrappers import run_thread from core.utils.data.state import state from core.utils.network.download import download_selected @@ -274,7 +273,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): status = magnetdl.status() if status.state == lt.torrent_status.seeding: - if state.download_path is not None and os.path.exists(state.download_path): + if state.download_path and os.path.exists(state.download_path): if platform.system() == "Windows": os.startfile(os.path.normpath(state.download_path)) elif platform.system() == "Linux": @@ -329,7 +328,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): layout.addStretch() widget.setLayout(layout) return widget - def editorEvent(self, event, model, option, index): if index.column() == 0 and event.type() == QEvent.Type.MouseButtonPress: self.clicked.emit(index.row()) @@ -459,6 +457,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.active_timer.timeout.connect(self.show_empty_downloads) self.active_timer.start(500) + self.context_menu = QtWidgets.QMenu(self) + self.context_menu.addAction("Open Containing Folder", self.openFolderAction) + self.context_menu.addAction("Copy Magnet URI", self.copyMagnetURIAction) + self.context_menu.addAction("Cancel Download", self.cancelDownload) + @staticmethod def add_log(text): if MainWindow._instance: @@ -515,3 +518,66 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): super().resizeEvent(event) table_width = self.qtablewidget.viewport().width() self.qtablewidget.setColumnWidth(1, int(table_width * 0.3)) + + def contextMenuEvent(self, event: QContextMenuEvent): + if self.downloadList.underMouse(): + pos = self.downloadList.viewport().mapFromGlobal(event.globalPos()) + index = self.downloadList.indexAt(pos) + + if index.isValid(): + row = index.row() + self._context_menu_row = row + self.context_menu.exec(event.globalPos()) + else: + super().contextMenuEvent(event) + + def openFolderAction(self): + if not hasattr(self, '_context_menu_row'): + return + + row = self._context_menu_row + if row < 0 or row >= len(state.active_downloads): + return + + magnet_link = list(state.active_downloads.keys())[row] + magnetdl = state.active_downloads[magnet_link] + + download_path = magnetdl.save_path() + + if download_path and os.path.exists(download_path): + if platform.system() == "Windows": + os.startfile(os.path.normpath(download_path)) + elif platform.system() == "Linux": + subprocess.Popen(["xdg-open", download_path]) + elif platform.system() == "Darwin": + subprocess.Popen(["open", download_path]) + + def copyMagnetURIAction(self): + if not hasattr(self, '_context_menu_row'): + return + + row = self._context_menu_row + if row < 0 or row >= len(state.active_downloads): + return + + magnet_link = list(state.active_downloads.keys())[row] + + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(magnet_link) + + def cancelDownload(self): + if not hasattr(self, '_context_menu_row'): + return + + row = self._context_menu_row + if row < 0 or row >= len(state.active_downloads): + return + + magnet_link = list(state.active_downloads.keys())[row] + magnetdl = state.active_downloads[magnet_link] + + confirm = QMessageBox.question(self, "Cancel Download", f"Are you sure you want to cancel the download of '{magnetdl.status().name}'?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + if confirm == QMessageBox.StandardButton.Yes: + del state.active_downloads[magnet_link] + remove_download_log(magnet_link) + consoleLog(f"Cancelled download: {magnetdl.status().name}", True) diff --git a/core/utils/general/logs.py b/core/utils/general/logs.py index 5fe7eea..48afec8 100644 --- a/core/utils/general/logs.py +++ b/core/utils/general/logs.py @@ -41,6 +41,30 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList: return download_list +def remove_download_log(magnet_uri) -> DownloadList: + downloads_file = os.path.join(state.settings_path, "downloads.json") + + if os.path.exists(downloads_file) and os.path.getsize(downloads_file) > 0: + try: + with open(downloads_file, "r") as file: + existing_data = json.load(file) + downloads = [Download(**d) for d in existing_data.get("data", [])] + except json.JSONDecodeError: + downloads = [] + else: + downloads = [] + + magnet_link = (magnet_uri or "").strip() + 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 {magnet_link} from Log File") + + download_list = DownloadList(data=downloads, count=len(downloads)) + with open(downloads_file, "w") as file: + json.dump(asdict(download_list), file, indent=4) + + return download_list + def update_download_completed(magnet_uri, completed) -> DownloadList: downloads_file = os.path.join(state.settings_path, "downloads.json") @@ -176,4 +200,4 @@ def consoleLog(text, printAnyways = False): pass if state.debug or printAnyways: - print(f"[{current_time}] {text}") \ No newline at end of file + print(f"[{current_time}] {text}") diff --git a/main.py b/main.py index b8bb61f..d59a825 100644 --- a/main.py +++ b/main.py @@ -18,7 +18,7 @@ import sys parser = argparse.ArgumentParser() -parser.add_argument("--debug", action="store_true") +parser.add_argument("--debug", action="store_true") args = parser.parse_args() def run_gui():