Files
SoftwareManager/src/interface/dialogs/contextmenu.py
T
2026-05-04 21:51:18 +02:00

274 lines
9.9 KiB
Python

from PySide6.QtCore import Qt, QPoint, Signal, QThread
from network.direct_download.handle import DirectDownloadHandle
from utils.logging.logs import consoleLog, remove_download_log
from utils.network.download import download_selected
from utils.data.tracker import get_magnet_link
from utils.general.wrappers import run_thread
from PySide6.QtWidgets import QMessageBox
from utils.data.state import state
from send2trash import send2trash
from PySide6 import QtWidgets
import threading
import webbrowser
import subprocess
import platform
import os
class MagnetWorker(QThread):
finished = Signal(str)
def __init__(self, url, parent=None):
super().__init__(parent)
self.url = url
def run(self):
magnet = get_magnet_link(self.url)
if magnet:
self.finished.emit(magnet)
class ContextMenu_Downloads:
def __init__(self, main_window):
self.main_window = main_window
self.context_menu = QtWidgets.QMenu(main_window)
self.context_menu.addAction("Open Containing Folder", self.openFolderAction)
self.context_menu.addAction("Copy Download Link", self.copyMagnetURIAction)
self.context_menu.addAction("Remove from list", self.cancelDownloadAction)
self.context_menu.addAction("Delete File", self.deleteFileAction)
main_window.downloadList.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
main_window.downloadList.customContextMenuRequested.connect(self._show_context_menu)
@property
def downloadList(self):
return self.main_window.downloadList
def _show_context_menu(self, pos: QPoint):
index = self.downloadList.indexAt(pos)
if index.isValid():
self._context_menu_row = index.row()
self.context_menu.exec(self.downloadList.viewport().mapToGlobal(pos))
def openFolderAction(self):
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads):
return
keys = list(state.active_downloads.keys())
magnet_link = 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
with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads):
return
keys = list(state.active_downloads.keys())
magnet_link = keys[row]
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(magnet_link)
def cancelDownloadAction(self):
selected_rows = sorted(set(index.row() for index in self.downloadList.selectedIndexes()), reverse=True)
if not selected_rows:
return
names = []
items_to_remove = []
with state.downloads_lock:
keys = list(state.active_downloads.keys())
for row in selected_rows:
if 0 <= row < len(keys):
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link]
try:
name = magnetdl.status().name
except RuntimeError:
name = "Unknown"
names.append(name)
items_to_remove.append((magnet_link, magnetdl, name))
confirm = QMessageBox.question(
self.main_window,
"Cancel Downloads",
f"Are you sure you want to cancel {len(names)} download(s)?\n\n" + "\n".join(names),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if confirm != QMessageBox.StandardButton.Yes:
return
for magnet_link, magnetdl, name in items_to_remove:
with state.downloads_lock:
state.active_downloads.pop(magnet_link, None)
remove_download_log(magnet_link)
if isinstance(magnetdl, DirectDownloadHandle):
try:
magnetdl.stop()
except Exception as e:
consoleLog(f"Exception while stopping direct download: {e}")
elif state.dl_session:
try:
state.dl_session.remove_torrent(magnetdl)
except Exception as e:
consoleLog(f"Exception while removing download from LibTorrent: {e}")
consoleLog(f"Cancelled download: {name}", True)
def deleteFileAction(self):
selected_rows = sorted(set(index.row() for index in self.downloadList.selectedIndexes()), reverse=True)
if not selected_rows:
return
items_to_remove = []
with state.downloads_lock:
keys = list(state.active_downloads.keys())
for row in selected_rows:
if 0 <= row < len(keys):
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link]
try:
status = magnetdl.status()
save_path = status.save_path
torrent_name = status.name
except RuntimeError:
consoleLog("Error: torrent handle already invalid", True)
continue
download_path = os.path.join(save_path, torrent_name)
items_to_remove.append((magnet_link, magnetdl, torrent_name, download_path))
if not items_to_remove:
return
names = [item[2] for item in items_to_remove]
confirm = QMessageBox.question(
self.main_window,
"Delete Files",
f"Are you sure you want to delete {len(names)} download(s)?\n\n" + "\n".join(names),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if confirm != QMessageBox.StandardButton.Yes:
return
for magnet_link, magnetdl, torrent_name, download_path in items_to_remove:
with state.downloads_lock:
state.active_downloads.pop(magnet_link, None)
remove_download_log(magnet_link)
if isinstance(magnetdl, DirectDownloadHandle):
try:
magnetdl.stop()
except Exception as e:
consoleLog(f"Exception while stopping direct download: {e}")
elif state.dl_session:
try:
state.dl_session.remove_torrent(magnetdl)
except Exception as e:
consoleLog(f"Exception while removing download from LibTorrent: {e}")
if download_path and os.path.exists(download_path):
try:
if os.path.isfile(download_path) or os.path.isdir(download_path):
send2trash(download_path)
consoleLog(f"Deleted files for: {torrent_name}", True)
except Exception as e:
consoleLog(f"Error deleting files: {e}", True)
else:
consoleLog(f"Removed entry (files not found): {torrent_name}", True)
class ContextMenu_TrackerTable:
def __init__(self, main_window):
self.main_window = main_window
self.context_menu = QtWidgets.QMenu(main_window)
self.context_menu.addAction("Copy Download Link", self.copyMagnetURIAction)
self.context_menu.addAction("Open in Browser", self.openInBrowserAction)
self.context_menu.addAction("Download", self.downloadItemAction)
state.trackertable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
state.trackertable.customContextMenuRequested.connect(self._show_context_menu)
@property
def TrackerTable(self):
return state.trackertable
def _show_context_menu(self, pos: QPoint):
index = self.TrackerTable.indexAt(pos)
if index.isValid():
self._context_menu_row = index.row()
self.context_menu.exec(self.TrackerTable.viewport().mapToGlobal(pos))
def copyMagnetURIAction(self):
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
if not state.posts or row < 0 or row >= len(state.posts):
return
post = state.posts[row]
url = post.get("url")
if not url:
return
worker = MagnetWorker(url, parent=self.main_window)
worker.finished.connect(self._on_magnet_fetched)
worker.finished.connect(worker.deleteLater)
worker.start()
def _on_magnet_fetched(self, magnet):
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(magnet)
consoleLog("Magnet URI copied to clipboard!", True)
def openInBrowserAction(self):
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
if not state.posts or row < 0 or row >= len(state.posts):
return
post = state.posts[row]
url = post.get("url")
if url:
try: webbrowser.open(url)
except Exception as e: consoleLog(f"Failed to open URL: {e}", True)
def downloadItemAction(self):
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
if not state.posts or row < 0 or row >= len(state.posts):
return
run_thread(threading.Thread(target=download_selected, args=(state.trackertable.selectedItems(),)))