mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-04 01:49:42 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7188a515a | |||
| 7b2522438c | |||
| 0686c7aec2 | |||
| 6be1784903 | |||
| 4589322f0a | |||
| 8132d789ef |
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from PySide6.QtWidgets import (
|
|||||||
QSpinBox,
|
QSpinBox,
|
||||||
QCheckBox,
|
QCheckBox,
|
||||||
QFileDialog,
|
QFileDialog,
|
||||||
|
QComboBox
|
||||||
)
|
)
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
@@ -56,7 +57,6 @@ 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)
|
dialog.layout().addWidget(autoresume_container)
|
||||||
|
|
||||||
@@ -206,6 +206,35 @@ def settings_dialog(self):
|
|||||||
max_downloads.setFixedHeight(30)
|
max_downloads.setFixedHeight(30)
|
||||||
dialog.layout().addWidget(max_downloads_container)
|
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 #
|
# SAVE/CANCEL #
|
||||||
###############
|
###############
|
||||||
@@ -214,7 +243,17 @@ 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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -150,6 +150,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
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()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from core.utils.general.logs import consoleLog
|
from core.utils.general.logs import consoleLog
|
||||||
|
from core.utils.data.state import state
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
addrs = psutil.net_if_addrs()
|
addrs = psutil.net_if_addrs()
|
||||||
@@ -38,3 +39,14 @@ def list_interfaces() -> None:
|
|||||||
status = "INACTIVE"
|
status = "INACTIVE"
|
||||||
consoleLog(f"Found Interface: {interface} [{status}]")
|
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,
|
||||||
@@ -26,8 +28,15 @@ def init_session():
|
|||||||
"connections_limit": state.max_connections,
|
"connections_limit": state.max_connections,
|
||||||
"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
|
||||||
|
|
||||||
@@ -98,6 +106,21 @@ def update_settings():
|
|||||||
"connections_limit": state.max_connections,
|
"connections_limit": state.max_connections,
|
||||||
"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)
|
||||||
|
|
||||||
|
def update_bound_interface():
|
||||||
|
|
||||||
|
settings = { "outgoing_interfaces": state.bound_interface }
|
||||||
|
|
||||||
|
state.dl_session.apply_settings(settings)
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ class AppState(QObject):
|
|||||||
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,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.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
|
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
|
||||||
@@ -46,14 +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)
|
||||||
run_thread(threading.Thread(target=send_notification, args=(shutdown_event,), daemon=True))
|
consoleLog("Starting SoftwareManager...")
|
||||||
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...")
|
consoleLog("Fetching Network Interfaces...")
|
||||||
list_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("Started Thread: check_completed")
|
||||||
consoleLog("Launching GUI")
|
consoleLog("Launching GUI")
|
||||||
run_gui()
|
run_gui()
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
Reference in New Issue
Block a user