mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-04 01:49:42 +02:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5f2be90af | |||
| 695b80c6de | |||
| b7188a515a | |||
| 7b2522438c | |||
| 0686c7aec2 | |||
| 6be1784903 | |||
| 4589322f0a | |||
| 8132d789ef | |||
| 3f39e67933 | |||
| d43d6e57cc | |||
| 28cffe59f5 | |||
| f1726f4e9a | |||
| b7ca895aeb | |||
| f2a79e5eed | |||
| 990135450f |
@@ -27,6 +27,9 @@ SoftwareManager is a Python-based GUI/TUI tool that simplifies downloading and m
|
|||||||
- If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of its branch.
|
- If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of its branch.
|
||||||
- This tool is for educational purposes only. Use it responsibly.
|
- This tool is for educational purposes only. Use it responsibly.
|
||||||
|
|
||||||
|
## Activity
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from core.utils.config.settings import save_settings
|
from core.utils.config.settings import save_settings
|
||||||
from core.utils.data.state import state
|
from core.utils.data.state import state
|
||||||
from core.utils.general.logs import consoleLog
|
from core.utils.general.logs import consoleLog
|
||||||
|
from core.interface.utils.tabhelper import general_tab
|
||||||
|
from core.interface.utils.tabhelper import paths_tab
|
||||||
|
from core.interface.utils.tabhelper import network_tab
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
@@ -13,6 +16,8 @@ from PySide6.QtWidgets import (
|
|||||||
QSpinBox,
|
QSpinBox,
|
||||||
QCheckBox,
|
QCheckBox,
|
||||||
QFileDialog,
|
QFileDialog,
|
||||||
|
QComboBox,
|
||||||
|
QTabWidget,
|
||||||
)
|
)
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
@@ -22,10 +27,9 @@ def settings_dialog(self):
|
|||||||
consoleLog("Settings dialog opened")
|
consoleLog("Settings dialog opened")
|
||||||
dialog = QDialog(self)
|
dialog = QDialog(self)
|
||||||
dialog.setWindowTitle("Settings")
|
dialog.setWindowTitle("Settings")
|
||||||
dialog.setFixedSize(800, 550)
|
dialog.setFixedSize(700, 450)
|
||||||
|
|
||||||
dialog.setLayout(QVBoxLayout())
|
dialog.setLayout(QVBoxLayout())
|
||||||
dialog.layout().addWidget(QLabel("Settings"))
|
|
||||||
|
|
||||||
def close_settings():
|
def close_settings():
|
||||||
dialog.reject()
|
dialog.reject()
|
||||||
@@ -56,9 +60,7 @@ def settings_dialog(self):
|
|||||||
|
|
||||||
autoresume_layout.addStretch()
|
autoresume_layout.addStretch()
|
||||||
autoresume_checkbox.setChecked(state.autoresume)
|
autoresume_checkbox.setChecked(state.autoresume)
|
||||||
autoresume_checkbox.toggled.connect(lambda checked: setattr(state, 'autoresume', checked))
|
|
||||||
autoresume_layout.addWidget(autoresume_checkbox)
|
autoresume_layout.addWidget(autoresume_checkbox)
|
||||||
dialog.layout().addWidget(autoresume_container)
|
|
||||||
|
|
||||||
|
|
||||||
# transparent window checkbox
|
# transparent window checkbox
|
||||||
@@ -73,7 +75,6 @@ def settings_dialog(self):
|
|||||||
transparent_window_checkbox.setChecked(state.window_transparency)
|
transparent_window_checkbox.setChecked(state.window_transparency)
|
||||||
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
|
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
|
||||||
transparent_window_layout.addWidget(transparent_window_checkbox)
|
transparent_window_layout.addWidget(transparent_window_checkbox)
|
||||||
dialog.layout().addWidget(transparent_window_container)
|
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# SERVER SETTING #
|
# SERVER SETTING #
|
||||||
@@ -88,7 +89,6 @@ def settings_dialog(self):
|
|||||||
api_url_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
api_url_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
api_url_container.setLayout(api_url_layout)
|
api_url_container.setLayout(api_url_layout)
|
||||||
api_url.setText(state.api_url)
|
api_url.setText(state.api_url)
|
||||||
dialog.layout().addWidget(api_url_container)
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# DOWNLOAD PATH #
|
# DOWNLOAD PATH #
|
||||||
@@ -103,7 +103,6 @@ def settings_dialog(self):
|
|||||||
download_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
download_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
download_path_container.setLayout(download_path_layout)
|
download_path_container.setLayout(download_path_layout)
|
||||||
download_path.setText(state.download_path)
|
download_path.setText(state.download_path)
|
||||||
dialog.layout().addWidget(download_path_container)
|
|
||||||
|
|
||||||
def browse_download_path():
|
def browse_download_path():
|
||||||
dir_path = QFileDialog.getExistingDirectory(dialog, "Select Download Directory", state.download_path)
|
dir_path = QFileDialog.getExistingDirectory(dialog, "Select Download Directory", state.download_path)
|
||||||
@@ -127,7 +126,6 @@ def settings_dialog(self):
|
|||||||
image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
image_path_container.setLayout(image_path_layout)
|
image_path_container.setLayout(image_path_layout)
|
||||||
image_path.setText(state.image_path)
|
image_path.setText(state.image_path)
|
||||||
dialog.layout().addWidget(image_path_container)
|
|
||||||
|
|
||||||
def browse_image_path():
|
def browse_image_path():
|
||||||
file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0]
|
file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0]
|
||||||
@@ -154,7 +152,6 @@ def settings_dialog(self):
|
|||||||
down_speed_limit_layout.addWidget(down_speed_limit)
|
down_speed_limit_layout.addWidget(down_speed_limit)
|
||||||
down_speed_limit.setFixedWidth(180)
|
down_speed_limit.setFixedWidth(180)
|
||||||
down_speed_limit.setFixedHeight(30)
|
down_speed_limit.setFixedHeight(30)
|
||||||
dialog.layout().addWidget(down_speed_limit_container)
|
|
||||||
|
|
||||||
up_speed_limit_container = QWidget()
|
up_speed_limit_container = QWidget()
|
||||||
up_speed_limit_layout = QHBoxLayout()
|
up_speed_limit_layout = QHBoxLayout()
|
||||||
@@ -168,7 +165,6 @@ def settings_dialog(self):
|
|||||||
up_speed_limit_layout.addWidget(up_speed_limit)
|
up_speed_limit_layout.addWidget(up_speed_limit)
|
||||||
up_speed_limit.setFixedWidth(180)
|
up_speed_limit.setFixedWidth(180)
|
||||||
up_speed_limit.setFixedHeight(30)
|
up_speed_limit.setFixedHeight(30)
|
||||||
dialog.layout().addWidget(up_speed_limit_container)
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# CONNECTION CONFIGS #
|
# CONNECTION CONFIGS #
|
||||||
@@ -186,7 +182,6 @@ def settings_dialog(self):
|
|||||||
max_connections_layout.addWidget(max_connections)
|
max_connections_layout.addWidget(max_connections)
|
||||||
max_connections.setFixedWidth(180)
|
max_connections.setFixedWidth(180)
|
||||||
max_connections.setFixedHeight(30)
|
max_connections.setFixedHeight(30)
|
||||||
dialog.layout().addWidget(max_connections_container)
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# DOWNLOAD CONFIGS #
|
# DOWNLOAD CONFIGS #
|
||||||
@@ -204,7 +199,34 @@ def settings_dialog(self):
|
|||||||
max_downloads_layout.addWidget(max_downloads)
|
max_downloads_layout.addWidget(max_downloads)
|
||||||
max_downloads.setFixedWidth(180)
|
max_downloads.setFixedWidth(180)
|
||||||
max_downloads.setFixedHeight(30)
|
max_downloads.setFixedHeight(30)
|
||||||
dialog.layout().addWidget(max_downloads_container)
|
|
||||||
|
#####################
|
||||||
|
# INTERFACE BINDING #
|
||||||
|
#####################
|
||||||
|
|
||||||
|
interface_container = QWidget()
|
||||||
|
interface_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
interface_label = QLabel("Network Interface:")
|
||||||
|
interface_layout.addWidget(interface_label)
|
||||||
|
interface_select = QComboBox()
|
||||||
|
interface_select.addItems(["None"] + state.interfaces)
|
||||||
|
|
||||||
|
target = state.bound_interface if state.bound_interface else "None"
|
||||||
|
|
||||||
|
index = interface_select.findText(target)
|
||||||
|
if index >= 0:
|
||||||
|
interface_select.setCurrentIndex(index)
|
||||||
|
else:
|
||||||
|
interface_select.setCurrentIndex(0)
|
||||||
|
|
||||||
|
interface_select.setFixedWidth(180)
|
||||||
|
interface_select.setFixedHeight(30)
|
||||||
|
interface_select.setFixedWidth(180)
|
||||||
|
interface_select.setFixedHeight(30)
|
||||||
|
interface_layout.addWidget(interface_select)
|
||||||
|
interface_container.setLayout(interface_layout)
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# SAVE/CANCEL #
|
# SAVE/CANCEL #
|
||||||
@@ -214,12 +236,28 @@ def settings_dialog(self):
|
|||||||
|
|
||||||
save_btn = QPushButton("Save")
|
save_btn = QPushButton("Save")
|
||||||
cancel_btn = QPushButton("Cancel")
|
cancel_btn = QPushButton("Cancel")
|
||||||
save_btn.clicked.connect(lambda: save_settings(close_settings, api_url.text(), download_path.text(), down_speed_limit.value(), up_speed_limit.value(), image_path.text(), None, max_connections.value(), max_downloads.value()))
|
save_btn.clicked.connect(lambda: save_settings(
|
||||||
|
close_settings,
|
||||||
|
api_url.text(),
|
||||||
|
download_path.text(),
|
||||||
|
down_speed_limit.value(),
|
||||||
|
up_speed_limit.value(),
|
||||||
|
image_path.text(),
|
||||||
|
autoresume_checkbox.isChecked(),
|
||||||
|
max_connections.value(),
|
||||||
|
max_downloads.value(),
|
||||||
|
interface_select.currentText()))
|
||||||
|
|
||||||
cancel_btn.clicked.connect(dialog.reject)
|
cancel_btn.clicked.connect(dialog.reject)
|
||||||
layout.addWidget(cancel_btn)
|
layout.addWidget(cancel_btn)
|
||||||
layout.addWidget(save_btn)
|
layout.addWidget(save_btn)
|
||||||
|
|
||||||
|
self.tabs = QTabWidget()
|
||||||
|
self.tab1 = general_tab("General", autoresume_container, update_checkbox_container, transparent_window_container, self.tabs)
|
||||||
|
self.tab2 = paths_tab("Paths", download_path_container, image_path_container, self.tabs)
|
||||||
|
self.tab3 = network_tab("Network", interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container, self.tabs)
|
||||||
|
|
||||||
|
dialog.layout().addWidget(self.tabs)
|
||||||
dialog.layout().addLayout(layout)
|
dialog.layout().addLayout(layout)
|
||||||
|
|
||||||
dialog.exec()
|
dialog.exec()
|
||||||
|
|||||||
+57
-21
@@ -29,7 +29,7 @@ import libtorrent as lt
|
|||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from core.utils.general.logs import consoleLog, remove_download_log
|
from core.utils.general.logs import consoleLog, remove_download_log, flush_log_buffer
|
||||||
from core.utils.general.wrappers import run_thread
|
from core.utils.general.wrappers import run_thread
|
||||||
from core.utils.data.state import state
|
from core.utils.data.state import state
|
||||||
from core.utils.network.download import download_selected
|
from core.utils.network.download import download_selected
|
||||||
@@ -134,42 +134,44 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter
|
self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter
|
||||||
|
|
||||||
self.dlbutton = QtWidgets.QPushButton("Download")
|
self.dlbutton = QtWidgets.QPushButton("Download")
|
||||||
self.dlbutton.setCursor(Qt.PointingHandCursor)
|
self.dlbutton.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
self.libraryList = QListWidget()
|
self.libraryList = QListWidget()
|
||||||
|
|
||||||
self.emptyResults = QLabel("No Results")
|
self.emptyResults = QLabel("No Results")
|
||||||
self.emptyResults.setAlignment(Qt.AlignCenter)
|
self.emptyResults.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.emptyResults.hide()
|
self.emptyResults.hide()
|
||||||
|
|
||||||
self.download_model = None
|
self.download_model = None
|
||||||
self.downloadList = QTableView()
|
self.downloadList = QTableView()
|
||||||
self.emptyLibrary = QLabel("No items in library.")
|
self.emptyLibrary = QLabel("No items in library.")
|
||||||
self.emptyDownload = QLabel("No items in downloads.")
|
self.emptyDownload = QLabel("No items in downloads.")
|
||||||
self.emptyDownload.setAlignment(Qt.AlignCenter)
|
self.emptyDownload.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
self.consoleLog = QTextEdit()
|
self.consoleLog = QTextEdit()
|
||||||
self.progressbar = QProgressBar()
|
self.progressbar = QProgressBar()
|
||||||
|
|
||||||
|
flush_log_buffer()
|
||||||
|
|
||||||
# Table Widget for Item List
|
# Table Widget for Item List
|
||||||
self.qtablewidget = QTableWidget()
|
self.qtablewidget = QTableWidget()
|
||||||
|
|
||||||
self.qtablewidget.setColumnCount(2)
|
self.qtablewidget.setColumnCount(2)
|
||||||
self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||||
self.qtablewidget.verticalHeader().setVisible(False)
|
self.qtablewidget.verticalHeader().setVisible(False)
|
||||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
header = self.qtablewidget.horizontalHeader()
|
header = self.qtablewidget.horizontalHeader()
|
||||||
header.setSectionResizeMode(0, QHeaderView.Stretch)
|
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
|
||||||
header.setSectionResizeMode(1, QHeaderView.Fixed)
|
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
|
||||||
header.setStretchLastSection(False)
|
header.setStretchLastSection(False)
|
||||||
|
|
||||||
self.qtablewidget.setAttribute(Qt.WA_TranslucentBackground)
|
self.qtablewidget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
self.qtablewidget.viewport().setAttribute(Qt.WA_TranslucentBackground)
|
self.qtablewidget.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
|
||||||
header = self.qtablewidget.horizontalHeader()
|
header = self.qtablewidget.horizontalHeader()
|
||||||
header.setSectionResizeMode(1, QHeaderView.Fixed)
|
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
|
||||||
header.resizeSection(1, 500)
|
header.resizeSection(1, 500)
|
||||||
|
|
||||||
container = QWidget()
|
container = QWidget()
|
||||||
@@ -273,13 +275,14 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
status = magnetdl.status()
|
status = magnetdl.status()
|
||||||
|
|
||||||
if status.state == lt.torrent_status.seeding:
|
if status.state == lt.torrent_status.seeding:
|
||||||
if state.download_path and os.path.exists(state.download_path):
|
save_path = magnetdl.save_path()
|
||||||
|
if save_path and os.path.exists(save_path):
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
os.startfile(os.path.normpath(state.download_path))
|
os.startfile(os.path.normpath(save_path))
|
||||||
elif platform.system() == "Linux":
|
elif platform.system() == "Linux":
|
||||||
subprocess.Popen(["xdg-open", state.download_path])
|
subprocess.Popen(["xdg-open", save_path])
|
||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
subprocess.Popen(["open", state.download_path])
|
subprocess.Popen(["open", save_path])
|
||||||
return
|
return
|
||||||
|
|
||||||
if status.paused:
|
if status.paused:
|
||||||
@@ -337,10 +340,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
self.download_model = DownloadModel()
|
self.download_model = DownloadModel()
|
||||||
self.downloadList.setModel(self.download_model)
|
self.downloadList.setModel(self.download_model)
|
||||||
self.downloadList.horizontalHeader().setStretchLastSection(False)
|
self.downloadList.horizontalHeader().setStretchLastSection(False)
|
||||||
self.downloadList.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
self.downloadList.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
|
||||||
self.downloadList.setSelectionBehavior(QTableView.SelectRows)
|
self.downloadList.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)
|
||||||
self.downloadList.setAttribute(Qt.WA_TranslucentBackground)
|
self.downloadList.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
self.downloadList.viewport().setAttribute(Qt.WA_TranslucentBackground)
|
self.downloadList.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
|
||||||
download_model = self.download_model
|
download_model = self.download_model
|
||||||
|
|
||||||
@@ -359,7 +362,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
|
|
||||||
# Tabs
|
# Tabs
|
||||||
self.tabs = QTabWidget()
|
self.tabs = QTabWidget()
|
||||||
self.tabs = QTabWidget()
|
|
||||||
self.tabs.setStyleSheet("""
|
self.tabs.setStyleSheet("""
|
||||||
QTabBar::tab {
|
QTabBar::tab {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@@ -460,7 +462,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
self.context_menu = QtWidgets.QMenu(self)
|
self.context_menu = QtWidgets.QMenu(self)
|
||||||
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 Magnet URI", self.copyMagnetURIAction)
|
||||||
self.context_menu.addAction("Cancel Download", self.cancelDownload)
|
self.context_menu.addAction("Cancel Download", self.cancelDownloadAction)
|
||||||
|
self.context_menu.addAction("Delete File", self.deleteFileAction)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_log(text):
|
def add_log(text):
|
||||||
@@ -565,7 +568,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
clipboard = QtWidgets.QApplication.clipboard()
|
clipboard = QtWidgets.QApplication.clipboard()
|
||||||
clipboard.setText(magnet_link)
|
clipboard.setText(magnet_link)
|
||||||
|
|
||||||
def cancelDownload(self):
|
def cancelDownloadAction(self):
|
||||||
if not hasattr(self, '_context_menu_row'):
|
if not hasattr(self, '_context_menu_row'):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -581,3 +584,36 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
del state.active_downloads[magnet_link]
|
del state.active_downloads[magnet_link]
|
||||||
remove_download_log(magnet_link)
|
remove_download_log(magnet_link)
|
||||||
consoleLog(f"Cancelled download: {magnetdl.status().name}", True)
|
consoleLog(f"Cancelled download: {magnetdl.status().name}", True)
|
||||||
|
|
||||||
|
def deleteFileAction(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]
|
||||||
|
status = magnetdl.status()
|
||||||
|
save_path = status.save_path
|
||||||
|
torrent_name = status.name
|
||||||
|
download_path = os.path.join(save_path, torrent_name)
|
||||||
|
|
||||||
|
confirm = QMessageBox.question(self, "Delete Files", f"Are you sure you want to delete the downloaded files of '{magnetdl.status().name}'? This action cannot be undone.", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||||
|
if confirm == QMessageBox.StandardButton.Yes:
|
||||||
|
if download_path and os.path.exists(download_path):
|
||||||
|
try:
|
||||||
|
if os.path.isfile(download_path):
|
||||||
|
os.remove(download_path)
|
||||||
|
del state.active_downloads[magnet_link]
|
||||||
|
remove_download_log(magnet_link)
|
||||||
|
consoleLog(f"Deleted files for: {magnetdl.status().name}", True)
|
||||||
|
else:
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(download_path)
|
||||||
|
del state.active_downloads[magnet_link]
|
||||||
|
remove_download_log(magnet_link)
|
||||||
|
consoleLog(f"Deleted files for: {magnetdl.status().name}", True)
|
||||||
|
except Exception as e:
|
||||||
|
consoleLog(f"Error deleting files: {e}", True)
|
||||||
|
|||||||
@@ -15,3 +15,42 @@ def create_tab(title, searchbar, software_list, tabs, dlbutton, layout2):
|
|||||||
tab.setLayout(layout)
|
tab.setLayout(layout)
|
||||||
tabs.addTab(tab, title)
|
tabs.addTab(tab, title)
|
||||||
return tab
|
return tab
|
||||||
|
|
||||||
|
#################
|
||||||
|
# SETTINGS TABS #
|
||||||
|
#################
|
||||||
|
def general_tab(title, autoresume, update_checkbox, transparent_window, tabs):
|
||||||
|
tab = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(autoresume)
|
||||||
|
layout.addWidget(update_checkbox)
|
||||||
|
layout.addWidget(transparent_window)
|
||||||
|
layout.addStretch()
|
||||||
|
tab.setLayout(layout)
|
||||||
|
tabs.addTab(tab, title)
|
||||||
|
return tab
|
||||||
|
|
||||||
|
def paths_tab(title, download_path, image_path, tabs):
|
||||||
|
tab = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(download_path)
|
||||||
|
layout.addWidget(image_path)
|
||||||
|
layout.addStretch()
|
||||||
|
tab.setLayout(layout)
|
||||||
|
tabs.addTab(tab, title)
|
||||||
|
return tab
|
||||||
|
|
||||||
|
|
||||||
|
def network_tab(title, network_interface, max_connections, max_downloads, up_speed_limit, down_speed_limit, api_url, tabs):
|
||||||
|
tab = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(network_interface)
|
||||||
|
layout.addWidget(max_connections)
|
||||||
|
layout.addWidget(max_downloads)
|
||||||
|
layout.addWidget(up_speed_limit)
|
||||||
|
layout.addWidget(down_speed_limit)
|
||||||
|
layout.addWidget(api_url)
|
||||||
|
layout.addStretch()
|
||||||
|
tab.setLayout(layout)
|
||||||
|
tabs.addTab(tab, title)
|
||||||
|
return tab
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
from core.utils.general.logs import consoleLog
|
||||||
|
from core.utils.data.state import state
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
addrs = psutil.net_if_addrs()
|
||||||
|
stats = psutil.net_if_stats()
|
||||||
|
|
||||||
|
def get_net_interfaces():
|
||||||
|
for interface in addrs.keys():
|
||||||
|
consoleLog(f"Found Interface: {interface}")
|
||||||
|
return addrs.keys()
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_interfaces():
|
||||||
|
active = []
|
||||||
|
|
||||||
|
for interface, addr_list in addrs.items():
|
||||||
|
up = stats[interface].isup
|
||||||
|
for addr in addr_list:
|
||||||
|
if addr.family == 2: # ipv4
|
||||||
|
ipv4 = addr.address
|
||||||
|
if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up == True:
|
||||||
|
active.append(interface)
|
||||||
|
consoleLog(f"Found Active: {interface}")
|
||||||
|
|
||||||
|
return active
|
||||||
|
|
||||||
|
|
||||||
|
def list_interfaces() -> None:
|
||||||
|
|
||||||
|
for interface, addr_list in addrs.items():
|
||||||
|
up = stats[interface].isup
|
||||||
|
for addr in addr_list:
|
||||||
|
if addr.family == 2: # ipv4
|
||||||
|
ipv4 = addr.address
|
||||||
|
if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up == True:
|
||||||
|
status = "ACTIVE"
|
||||||
|
else:
|
||||||
|
status = "INACTIVE"
|
||||||
|
consoleLog(f"Found Interface: {interface} [{status}]")
|
||||||
|
|
||||||
|
def init_interfaces():
|
||||||
|
state.interfaces = list(addrs.keys())
|
||||||
|
state.active_interfaces = get_active_interfaces()
|
||||||
|
|
||||||
|
def get_interface_ip(interface_name):
|
||||||
|
if interface_name in addrs:
|
||||||
|
for addr in addrs[interface_name]:
|
||||||
|
if addr.family == 2: # ipv4
|
||||||
|
if not addr.address.startswith("127.") and not addr.address.startswith("169.254"):
|
||||||
|
return addr.address
|
||||||
|
return None
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
from core.utils.general.wrappers import run_thread
|
from core.utils.general.wrappers import run_thread
|
||||||
|
from core.network.interface import get_interface_ip
|
||||||
import threading
|
import threading
|
||||||
import libtorrent as lt
|
import libtorrent as lt
|
||||||
from core.utils.data.state import state
|
from core.utils.data.state import state
|
||||||
@@ -15,6 +16,7 @@ def init_session():
|
|||||||
|
|
||||||
state.dl_session = lt.session()
|
state.dl_session = lt.session()
|
||||||
|
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
"upload_rate_limit": state.up_speed_limit,
|
"upload_rate_limit": state.up_speed_limit,
|
||||||
"download_rate_limit": state.down_speed_limit,
|
"download_rate_limit": state.down_speed_limit,
|
||||||
@@ -27,7 +29,14 @@ def init_session():
|
|||||||
"active_downloads": state.max_downloads
|
"active_downloads": state.max_downloads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.bound_interface:
|
||||||
|
interface_ip = get_interface_ip(state.bound_interface)
|
||||||
|
if interface_ip:
|
||||||
|
settings["outgoing_interfaces"] = interface_ip
|
||||||
|
settings["listen_interfaces"] = f"{interface_ip}:6881"
|
||||||
|
consoleLog(f"Binding to Interface IP: {interface_ip}")
|
||||||
|
else:
|
||||||
|
consoleLog(f"Error finding IP for Interface {state.bound_interface}")
|
||||||
|
|
||||||
state.dl_session.apply_settings(settings)
|
state.dl_session.apply_settings(settings)
|
||||||
|
|
||||||
@@ -45,7 +54,6 @@ def add_download(magnet_uri, dl_path=state.download_path):
|
|||||||
consoleLog("Skipping, download already running...")
|
consoleLog("Skipping, download already running...")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
magnetdl = lt.parse_magnet_uri(magnet_uri)
|
magnetdl = lt.parse_magnet_uri(magnet_uri)
|
||||||
magnetdl.save_path = dl_path
|
magnetdl.save_path = dl_path
|
||||||
|
|
||||||
@@ -99,5 +107,20 @@ def update_settings():
|
|||||||
"active_downloads": state.max_downloads
|
"active_downloads": state.max_downloads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.bound_interface:
|
||||||
|
interface_ip = get_interface_ip(state.bound_interface)
|
||||||
|
if interface_ip:
|
||||||
|
settings["outgoing_interfaces"] = interface_ip
|
||||||
|
settings["listen_interfaces"] = f"{interface_ip}:6881"
|
||||||
|
consoleLog(f"Binding to Interface IP: {interface_ip}")
|
||||||
|
else:
|
||||||
|
consoleLog(f"Error finding IP for Interface {state.bound_interface}")
|
||||||
|
|
||||||
|
state.dl_session.apply_settings(settings)
|
||||||
|
|
||||||
|
def update_bound_interface():
|
||||||
|
|
||||||
|
settings = { "outgoing_interfaces": state.bound_interface }
|
||||||
|
|
||||||
state.dl_session.apply_settings(settings)
|
state.dl_session.apply_settings(settings)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,3 @@ def add_magnet(uri):
|
|||||||
consoleLog("Magnet URI added to LibTorrent")
|
consoleLog("Magnet URI added to LibTorrent")
|
||||||
else:
|
else:
|
||||||
consoleLog(f"Invalid Magnet Link: {uri}")
|
consoleLog(f"Invalid Magnet Link: {uri}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from core.utils.general.logs import consoleLog
|
|||||||
from .config import create_config
|
from .config import create_config
|
||||||
|
|
||||||
|
|
||||||
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):
|
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):
|
||||||
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:
|
||||||
@@ -21,6 +21,8 @@ def save_settings(close=lambda: None, apiurl=None, download_path=None, down_spee
|
|||||||
state.max_connections = max_connections
|
state.max_connections = max_connections
|
||||||
if max_downloads is not None:
|
if max_downloads is not None:
|
||||||
state.max_downloads = max_downloads
|
state.max_downloads = max_downloads
|
||||||
|
if bound_interface is not None:
|
||||||
|
state.bound_interface = None if bound_interface == "None" else bound_interface
|
||||||
|
|
||||||
|
|
||||||
update_settings()
|
update_settings()
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ class AppState(QObject):
|
|||||||
self.down_speed_limit: int = 0
|
self.down_speed_limit: int = 0
|
||||||
self.max_connections: int = 200
|
self.max_connections: int = 200
|
||||||
self.max_downloads: int = 10
|
self.max_downloads: int = 10
|
||||||
self.settings_path: str = None
|
self.settings_path: str = ""
|
||||||
self.dl_session: Any = None
|
self.dl_session: Any = None
|
||||||
self.active_downloads: List[str] = {}
|
self.active_downloads: List[str] = {}
|
||||||
self.window_transparency: bool = False
|
self.window_transparency: bool = False
|
||||||
|
self.interfaces: List = []
|
||||||
|
self.active_interfaces: List = []
|
||||||
|
self.bound_interface: str = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_path(self) -> str:
|
def image_path(self) -> str:
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
_log_buffer = []
|
||||||
|
|
||||||
|
|
||||||
def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
|
def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
|
||||||
downloads_file = os.path.join(state.settings_path, "downloads.json")
|
downloads_file = os.path.join(state.settings_path, "downloads.json")
|
||||||
@@ -189,15 +191,28 @@ def set_main_window(window):
|
|||||||
global _main_window
|
global _main_window
|
||||||
_main_window = window
|
_main_window = window
|
||||||
|
|
||||||
|
def flush_log_buffer(): # credits to claude
|
||||||
|
global _log_buffer
|
||||||
|
if _log_buffer:
|
||||||
|
try:
|
||||||
|
from core.interface.gui import MainWindow
|
||||||
|
for log_entry in _log_buffer:
|
||||||
|
MainWindow.add_log(log_entry)
|
||||||
|
_log_buffer = []
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def consoleLog(text, printAnyways = False):
|
def consoleLog(text, printAnyways = False):
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
current_time = now.strftime("%H:%M:%S")
|
current_time = now.strftime("%H:%M:%S")
|
||||||
|
formatted_text = f"[{current_time}] {text}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from core.interface.gui import MainWindow
|
from core.interface.gui import MainWindow
|
||||||
MainWindow.add_log(f"[{current_time}] {text}")
|
MainWindow.add_log(formatted_text)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
global _log_buffer
|
||||||
|
_log_buffer.append(formatted_text)
|
||||||
|
|
||||||
if state.debug or printAnyways:
|
if state.debug or printAnyways:
|
||||||
print(f"[{current_time}] {text}")
|
print(formatted_text)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from core.utils.general.logs import get_download_logs
|
|||||||
from core.utils.general.shutdown import closehelper, shutdown_event
|
from core.utils.general.shutdown import closehelper, shutdown_event
|
||||||
from core.utils.general.wrappers import run_thread
|
from core.utils.general.wrappers import run_thread
|
||||||
from core.utils.general.loghandler import split_data, check_completed
|
from core.utils.general.loghandler import split_data, check_completed
|
||||||
|
from core.network.interface import list_interfaces, init_interfaces
|
||||||
|
from core.network.libtorrent_int import update_bound_interface
|
||||||
from core.utils.config.config import read_config
|
from core.utils.config.config import read_config
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
@@ -45,12 +47,18 @@ if __name__ == "__main__":
|
|||||||
if args.debug:
|
if args.debug:
|
||||||
state.debug = args.debug # override of read_config
|
state.debug = args.debug # override of read_config
|
||||||
signal.signal(signal.SIGINT, keyboardinterrupthandler)
|
signal.signal(signal.SIGINT, keyboardinterrupthandler)
|
||||||
|
consoleLog("Starting SoftwareManager...")
|
||||||
|
consoleLog("Fetching Network Interfaces...")
|
||||||
|
list_interfaces()
|
||||||
|
consoleLog("Initialiting Interface variables...")
|
||||||
|
init_interfaces()
|
||||||
|
consoleLog(f"Current Bound: {state.bound_interface}")
|
||||||
run_thread(threading.Thread(target=send_notification, args=(shutdown_event,), daemon=True))
|
run_thread(threading.Thread(target=send_notification, args=(shutdown_event,), daemon=True))
|
||||||
consoleLog("Started send_notification thread")
|
consoleLog("Started Thread: send_notification")
|
||||||
run_thread(threading.Thread(target=update_log, args=(shutdown_event,), daemon=True))
|
run_thread(threading.Thread(target=update_log, args=(shutdown_event,), daemon=True))
|
||||||
consoleLog("Started update_log thread")
|
consoleLog("Started Thread: update_log")
|
||||||
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
|
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
|
||||||
consoleLog("Started check_completed thread")
|
consoleLog("Started Thread: check_completed")
|
||||||
consoleLog("Launching GUI")
|
consoleLog("Launching GUI")
|
||||||
run_gui()
|
run_gui()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user