Merge pull request #78 from KeksPirates/feat/contextmenu

Enhance download management with context menus and tray icon features
This commit is contained in:
shayaa
2026-04-10 00:10:48 +02:00
committed by GitHub
11 changed files with 284 additions and 65 deletions
+1
View File
@@ -51,6 +51,7 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
psutil (7.1.0) psutil (7.1.0)
libtorrent (2.0.11) libtorrent (2.0.11)
libtorrent-windows-dll (0.0.3) libtorrent-windows-dll (0.0.3)
send2trash (2.1.0)
``` ```
<!-- python-dependencies:end --> <!-- python-dependencies:end -->
+2 -1
View File
@@ -7,4 +7,5 @@ PyQtDarkTheme-fork==2.3.4
plyer==2.1.0 plyer==2.1.0
psutil==7.1.0 psutil==7.1.0
libtorrent==2.0.11 libtorrent==2.0.11
libtorrent-windows-dll==0.0.3 libtorrent-windows-dll==0.0.3
send2trash==2.1.0
+5 -7
View File
@@ -37,13 +37,12 @@ class SteamripScraper:
links = [] links = []
names = [] names = []
for gamehtml in games: for gamehtml in games:
link = gamehtml.find("a", href=lambda x: x and x.startswith("/")) link = gamehtml.find("a", href=lambda x: x and x.startswith("/"))
links += re.findall(r'(?<=href=")[^"]*', link.__str__()) if link:
name = gamehtml.find("a", href=lambda x: x and x.startswith("/")) links.append("https://steamrip.com" + link["href"])
names += re.findall(r'(?<=\/">)[^<]*', name.__str__()) names.append(link.get_text())
# construct the list[dict[str,str]]
ret = [] ret = []
@@ -55,8 +54,7 @@ class SteamripScraper:
return ret return ret
def scrape_steamrip_game_downloads(self, gamelink): def scrape_steamrip_game_downloads(self, url):
url = "https://steamrip.com" + gamelink
response = requests.get(url) response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser") soup = BeautifulSoup(response.text, "html.parser")
download_link_elements = soup.find_all("a",class_="shortc-button") download_link_elements = soup.find_all("a",class_="shortc-button")
+164 -45
View File
@@ -1,19 +1,38 @@
from PySide6.QtCore import Qt, QPoint, Signal, QThreadPool, QThread
from utils.logging.logs import consoleLog, remove_download_log 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 PySide6.QtWidgets import QMessageBox
from PySide6.QtCore import Qt, QPoint
from utils.data.state import state from utils.data.state import state
from send2trash import send2trash
from PySide6 import QtWidgets from PySide6 import QtWidgets
import threading
import webbrowser
import subprocess import subprocess
import platform import platform
import time import time
import os import os
class ContextMenu:
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): def __init__(self, main_window):
self.main_window = main_window self.main_window = main_window
self.context_menu = QtWidgets.QMenu(main_window) self.context_menu = QtWidgets.QMenu(main_window)
self.context_menu.addAction("Open Containing Folder", self.openFolderAction) self.context_menu.addAction("Open Containing Folder", self.openFolderAction)
self.context_menu.addAction("Copy Magnet URI", self.copyMagnetURIAction) self.context_menu.addAction("Copy Download Link", self.copyMagnetURIAction)
self.context_menu.addAction("Remove from list", self.cancelDownloadAction) self.context_menu.addAction("Remove from list", self.cancelDownloadAction)
self.context_menu.addAction("Delete File", self.deleteFileAction) self.context_menu.addAction("Delete File", self.deleteFileAction)
@@ -62,30 +81,44 @@ class ContextMenu:
clipboard.setText(magnet_link) clipboard.setText(magnet_link)
def cancelDownloadAction(self): def cancelDownloadAction(self):
if not hasattr(self, '_context_menu_row'): selected_rows = sorted(set(index.row() for index in self.downloadList.selectedIndexes()), reverse=True)
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]
# Cache the name BEFORE removing from session if not selected_rows:
try: return
torrent_name = magnetdl.status().name
except RuntimeError: names = []
torrent_name = "Unknown" 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( confirm = QMessageBox.question(
self.main_window, "Cancel Download", self.main_window,
f"Are you sure you want to cancel the download of '{torrent_name}'?", "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 QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
) )
if confirm == QMessageBox.StandardButton.Yes:
if confirm != QMessageBox.StandardButton.Yes:
return
for magnet_link, magnetdl, name in items_to_remove:
with state.downloads_lock: with state.downloads_lock:
state.active_downloads.pop(magnet_link, None) state.active_downloads.pop(magnet_link, None)
remove_download_log(magnet_link) remove_download_log(magnet_link)
if state.dl_session: if state.dl_session:
@@ -94,53 +127,71 @@ class ContextMenu:
except Exception as e: except Exception as e:
consoleLog(f"Exception while removing download from LibTorrent: {e}") consoleLog(f"Exception while removing download from LibTorrent: {e}")
consoleLog(f"Cancelled download: {torrent_name}", True) consoleLog(f"Cancelled download: {name}", True)
def deleteFileAction(self): def deleteFileAction(self):
if not hasattr(self, '_context_menu_row'): selected_rows = sorted(set(index.row() for index in self.downloadList.selectedIndexes()), reverse=True)
if not selected_rows:
return return
row = self._context_menu_row
items_to_remove = []
with state.downloads_lock: with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads): keys = list(state.active_downloads.keys())
return
magnet_link = list(state.active_downloads.keys())[row] for row in selected_rows:
magnetdl = state.active_downloads[magnet_link] if 0 <= row < len(keys):
try: magnet_link = keys[row]
status = magnetdl.status() magnetdl = state.active_downloads[magnet_link]
save_path = status.save_path
torrent_name = status.name try:
except RuntimeError: status = magnetdl.status()
consoleLog("Error: torrent handle already invalid", True) save_path = status.save_path
with state.downloads_lock: torrent_name = status.name
state.active_downloads.pop(magnet_link, None) except RuntimeError:
remove_download_log(magnet_link) 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 return
download_path = os.path.join(save_path, torrent_name)
names = [item[2] for item in items_to_remove]
confirm = QMessageBox.question( confirm = QMessageBox.question(
self.main_window, "Delete Files", self.main_window,
f"Are you sure you want to delete the downloaded files of '{torrent_name}'? This action cannot be undone.", "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 QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
) )
if confirm == QMessageBox.StandardButton.Yes:
if confirm != QMessageBox.StandardButton.Yes:
return
for magnet_link, magnetdl, torrent_name, download_path in items_to_remove:
with state.downloads_lock: with state.downloads_lock:
state.active_downloads.pop(magnet_link, None) state.active_downloads.pop(magnet_link, None)
remove_download_log(magnet_link) remove_download_log(magnet_link)
if state.dl_session: if state.dl_session:
try: try:
state.dl_session.remove_torrent(magnetdl) state.dl_session.remove_torrent(magnetdl)
time.sleep(0.5) time.sleep(0.5)
except Exception as e: except Exception as e:
consoleLog(f"Exception while removing download from LibTorrent: {e}") consoleLog(f"Exception while removing download from LibTorrent: {e}")
if download_path and os.path.exists(download_path): if download_path and os.path.exists(download_path):
import shutil
last_error = None last_error = None
for attempt in range(3): for attempt in range(3):
try: try:
if os.path.isfile(download_path): if os.path.isfile(download_path) or os.path.isdir(download_path):
os.remove(download_path) send2trash(download_path)
else:
shutil.rmtree(download_path)
consoleLog(f"Deleted files for: {torrent_name}", True) consoleLog(f"Deleted files for: {torrent_name}", True)
last_error = None last_error = None
break break
@@ -148,7 +199,75 @@ class ContextMenu:
last_error = e last_error = e
if attempt < 2: if attempt < 2:
time.sleep(0.5) time.sleep(0.5)
if last_error: if last_error:
consoleLog(f"Error deleting files: {last_error}", True) consoleLog(f"Error deleting files: {last_error}", True)
else: else:
consoleLog(f"Removed entry (files not found): {torrent_name}", True) 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(),)))
+8 -2
View File
@@ -297,6 +297,11 @@ def settings_dialog(self):
interface_layout.addWidget(interface_select) interface_layout.addWidget(interface_select)
interface_container.setLayout(interface_layout) interface_container.setLayout(interface_layout)
# Close To Tray
close_to_tray_container, close_to_tray_checkbox = create_widget(QCheckBox, "Close to Tray: ")
close_to_tray_checkbox.setChecked(state.close_to_tray)
close_to_tray_checkbox.toggled.connect(lambda checked: setattr(state, 'close_to_tray', checked))
# Save / Cancel buttons # Save / Cancel buttons
layout = QHBoxLayout() layout = QHBoxLayout()
@@ -322,7 +327,8 @@ def settings_dialog(self):
image_opacity.value(), image_opacity.value(),
image_as_wallpaper=image_mode_checkbox.isChecked(), image_as_wallpaper=image_mode_checkbox.isChecked(),
image_position=image_position_combo.currentText(), image_position=image_position_combo.currentText(),
accent_color=accent_color_input.text().strip() accent_color=accent_color_input.text().strip(),
close_to_tray=close_to_tray_checkbox.isChecked()
) )
color = _accent_selection_color() color = _accent_selection_color()
if color: if color:
@@ -337,7 +343,7 @@ def settings_dialog(self):
layout.addWidget(save_btn) layout.addWidget(save_btn)
tabs = QtWidgets.QTabWidget() tabs = QtWidgets.QTabWidget()
create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container, accent_color_container], tabs=tabs, stretch=True) create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container, accent_color_container, close_to_tray_container], tabs=tabs, stretch=True)
create_tab("Image", [enable_image_container, image_mode_container, image_position_container, image_width_container, image_offset_container, image_opacity_container], tabs=tabs, stretch=True) create_tab("Image", [enable_image_container, image_mode_container, image_position_container, image_width_container, image_offset_container, image_opacity_container], tabs=tabs, stretch=True)
create_tab("Paths", [download_path_container, image_path_container], tabs=tabs, stretch=True) create_tab("Paths", [download_path_container, image_path_container], tabs=tabs, stretch=True)
create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=tabs, stretch=True) create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=tabs, stretch=True)
+12 -2
View File
@@ -132,6 +132,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self._tracker_hover_delegate = TrackerHoverDelegate(lambda: self._tracker_hovered_row, self) self._tracker_hover_delegate = TrackerHoverDelegate(lambda: self._tracker_hovered_row, self)
state.trackertable = _create_tracker_table(self) state.trackertable = _create_tracker_table(self)
state.trackertable.cellDoubleClicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(state.trackertable.selectedItems(),))))
container = QWidget() container = QWidget()
containerLayout = QVBoxLayout() containerLayout = QVBoxLayout()
@@ -251,7 +252,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.active_timer.timeout.connect(self.show_empty_downloads) self.active_timer.timeout.connect(self.show_empty_downloads)
self.active_timer.start(500) self.active_timer.start(500)
self._context_menu = interface.dialogs.contextmenu.ContextMenu(self) self._context_menu_downloads = interface.dialogs.contextmenu.ContextMenu_Downloads(self)
self._context_menu_trackertable = interface.dialogs.contextmenu.ContextMenu_TrackerTable(self)
self.image_overlay = Image(self) self.image_overlay = Image(self)
def _apply_default_headers(self, table): def _apply_default_headers(self, table):
@@ -352,7 +354,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
table.setRowCount(len(state.posts)) table.setRowCount(len(state.posts))
for x, rowdata in enumerate(state.posts): for x, rowdata in enumerate(state.posts):
for y, (key, data) in enumerate(rowdata.items()): for y, (_, data) in enumerate(rowdata.items()):
item = QTableWidgetItem(str(data)) item = QTableWidgetItem(str(data))
item.setData(Qt.ItemDataRole.UserRole, x) item.setData(Qt.ItemDataRole.UserRole, x)
table.setItem(x, y, item) table.setItem(x, y, item)
@@ -395,6 +397,14 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
super().changeEvent(event) super().changeEvent(event)
def closeEvent(self, event: QCloseEvent): def closeEvent(self, event: QCloseEvent):
if state.close_to_tray is True:
event.ignore()
self.hide()
return
else:
self.shutdown(event)
def shutdown(self, event: QCloseEvent):
closehelper() closehelper()
event.accept() event.accept()
from utils.general.shutdown import force_exit from utils.general.shutdown import force_exit
+83 -4
View File
@@ -1,40 +1,119 @@
from network.libtorrent_misc import send_notification, update_log, check_deleted_files from network.libtorrent_misc import send_notification, update_log, check_deleted_files
from utils.logging.loghandler import split_data, check_completed, check_downloads from utils.logging.loghandler import split_data, check_completed, check_downloads
from network.interface import list_interfaces, init_interfaces from network.interface import list_interfaces, init_interfaces
from PySide6.QtNetwork import QLocalServer, QLocalSocket
from utils.logging.logs import get_download_logs from utils.logging.logs import get_download_logs
from utils.logging.logs import set_main_window from utils.logging.logs import set_main_window
from network.libtorrent_int import check_space from network.libtorrent_int import check_space
from utils.general.shutdown import closehelper from utils.general.shutdown import closehelper
from utils.general.wrappers import run_thread from utils.general.wrappers import run_thread
from interface.assets.base64_icons import logo_base64
from PySide6.QtWidgets import QSystemTrayIcon, QMenu
from utils.general.shutdown import force_exit from utils.general.shutdown import force_exit
from utils.config.config import read_config from utils.config.config import read_config
from PySide6.QtGui import QAction, QPixmap
from utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from interface.gui import MainWindow from interface.gui import MainWindow
from PySide6.QtCore import Qt, QObject
from utils.data.state import state from utils.data.state import state
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt
import qdarktheme import qdarktheme
import threading import threading
import platform import platform
import base64
import signal import signal
import time import time
import sys import sys
SERVER_NAME = "SoftwareManager_KeksPirates"
class SingleInstance(QObject):
def __init__(self):
super().__init__()
self.server = QLocalServer()
socket = QLocalSocket()
socket.connectToServer(SERVER_NAME)
if socket.waitForConnected(100):
socket.write(b"raise")
socket.flush()
socket.waitForBytesWritten(100)
self.is_running = True
else:
QLocalServer.removeServer(SERVER_NAME)
self.server.listen(SERVER_NAME)
self.server.newConnection.connect(self.handle_connection)
self.is_running = False
self.is_running = False
def handle_connection(self):
socket = self.server.nextPendingConnection()
socket.readyRead.connect(lambda: self.read_socket(socket))
def read_socket(self, socket):
msg = socket.readAll().data()
if msg == b"raise":
self.on_raise()
def on_raise(self):
pass
def check_running(app):
single = SingleInstance()
if single.is_running:
print("Another instance is already running. Exiting this instance.")
closehelper()
force_exit()
app._single_instance = single
def run_gui(app): def run_gui(app):
single = app._single_instance
custom_colors = {} custom_colors = {}
if state.accent_color: if state.accent_color:
custom_colors["primary"] = state.accent_color custom_colors["primary"] = state.accent_color
qdarktheme.setup_theme("auto", custom_colors=custom_colors if custom_colors else None) qdarktheme.setup_theme("auto", custom_colors=custom_colors if custom_colors else None)
widget = MainWindow() widget = MainWindow()
# Check OS for window transparency compatibility and apply # Check OS for window transparency compatibility and apply
if state.window_transparency and platform.system() != "Windows": if state.window_transparency and platform.system() != "Windows":
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
transparent_colors = {"background": "#00000000", **custom_colors} transparent_colors = {"background": "#00000000", **custom_colors}
qdarktheme.setup_theme("auto", custom_colors=transparent_colors) qdarktheme.setup_theme("auto", custom_colors=transparent_colors)
pixmap = QPixmap()
image_data = base64.b64decode(logo_base64)
pixmap.loadFromData(image_data)
tray = QSystemTrayIcon()
tray.setIcon(pixmap)
tray.setVisible(True)
menu = QMenu()
show = QAction("Show")
show.triggered.connect(lambda: widget.show())
menu.addAction(show)
quit = QAction("Quit")
quit.triggered.connect(lambda: (closehelper(), force_exit()))
menu.addAction(quit)
tray.activated.connect(lambda reason: widget.show() if reason == QSystemTrayIcon.ActivationReason.Trigger else None)
tray.setContextMenu(menu)
def show_from_tray():
widget.show()
widget.raise_()
widget.activateWindow()
single.on_raise = show_from_tray
set_main_window(widget) set_main_window(widget)
widget.show() widget.show()
signal.signal(signal.SIGINT, signal.SIG_DFL)
sys.exit(app.exec()) sys.exit(app.exec())
def keyboardinterrupthandler(signum, frame): def keyboardinterrupthandler(signum, frame):
@@ -45,10 +124,10 @@ def keyboardinterrupthandler(signum, frame):
def main(): def main():
# Begin counting startup time # Begin counting startup time
start_time = time.perf_counter() start_time = time.perf_counter()
# Initialize UI Engine # Initialize UI Engine
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
# Check if SoftwareManager is already running
check_running(app)
# Parse saved files # Parse saved files
read_config() read_config()
logs = get_download_logs() logs = get_download_logs()
+4 -2
View File
@@ -15,7 +15,8 @@ def create_config():
"ignore_updates": str(state.ignore_updates), "ignore_updates": str(state.ignore_updates),
"autoresume": str(state.autoresume), "autoresume": str(state.autoresume),
"window_transparency": str(state.window_transparency), "window_transparency": str(state.window_transparency),
"accent_color": str(state.accent_color) "accent_color": str(state.accent_color),
"close_to_tray": str(state.close_to_tray)
} }
config["Network"] = { config["Network"] = {
@@ -86,6 +87,7 @@ def read_config():
state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume) state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume)
state.window_transparency = config.getboolean("General", "window_transparency", fallback=state.window_transparency) state.window_transparency = config.getboolean("General", "window_transparency", fallback=state.window_transparency)
state.accent_color = config.get("General", "accent_color", fallback=state.accent_color) state.accent_color = config.get("General", "accent_color", fallback=state.accent_color)
state.close_to_tray = config.getboolean("General", "close_to_tray", fallback=state.close_to_tray)
# Network # Network
state.api_url = config.get("Network", "api_url", fallback=state.api_url) state.api_url = config.get("Network", "api_url", fallback=state.api_url)
@@ -147,4 +149,4 @@ def backup_config(config_path):
os.replace(config_path, backup_path) os.replace(config_path, backup_path)
consoleLog(f"Successfully created backup of corrupted config: {backup_path}") consoleLog(f"Successfully created backup of corrupted config: {backup_path}")
except Exception as e: except Exception as e:
consoleLog(f"Failed to create backup of corrupted config: {e}") consoleLog(f"Failed to create backup of corrupted config: {e}")
+3 -1
View File
@@ -8,7 +8,7 @@ import platform
def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None, image_width=None, image_offset=None, image_opacity=None, image_as_wallpaper=None, image_position=None, accent_color=None): def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None, image_width=None, image_offset=None, image_opacity=None, image_as_wallpaper=None, image_position=None, accent_color=None, close_to_tray=None):
if apiurl is not None: if apiurl is not None:
state.api_url = apiurl state.api_url = apiurl
if download_path is not None: if download_path is not None:
@@ -43,6 +43,8 @@ def save_settings(close=lambda: None, apiurl=None, download_path=None, down_spee
if state.window_transparency and platform.system() != "Windows": if state.window_transparency and platform.system() != "Windows":
custom_colors["background"] = "#00000000" custom_colors["background"] = "#00000000"
qdarktheme.setup_theme("auto", custom_colors=custom_colors if custom_colors else None) qdarktheme.setup_theme("auto", custom_colors=custom_colors if custom_colors else None)
if close_to_tray is not None:
state.close_to_tray = close_to_tray
update_settings() # Update LibTorrent Session Settings update_settings() # Update LibTorrent Session Settings
consoleLog("Saved Settings") consoleLog("Saved Settings")
+1
View File
@@ -30,6 +30,7 @@ class AppState(QObject):
self.interfaces: List = [] self.interfaces: List = []
self.active_interfaces: List = [] self.active_interfaces: List = []
self.bound_interface: Any = None self.bound_interface: Any = None
self.close_to_tray: bool = True
# Image # Image
self._image_enabled: bool = False self._image_enabled: bool = False
+1 -1
View File
@@ -32,4 +32,4 @@ def check_downloads(downloads):
elif download.completed == True and not os.path.exists(download.path): elif download.completed == True and not os.path.exists(download.path):
consoleLog(f"Inexistent Download: {download.title}") consoleLog(f"Inexistent Download: {download.title}")
remove_download_log(download.magnet_uri) remove_download_log(download.magnet_uri)