Compare commits

..

5 Commits

Author SHA1 Message Date
Vxrtrauter 7b2522438c feat: add network interface binding to settings dialog and update save_settings function 2026-02-06 23:50:18 +01:00
Vxrtrauter 0686c7aec2 Merge branch 'main' of https://github.com/KeksPirates/SoftwareManager 2026-02-06 17:52:21 +01:00
Vxrtrauter 6be1784903 feat: add functionality to retrieve and bind IP address of network interfaces 2026-02-06 17:50:42 +01:00
Vxrtrauter 4589322f0a feat: initialize network interface state and log interface details 2026-02-05 19:15:39 +01:00
shayaa 8132d789ef Add activity section to README
Added activity section with analytics image to README.
2026-02-05 17:35:38 +01:00
8 changed files with 97 additions and 46 deletions
+3
View File
@@ -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.
- This tool is for educational purposes only. Use it responsibly.
## Activity
![Alt](https://repobeats.axiom.co/api/embed/3a61ea02ffdeb9ed5dd948d974a21e0d6cfae36c.svg "Repobeats analytics image")
## Star History
+41 -2
View File
@@ -13,6 +13,7 @@ from PySide6.QtWidgets import (
QSpinBox,
QCheckBox,
QFileDialog,
QComboBox
)
import platform
@@ -56,7 +57,6 @@ def settings_dialog(self):
autoresume_layout.addStretch()
autoresume_checkbox.setChecked(state.autoresume)
autoresume_checkbox.toggled.connect(lambda checked: setattr(state, 'autoresume', checked))
autoresume_layout.addWidget(autoresume_checkbox)
dialog.layout().addWidget(autoresume_container)
@@ -206,6 +206,35 @@ def settings_dialog(self):
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)
dialog.layout().addWidget(interface_container)
###############
# SAVE/CANCEL #
###############
@@ -214,7 +243,17 @@ def settings_dialog(self):
save_btn = QPushButton("Save")
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)
layout.addWidget(cancel_btn)
+12
View File
@@ -1,4 +1,5 @@
from core.utils.general.logs import consoleLog
from core.utils.data.state import state
import psutil
addrs = psutil.net_if_addrs()
@@ -38,3 +39,14 @@ def list_interfaces() -> None:
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
+25 -2
View File
@@ -1,5 +1,6 @@
import time
from core.utils.general.wrappers import run_thread
from core.network.interface import get_interface_ip
import threading
import libtorrent as lt
from core.utils.data.state import state
@@ -15,6 +16,7 @@ def init_session():
state.dl_session = lt.session()
settings = {
"upload_rate_limit": state.up_speed_limit,
"download_rate_limit": state.down_speed_limit,
@@ -27,7 +29,14 @@ def init_session():
"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)
@@ -45,7 +54,6 @@ def add_download(magnet_uri, dl_path=state.download_path):
consoleLog("Skipping, download already running...")
return
magnetdl = lt.parse_magnet_uri(magnet_uri)
magnetdl.save_path = dl_path
@@ -99,5 +107,20 @@ def update_settings():
"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)
+3 -1
View File
@@ -4,7 +4,7 @@ from core.utils.general.logs import consoleLog
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:
state.api_url = apiurl
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
if max_downloads is not None:
state.max_downloads = max_downloads
if bound_interface is not None:
state.bound_interface = None if bound_interface == "None" else bound_interface
update_settings()
+3
View File
@@ -27,6 +27,9 @@ class AppState(QObject):
self.dl_session: Any = None
self.active_downloads: List[str] = {}
self.window_transparency: bool = False
self.interfaces: List = []
self.active_interfaces: List = []
self.bound_interface: str = None
@property
def image_path(self) -> str:
+10 -6
View File
@@ -6,7 +6,8 @@ from core.utils.general.logs import get_download_logs
from core.utils.general.shutdown import closehelper, shutdown_event
from core.utils.general.wrappers import run_thread
from core.utils.general.loghandler import split_data, check_completed
from core.network.interface import list_interfaces
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 PySide6 import QtWidgets
from PySide6.QtCore import Qt
@@ -46,14 +47,17 @@ if __name__ == "__main__":
if args.debug:
state.debug = args.debug # override of read_config
signal.signal(signal.SIGINT, keyboardinterrupthandler)
run_thread(threading.Thread(target=send_notification, args=(shutdown_event,), daemon=True))
consoleLog("Started send_notification thread")
run_thread(threading.Thread(target=update_log, args=(shutdown_event,), daemon=True))
consoleLog("Started update_log thread")
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
consoleLog("Started check_completed thread")
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))
consoleLog("Started Thread: send_notification")
run_thread(threading.Thread(target=update_log, args=(shutdown_event,), daemon=True))
consoleLog("Started Thread: update_log")
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
consoleLog("Launching GUI")
run_gui()
-35
View File
@@ -1,35 +0,0 @@
import psutil
addrs = psutil.net_if_addrs()
stats = psutil.net_if_stats()
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:
print(f'"{interface}": True')
active.append(interface)
print(active)
return active
get_active_interfaces()
# print(addrs)
# print()
# sigmer = list()
# for interface in stats.keys():
# print(f"{interface}: UP = {stats[interface].isup}")
# sigmer.append(f"{interface}: {stats[interface].isup}")
# print(sigmer)