Files
SoftwareManager/src/interface/dialogs/contextmenu.py
T

274 lines
9.7 KiB
Python

from PySide6.QtCore import Qt, QPoint, Signal, QThread
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 time
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 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 state.dl_session:
try:
state.dl_session.remove_torrent(magnetdl)
time.sleep(0.5)
except Exception as e:
consoleLog(f"Exception while removing download from LibTorrent: {e}")
if download_path and os.path.exists(download_path):
last_error = None
for attempt in range(3):
try:
if os.path.isfile(download_path) or os.path.isdir(download_path):
send2trash(download_path)
consoleLog(f"Deleted files for: {torrent_name}", True)
last_error = None
break
except Exception as e:
last_error = e
if attempt < 2:
time.sleep(0.5)
if last_error:
consoleLog(f"Error deleting files: {last_error}", 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(),)))