From 2beabbb1bab6e415a88705c21b41f597a6c7b2c8 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Mon, 16 Mar 2026 14:24:23 +0100 Subject: [PATCH 01/13] refactor: improve search functionality and code organization in GUI components --- src/core/interface/gui.py | 4 ++-- src/core/interface/utils/searchhelper.py | 12 ++++++++++-- src/core/interface/utils/svghelper.py | 3 +-- src/core/interface/utils/tabhelper.py | 6 ++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index 50dfbf5..1312728 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -8,11 +8,11 @@ from core.interface.dialogs.downloadlist import download_list_update from core.interface.dialogs.update import get_version, UpdateDialog from core.utils.logging.logs import consoleLog, flush_log_buffer from core.interface.dialogs.downloadmodel import DownloadModel -from core.interface.utils.searchhelper import return_pressed from core.interface.dialogs.settings import settings_dialog from core.interface.assets.base64_icons import logo_base64 from core.interface.dialogs.eventfilter import eventFilter from core.utils.network.download import download_selected +from core.interface.utils.searchhelper import run_search from core.interface.utils.tabhelper import create_tab from core.utils.general.shutdown import closehelper from core.utils.general.wrappers import run_thread @@ -295,7 +295,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.searchbar.setEnabled(False) def _search_thread(): try: - return_pressed(self) + run_search(self) except Exception as e: consoleLog(f"Search error: {e}", True) self.search_results_signal.emit([]) diff --git a/src/core/interface/utils/searchhelper.py b/src/core/interface/utils/searchhelper.py index 118d754..09a49fb 100644 --- a/src/core/interface/utils/searchhelper.py +++ b/src/core/interface/utils/searchhelper.py @@ -5,22 +5,30 @@ from core.data.scrapers.monkrus import init_m0nkrus from core.utils.logging.logs import consoleLog from core.utils.data.state import state +# Call initialization function for each tracker / site init_rutracker() init_uztracker() init_m0nkrus() init_steamrip() -def return_pressed(self): + +def run_search(self) -> None: self.show_empty_results(False) + search_text = self.searchbar.text() + # Check for empty search if search_text == "": consoleLog("Error: Can't search for nothing") return consoleLog(f"User searched for: {search_text}") + # Get current tracker and call its search function tracker = state.trackers[state.currenttracker] scrapefunc = tracker["scrapeFunc"] state.posts = scrapefunc(search_text) - + + # Late to prevent circular import from core.interface.gui import MainWindow + + # Update GUI Headers MainWindow._instance.search_results_signal.emit(tracker["headers"]) diff --git a/src/core/interface/utils/svghelper.py b/src/core/interface/utils/svghelper.py index 393b321..bf4f25f 100644 --- a/src/core/interface/utils/svghelper.py +++ b/src/core/interface/utils/svghelper.py @@ -6,11 +6,10 @@ from PySide6.QtGui import QIcon, QPixmap, QPainter from PySide6.QtSvg import QSvgRenderer import darkdetect - +# Check if darkmode is enabled def _is_dark_mode(): return darkdetect.isDark() - def svg_icon(svg_str, size=20): app = QtWidgets.QApplication.instance() if app: diff --git a/src/core/interface/utils/tabhelper.py b/src/core/interface/utils/tabhelper.py index 74c4dd1..f22304b 100644 --- a/src/core/interface/utils/tabhelper.py +++ b/src/core/interface/utils/tabhelper.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout - +# Tab creation helper function def create_tab(title, searchbar, software_list, tabs, dlbutton, layout2): tab = QWidget() layout = QVBoxLayout() @@ -16,9 +16,7 @@ def create_tab(title, searchbar, software_list, tabs, dlbutton, layout2): tabs.addTab(tab, title) return tab -################# -# SETTINGS TABS # -################# +# Settings Tabs def general_tab(title, autoresume, update_checkbox, transparent_window, tabs): tab = QWidget() layout = QVBoxLayout() From 53d4a240a07520db920aaed14b8d591d3fd12891 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:33:33 +0100 Subject: [PATCH 02/13] refactor state.py and add comments to it, add another check to version getter. --- src/core/interface/dialogs/update.py | 3 +- src/core/utils/config/settings.py | 2 +- src/core/utils/data/state.py | 67 +++++++++++++++------------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/core/interface/dialogs/update.py b/src/core/interface/dialogs/update.py index f2184e0..8cb0a21 100644 --- a/src/core/interface/dialogs/update.py +++ b/src/core/interface/dialogs/update.py @@ -24,7 +24,7 @@ def _get_build_info_path() -> (Path | None): def get_version() -> None: # Get build info filepath build_info_path = _get_build_info_path() - if os.path.exists(build_info_path): + if build_info_path and os.path.exists(build_info_path): with open(build_info_path, "r") as f: build_info = json.load(f) state.version = build_info.get("version") @@ -46,3 +46,4 @@ class UpdateDialog(QDialog): response = msg.exec_() if response == QMessageBox.StandardButton.Ok: download_update(assets) + diff --git a/src/core/utils/config/settings.py b/src/core/utils/config/settings.py index 9dd986b..5001dc7 100644 --- a/src/core/utils/config/settings.py +++ b/src/core/utils/config/settings.py @@ -1,7 +1,7 @@ from core.network.libtorrent_int import update_settings from core.utils.logging.logs import consoleLog from core.utils.data.state import state -from .config import create_config +from core.utils.config.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, bound_interface=None): diff --git a/src/core/utils/data/state.py b/src/core/utils/data/state.py index a98223a..de42ee9 100644 --- a/src/core/utils/data/state.py +++ b/src/core/utils/data/state.py @@ -9,43 +9,50 @@ class AppState(QObject): def __init__(self): super().__init__() - self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seederm leecher - self.version: str = "dev" - self._image_path: str = "" - self.ignore_updates: bool = False - self.debug: bool = False - self.autoresume: bool = True - self.currenttracker: str = "rutracker" - self.trackertable: QTableWidget - self.trackers: Dict[str,Dict[str,Any]] = {} # each tracker should add itself here - ''' - an example: - "rutracker" : { - "name" : "rutracker", # name of the tracker - "headers" : ["author", "title"], # keys shown in the table - "scrapeFunc" : function, - } - ''' - self.api_url: str = "https://api.michijackson.xyz" - self.seeded_magnets: set = set() + # General + self.version: str = "Unknown" + self.debug: bool = False + self.main_window: Any = None + self.loop_running: bool = False + self.shutdown_event = threading.Event() + self.log_buffer: List[str] = [] + + # Paths + self.settings_path: str = "" + self._image_path: str = "" self.download_path: str = str(Path.home() / "Downloads") + + # GUI + self.window_transparency: bool = False + self.trackertable: QTableWidget + self.interfaces: List = [] + self.active_interfaces: List = [] + self.bound_interface: Any = None + + # Trackers / Scraping + self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers + self.currenttracker: str = "rutracker" + self.api_url: str = "https://api.michijackson.xyz" + self.trackers: Dict[str,Dict[str,Any]] = {} # each tracker should add itself here + # an example: + # "rutracker" : { + # "name" : "rutracker", + # "headers" : ["author", "title"], + # "scrapeFunc" : function, + # } + + # LibTorrent / Download related stuff + self.dl_session: Any = None + self.active_downloads: Dict = {} + self.seeded_magnets: set = set() + self.ignore_updates: bool = False + self.autoresume: bool = True self.up_speed_limit: int = 0 self.down_speed_limit: int = 0 self.max_connections: int = 200 self.max_downloads: int = 10 - self.settings_path: str = "" - self.dl_session: Any = None - self.active_downloads: Dict = {} - self.window_transparency: bool = False - self.interfaces: List = [] - self.active_interfaces: List = [] - self.bound_interface: Any = None - self.log_buffer: List[str] = [] self.downloads_lock = threading.RLock() - self.main_window: Any = None - self.loop_running: bool = False - self.shutdown_event = threading.Event() @property def image_path(self) -> str: From 325eda54fa5786c55fd576a253b35ed680ae5b93 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Thu, 19 Mar 2026 20:42:28 +0100 Subject: [PATCH 03/13] refactor: remove core/ folder --- .github/workflows/dev.yml | 2 +- src/{core => }/data/__init__.py | 0 src/{core => }/data/scrapers/__init__.py | 0 src/{core => }/data/scrapers/monkrus.py | 4 +- .../data/scrapers/provider/buzzheavier.py | 0 .../data/scrapers/provider/gofile.py | 2 +- src/{core => }/data/scrapers/rutracker.py | 8 +-- src/{core => }/data/scrapers/steamrip.py | 8 +-- src/{core => }/data/scrapers/uztracker.py | 6 +-- .../interface/assets/base64_icons.py | 0 src/{core => }/interface/assets/logo.png | Bin .../interface/dialogs/contextmenu.py | 4 +- .../interface/dialogs/downloadlist.py | 10 ++-- .../interface/dialogs/downloadmodel.py | 4 +- .../interface/dialogs/elideditemdelegate.py | 2 +- .../interface/dialogs/eventfilter.py | 4 +- .../interface/dialogs/hoverrowdelegate.py | 4 +- src/{core => }/interface/dialogs/image.py | 2 +- .../interface/dialogs/pauseresumedelegate.py | 8 +-- src/{core => }/interface/dialogs/settings.py | 14 ++--- src/{core => }/interface/dialogs/theme.py | 0 .../interface/dialogs/trackerhoverdelegate.py | 2 +- .../interface/dialogs/trackertable.py | 2 +- src/{core => }/interface/dialogs/update.py | 8 +-- src/{core => }/interface/gui.py | 50 +++++++++--------- .../interface/utils/searchhelper.py | 14 ++--- src/{core => }/interface/utils/svghelper.py | 6 +-- src/{core => }/interface/utils/tabhelper.py | 0 src/main.py | 24 ++++----- src/{core => }/network/__init__.py | 0 .../network/direct_download/__init__.py | 5 +- .../network/direct_download/handle.py | 8 +-- .../network/direct_download/status.py | 0 .../network/direct_download/utils.py | 2 +- src/{core => }/network/interface.py | 4 +- src/{core => }/network/libtorrent_int.py | 8 +-- src/{core => }/network/libtorrent_misc.py | 6 +-- src/{core => }/network/libtorrent_wrapper.py | 4 +- src/{core => }/utils/__init__.py | 0 src/{core => }/utils/config/config.py | 2 +- src/{core => }/utils/config/settings.py | 9 ++-- src/{core => }/utils/data/models.py | 0 src/{core => }/utils/data/state.py | 0 src/{core => }/utils/data/tracker.py | 2 +- src/{core => }/utils/general/shutdown.py | 6 +-- src/{core => }/utils/general/wrappers.py | 2 +- src/{core => }/utils/logging/loghandler.py | 6 +-- src/{core => }/utils/logging/logs.py | 8 +-- src/{core => }/utils/network/download.py | 14 ++--- src/{core => }/utils/network/jsonhandler.py | 0 .../utils/network/update_checker.py | 4 +- src/{core => }/utils/network/updater.py | 6 +-- 52 files changed, 142 insertions(+), 142 deletions(-) rename src/{core => }/data/__init__.py (100%) rename src/{core => }/data/scrapers/__init__.py (100%) rename src/{core => }/data/scrapers/monkrus.py (96%) rename src/{core => }/data/scrapers/provider/buzzheavier.py (100%) rename src/{core => }/data/scrapers/provider/gofile.py (96%) rename src/{core => }/data/scrapers/rutracker.py (87%) rename src/{core => }/data/scrapers/steamrip.py (92%) rename src/{core => }/data/scrapers/uztracker.py (91%) rename src/{core => }/interface/assets/base64_icons.py (100%) rename src/{core => }/interface/assets/logo.png (100%) rename src/{core => }/interface/dialogs/contextmenu.py (98%) rename src/{core => }/interface/dialogs/downloadlist.py (93%) rename src/{core => }/interface/dialogs/downloadmodel.py (98%) rename src/{core => }/interface/dialogs/elideditemdelegate.py (93%) rename src/{core => }/interface/dialogs/eventfilter.py (95%) rename src/{core => }/interface/dialogs/hoverrowdelegate.py (86%) rename src/{core => }/interface/dialogs/image.py (96%) rename src/{core => }/interface/dialogs/pauseresumedelegate.py (93%) rename src/{core => }/interface/dialogs/settings.py (96%) rename src/{core => }/interface/dialogs/theme.py (100%) rename src/{core => }/interface/dialogs/trackerhoverdelegate.py (90%) rename src/{core => }/interface/dialogs/trackertable.py (95%) rename src/{core => }/interface/dialogs/update.py (88%) rename src/{core => }/interface/gui.py (90%) rename src/{core => }/interface/utils/searchhelper.py (67%) rename src/{core => }/interface/utils/svghelper.py (99%) rename src/{core => }/interface/utils/tabhelper.py (100%) rename src/{core => }/network/__init__.py (100%) rename src/{core => }/network/direct_download/__init__.py (89%) rename src/{core => }/network/direct_download/handle.py (98%) rename src/{core => }/network/direct_download/status.py (100%) rename src/{core => }/network/direct_download/utils.py (97%) rename src/{core => }/network/interface.py (95%) rename src/{core => }/network/libtorrent_int.py (96%) rename src/{core => }/network/libtorrent_misc.py (95%) rename src/{core => }/network/libtorrent_wrapper.py (68%) rename src/{core => }/utils/__init__.py (100%) rename src/{core => }/utils/config/config.py (98%) rename src/{core => }/utils/config/settings.py (84%) rename src/{core => }/utils/data/models.py (100%) rename src/{core => }/utils/data/state.py (100%) rename src/{core => }/utils/data/tracker.py (94%) rename src/{core => }/utils/general/shutdown.py (61%) rename src/{core => }/utils/general/wrappers.py (84%) rename src/{core => }/utils/logging/loghandler.py (85%) rename src/{core => }/utils/logging/logs.py (97%) rename src/{core => }/utils/network/download.py (82%) rename src/{core => }/utils/network/jsonhandler.py (100%) rename src/{core => }/utils/network/update_checker.py (93%) rename src/{core => }/utils/network/updater.py (94%) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 2ee2ba3..03c93f6 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -106,7 +106,7 @@ jobs: run: | python -c " from PIL import Image - img = Image.open('src/core/interface/assets/logo.png') + img = Image.open('src/interface/assets/logo.png') img.save('app_icon.ico', format='ICO', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)]) print('Icon converted successfully') " diff --git a/src/core/data/__init__.py b/src/data/__init__.py similarity index 100% rename from src/core/data/__init__.py rename to src/data/__init__.py diff --git a/src/core/data/scrapers/__init__.py b/src/data/scrapers/__init__.py similarity index 100% rename from src/core/data/scrapers/__init__.py rename to src/data/scrapers/__init__.py diff --git a/src/core/data/scrapers/monkrus.py b/src/data/scrapers/monkrus.py similarity index 96% rename from src/core/data/scrapers/monkrus.py rename to src/data/scrapers/monkrus.py index bf4c1cf..613dc8d 100644 --- a/src/core/data/scrapers/monkrus.py +++ b/src/data/scrapers/monkrus.py @@ -1,5 +1,5 @@ -from core.utils.data.tracker import get_magnet_link -from core.utils.data.state import state +from utils.data.tracker import get_magnet_link +from utils.data.state import state from bs4 import BeautifulSoup from typing import Dict import requests diff --git a/src/core/data/scrapers/provider/buzzheavier.py b/src/data/scrapers/provider/buzzheavier.py similarity index 100% rename from src/core/data/scrapers/provider/buzzheavier.py rename to src/data/scrapers/provider/buzzheavier.py diff --git a/src/core/data/scrapers/provider/gofile.py b/src/data/scrapers/provider/gofile.py similarity index 96% rename from src/core/data/scrapers/provider/gofile.py rename to src/data/scrapers/provider/gofile.py index 342b789..a7a86a8 100644 --- a/src/core/data/scrapers/provider/gofile.py +++ b/src/data/scrapers/provider/gofile.py @@ -1,4 +1,4 @@ -from core.utils.logging.logs import consoleLog +from utils.logging.logs import consoleLog import requests import re diff --git a/src/core/data/scrapers/rutracker.py b/src/data/scrapers/rutracker.py similarity index 87% rename from src/core/data/scrapers/rutracker.py rename to src/data/scrapers/rutracker.py index e6a0905..15acbce 100644 --- a/src/core/data/scrapers/rutracker.py +++ b/src/data/scrapers/rutracker.py @@ -1,7 +1,7 @@ -from core.utils.network.jsonhandler import split_data, format_data -from core.utils.data.tracker import get_magnet_link -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.network.jsonhandler import split_data, format_data +from utils.data.tracker import get_magnet_link +from utils.logging.logs import consoleLog +from utils.data.state import state from typing import Dict import requests diff --git a/src/core/data/scrapers/steamrip.py b/src/data/scrapers/steamrip.py similarity index 92% rename from src/core/data/scrapers/steamrip.py rename to src/data/scrapers/steamrip.py index 24c0165..22f0735 100644 --- a/src/core/data/scrapers/steamrip.py +++ b/src/data/scrapers/steamrip.py @@ -1,7 +1,7 @@ -from core.data.scrapers.provider.buzzheavier import scrape_buzzheavier -from core.data.scrapers.provider.gofile import scrape_gofile -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from data.scrapers.provider.buzzheavier import scrape_buzzheavier +from data.scrapers.provider.gofile import scrape_gofile +from utils.logging.logs import consoleLog +from utils.data.state import state from bs4 import BeautifulSoup from typing import Dict import webbrowser diff --git a/src/core/data/scrapers/uztracker.py b/src/data/scrapers/uztracker.py similarity index 91% rename from src/core/data/scrapers/uztracker.py rename to src/data/scrapers/uztracker.py index c5b118b..3c55c72 100644 --- a/src/core/data/scrapers/uztracker.py +++ b/src/data/scrapers/uztracker.py @@ -1,6 +1,6 @@ -from core.utils.data.tracker import get_magnet_link -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.data.tracker import get_magnet_link +from utils.logging.logs import consoleLog +from utils.data.state import state from urllib.parse import urljoin from bs4 import BeautifulSoup from typing import Dict diff --git a/src/core/interface/assets/base64_icons.py b/src/interface/assets/base64_icons.py similarity index 100% rename from src/core/interface/assets/base64_icons.py rename to src/interface/assets/base64_icons.py diff --git a/src/core/interface/assets/logo.png b/src/interface/assets/logo.png similarity index 100% rename from src/core/interface/assets/logo.png rename to src/interface/assets/logo.png diff --git a/src/core/interface/dialogs/contextmenu.py b/src/interface/dialogs/contextmenu.py similarity index 98% rename from src/core/interface/dialogs/contextmenu.py rename to src/interface/dialogs/contextmenu.py index 1b7362e..fb8c98f 100644 --- a/src/core/interface/dialogs/contextmenu.py +++ b/src/interface/dialogs/contextmenu.py @@ -1,7 +1,7 @@ -from core.utils.logging.logs import consoleLog, remove_download_log +from utils.logging.logs import consoleLog, remove_download_log from PySide6.QtWidgets import QMessageBox -from core.utils.data.state import state from PySide6.QtCore import Qt, QPoint +from utils.data.state import state from PySide6 import QtWidgets import subprocess import platform diff --git a/src/core/interface/dialogs/downloadlist.py b/src/interface/dialogs/downloadlist.py similarity index 93% rename from src/core/interface/dialogs/downloadlist.py rename to src/interface/dialogs/downloadlist.py index 1c6e66b..d381e24 100644 --- a/src/core/interface/dialogs/downloadlist.py +++ b/src/interface/dialogs/downloadlist.py @@ -1,9 +1,9 @@ -from core.interface.dialogs.pauseresumedelegate import PauseResumeDelegate -from core.interface.dialogs.hoverrowdelegate import HoverRowDelegate -from core.interface.dialogs.downloadmodel import DownloadModel -from core.interface.dialogs.theme import _table_stylesheet +from interface.dialogs.pauseresumedelegate import PauseResumeDelegate +from interface.dialogs.hoverrowdelegate import HoverRowDelegate +from interface.dialogs.downloadmodel import DownloadModel +from interface.dialogs.theme import _table_stylesheet from PySide6.QtWidgets import QTableView, QHeaderView -from core.utils.logging.logs import consoleLog +from utils.logging.logs import consoleLog from PySide6 import QtWidgets from PySide6.QtCore import Qt diff --git a/src/core/interface/dialogs/downloadmodel.py b/src/interface/dialogs/downloadmodel.py similarity index 98% rename from src/core/interface/dialogs/downloadmodel.py rename to src/interface/dialogs/downloadmodel.py index 7785fe9..6cb0237 100644 --- a/src/core/interface/dialogs/downloadmodel.py +++ b/src/interface/dialogs/downloadmodel.py @@ -1,6 +1,6 @@ from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import consoleLog +from utils.data.state import state import libtorrent as lt import subprocess import platform diff --git a/src/core/interface/dialogs/elideditemdelegate.py b/src/interface/dialogs/elideditemdelegate.py similarity index 93% rename from src/core/interface/dialogs/elideditemdelegate.py rename to src/interface/dialogs/elideditemdelegate.py index 5ef8671..6957ae2 100644 --- a/src/core/interface/dialogs/elideditemdelegate.py +++ b/src/interface/dialogs/elideditemdelegate.py @@ -1,4 +1,4 @@ -from core.interface.dialogs.theme import _theme_colors +from interface.dialogs.theme import _theme_colors from PySide6.QtWidgets import QStyledItemDelegate from PySide6.QtCore import Qt diff --git a/src/core/interface/dialogs/eventfilter.py b/src/interface/dialogs/eventfilter.py similarity index 95% rename from src/core/interface/dialogs/eventfilter.py rename to src/interface/dialogs/eventfilter.py index 4bddc92..760ccab 100644 --- a/src/core/interface/dialogs/eventfilter.py +++ b/src/interface/dialogs/eventfilter.py @@ -1,5 +1,5 @@ -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import consoleLog +from utils.data.state import state from PySide6.QtCore import QEvent def eventFilter(self, obj, event): diff --git a/src/core/interface/dialogs/hoverrowdelegate.py b/src/interface/dialogs/hoverrowdelegate.py similarity index 86% rename from src/core/interface/dialogs/hoverrowdelegate.py rename to src/interface/dialogs/hoverrowdelegate.py index 3293099..11b16ab 100644 --- a/src/core/interface/dialogs/hoverrowdelegate.py +++ b/src/interface/dialogs/hoverrowdelegate.py @@ -1,11 +1,11 @@ -from core.interface.dialogs.theme import _theme_colors +from interface.dialogs.theme import _theme_colors from PySide6.QtWidgets import QStyledItemDelegate from PySide6 import QtWidgets class HoverRowDelegate(QStyledItemDelegate): def paint(self, painter, option, index): - from core.interface.gui import MainWindow + from interface.gui import MainWindow opt = QtWidgets.QStyleOptionViewItem(option) option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus diff --git a/src/core/interface/dialogs/image.py b/src/interface/dialogs/image.py similarity index 96% rename from src/core/interface/dialogs/image.py rename to src/interface/dialogs/image.py index 0abb90c..f39c327 100644 --- a/src/core/interface/dialogs/image.py +++ b/src/interface/dialogs/image.py @@ -1,6 +1,6 @@ from PySide6.QtGui import QImage, QPixmap -from core.utils.data.state import state from PySide6.QtWidgets import QLabel +from utils.data.state import state from PySide6.QtCore import Qt import os diff --git a/src/core/interface/dialogs/pauseresumedelegate.py b/src/interface/dialogs/pauseresumedelegate.py similarity index 93% rename from src/core/interface/dialogs/pauseresumedelegate.py rename to src/interface/dialogs/pauseresumedelegate.py index 7570a5b..ec4668b 100644 --- a/src/core/interface/dialogs/pauseresumedelegate.py +++ b/src/interface/dialogs/pauseresumedelegate.py @@ -1,8 +1,8 @@ -from core.interface.dialogs.theme import _theme_colors, SVG_PLAY, SVG_PAUSE, SVG_FOLDER +from interface.dialogs.theme import _theme_colors, SVG_PLAY, SVG_PAUSE, SVG_FOLDER from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QHBoxLayout -from core.interface.utils.svghelper import svg_icon +from interface.utils.svghelper import svg_icon from PySide6.QtCore import Qt, QSize, Signal -from core.utils.data.state import state +from utils.data.state import state from PySide6 import QtWidgets import libtorrent as lt @@ -10,7 +10,7 @@ class PauseResumeDelegate(QStyledItemDelegate): clicked = Signal(int) def paint(self, painter, option, index): - from core.interface.gui import MainWindow + from interface.gui import MainWindow opt = QtWidgets.QStyleOptionViewItem(option) option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus diff --git a/src/core/interface/dialogs/settings.py b/src/interface/dialogs/settings.py similarity index 96% rename from src/core/interface/dialogs/settings.py rename to src/interface/dialogs/settings.py index 5589b68..56e6761 100644 --- a/src/core/interface/dialogs/settings.py +++ b/src/interface/dialogs/settings.py @@ -1,11 +1,11 @@ -from core.interface.utils.tabhelper import general_tab -from core.interface.utils.tabhelper import network_tab -from core.utils.config.settings import save_settings -from core.interface.utils.tabhelper import paths_tab -from core.interface.utils.svghelper import svg_icon -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from interface.utils.tabhelper import general_tab +from interface.utils.tabhelper import network_tab +from utils.config.settings import save_settings +from interface.utils.tabhelper import paths_tab +from interface.utils.svghelper import svg_icon +from utils.logging.logs import consoleLog from PySide6.QtCore import Qt, QSize +from utils.data.state import state from PySide6 import QtWidgets from PySide6.QtWidgets import ( QLineEdit, diff --git a/src/core/interface/dialogs/theme.py b/src/interface/dialogs/theme.py similarity index 100% rename from src/core/interface/dialogs/theme.py rename to src/interface/dialogs/theme.py diff --git a/src/core/interface/dialogs/trackerhoverdelegate.py b/src/interface/dialogs/trackerhoverdelegate.py similarity index 90% rename from src/core/interface/dialogs/trackerhoverdelegate.py rename to src/interface/dialogs/trackerhoverdelegate.py index c83af6d..f6af434 100644 --- a/src/core/interface/dialogs/trackerhoverdelegate.py +++ b/src/interface/dialogs/trackerhoverdelegate.py @@ -1,4 +1,4 @@ -from core.interface.dialogs.theme import _theme_colors +from interface.dialogs.theme import _theme_colors from PySide6.QtWidgets import QStyledItemDelegate class TrackerHoverDelegate(QStyledItemDelegate): diff --git a/src/core/interface/dialogs/trackertable.py b/src/interface/dialogs/trackertable.py similarity index 95% rename from src/core/interface/dialogs/trackertable.py rename to src/interface/dialogs/trackertable.py index a382e75..68f188d 100644 --- a/src/core/interface/dialogs/trackertable.py +++ b/src/interface/dialogs/trackertable.py @@ -1,4 +1,4 @@ -from core.interface.dialogs.theme import _table_stylesheet +from interface.dialogs.theme import _table_stylesheet from PySide6.QtWidgets import QTableWidget from PySide6 import QtWidgets from PySide6.QtCore import Qt diff --git a/src/core/interface/dialogs/update.py b/src/interface/dialogs/update.py similarity index 88% rename from src/core/interface/dialogs/update.py rename to src/interface/dialogs/update.py index 8cb0a21..a45a329 100644 --- a/src/core/interface/dialogs/update.py +++ b/src/interface/dialogs/update.py @@ -1,8 +1,8 @@ -from core.utils.network.update_checker import get_updates -from core.utils.network.updater import download_update +from utils.network.update_checker import get_updates from PySide6.QtWidgets import QDialog, QMessageBox -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.network.updater import download_update +from utils.logging.logs import consoleLog +from utils.data.state import state from pathlib import Path import platform import json diff --git a/src/core/interface/gui.py b/src/interface/gui.py similarity index 90% rename from src/core/interface/gui.py rename to src/interface/gui.py index 1312728..7eb289b 100644 --- a/src/core/interface/gui.py +++ b/src/interface/gui.py @@ -1,25 +1,25 @@ -from core.interface.dialogs.trackerhoverdelegate import TrackerHoverDelegate -from core.interface.dialogs.elideditemdelegate import ElidedItemDelegate -from core.interface.dialogs.trackertable import _create_tracker_table -from core.interface.dialogs.downloadlist import _create_download_list -from core.interface.assets.base64_icons import settings_white_base64 -from core.interface.assets.base64_icons import settings_black_base64 -from core.interface.dialogs.downloadlist import download_list_update -from core.interface.dialogs.update import get_version, UpdateDialog -from core.utils.logging.logs import consoleLog, flush_log_buffer -from core.interface.dialogs.downloadmodel import DownloadModel -from core.interface.dialogs.settings import settings_dialog -from core.interface.assets.base64_icons import logo_base64 -from core.interface.dialogs.eventfilter import eventFilter -from core.utils.network.download import download_selected -from core.interface.utils.searchhelper import run_search -from core.interface.utils.tabhelper import create_tab -from core.utils.general.shutdown import closehelper -from core.utils.general.wrappers import run_thread -from core.interface.dialogs.image import Image -from core.utils.data.state import state +from interface.dialogs.trackerhoverdelegate import TrackerHoverDelegate +from interface.dialogs.elideditemdelegate import ElidedItemDelegate +from interface.dialogs.trackertable import _create_tracker_table +from interface.dialogs.downloadlist import _create_download_list +from interface.assets.base64_icons import settings_white_base64 +from interface.assets.base64_icons import settings_black_base64 +from interface.dialogs.downloadlist import download_list_update +from interface.dialogs.update import get_version, UpdateDialog +from utils.logging.logs import consoleLog, flush_log_buffer +from interface.dialogs.downloadmodel import DownloadModel +from interface.dialogs.settings import settings_dialog +from interface.assets.base64_icons import logo_base64 +from interface.dialogs.eventfilter import eventFilter +from utils.network.download import download_selected +from interface.utils.searchhelper import run_search +from interface.utils.tabhelper import create_tab +from utils.general.shutdown import closehelper +from utils.general.wrappers import run_thread +from interface.dialogs.image import Image +from utils.data.state import state -import core.interface.dialogs.contextmenu +import interface.dialogs.contextmenu from PySide6 import QtWidgets from PySide6.QtCore import ( @@ -86,7 +86,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): consoleLog(f"Could not set app ID: {e}") # Get current version - # core.interface.dialogs.update + # interface.dialogs.update get_version() UpdateDialog() @@ -137,7 +137,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): containerLayout.addWidget(self.searchbar) containerLayout.addWidget(state.trackertable) - # core.interface.dialogs.hoverrowdelegate + # interface.dialogs.hoverrowdelegate self.download_model = DownloadModel() self.downloadList = _create_download_list(self) self.downloadList.viewport().installEventFilter(self) @@ -246,7 +246,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.active_timer.timeout.connect(self.show_empty_downloads) self.active_timer.start(500) - self._context_menu = core.interface.dialogs.contextmenu.ContextMenu(self) + self._context_menu = interface.dialogs.contextmenu.ContextMenu(self) self.image_overlay = Image(self) def _apply_default_headers(self, table): @@ -384,7 +384,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): def closeEvent(self, event: QCloseEvent): closehelper() event.accept() - from core.utils.general.shutdown import force_exit + from utils.general.shutdown import force_exit force_exit() def _invalidate_hover_row(self, row): diff --git a/src/core/interface/utils/searchhelper.py b/src/interface/utils/searchhelper.py similarity index 67% rename from src/core/interface/utils/searchhelper.py rename to src/interface/utils/searchhelper.py index 09a49fb..ab006b0 100644 --- a/src/core/interface/utils/searchhelper.py +++ b/src/interface/utils/searchhelper.py @@ -1,9 +1,9 @@ -from core.data.scrapers.rutracker import init_rutracker -from core.data.scrapers.uztracker import init_uztracker -from core.data.scrapers.steamrip import init_steamrip -from core.data.scrapers.monkrus import init_m0nkrus -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from data.scrapers.rutracker import init_rutracker +from data.scrapers.uztracker import init_uztracker +from data.scrapers.steamrip import init_steamrip +from data.scrapers.monkrus import init_m0nkrus +from utils.logging.logs import consoleLog +from utils.data.state import state # Call initialization function for each tracker / site init_rutracker() @@ -28,7 +28,7 @@ def run_search(self) -> None: state.posts = scrapefunc(search_text) # Late to prevent circular import - from core.interface.gui import MainWindow + from interface.gui import MainWindow # Update GUI Headers MainWindow._instance.search_results_signal.emit(tracker["headers"]) diff --git a/src/core/interface/utils/svghelper.py b/src/interface/utils/svghelper.py similarity index 99% rename from src/core/interface/utils/svghelper.py rename to src/interface/utils/svghelper.py index bf4f25f..d9f8763 100644 --- a/src/core/interface/utils/svghelper.py +++ b/src/interface/utils/svghelper.py @@ -1,9 +1,7 @@ - - -from PySide6 import QtWidgets -from PySide6.QtCore import Qt, QByteArray from PySide6.QtGui import QIcon, QPixmap, QPainter +from PySide6.QtCore import Qt, QByteArray from PySide6.QtSvg import QSvgRenderer +from PySide6 import QtWidgets import darkdetect # Check if darkmode is enabled diff --git a/src/core/interface/utils/tabhelper.py b/src/interface/utils/tabhelper.py similarity index 100% rename from src/core/interface/utils/tabhelper.py rename to src/interface/utils/tabhelper.py diff --git a/src/main.py b/src/main.py index 66d6998..84f9552 100644 --- a/src/main.py +++ b/src/main.py @@ -1,15 +1,15 @@ -from core.network.libtorrent_misc import send_notification, update_log, check_deleted_files -from core.utils.logging.loghandler import split_data, check_completed, check_downloads -from core.network.interface import list_interfaces, init_interfaces -from core.utils.logging.logs import get_download_logs -from core.utils.logging.logs import set_main_window -from core.utils.general.shutdown import closehelper -from core.utils.general.wrappers import run_thread -from core.utils.general.shutdown import force_exit -from core.utils.config.config import read_config -from core.utils.logging.logs import consoleLog -from core.interface.gui import MainWindow -from core.utils.data.state import state +from network.libtorrent_misc import send_notification, update_log, check_deleted_files +from utils.logging.loghandler import split_data, check_completed, check_downloads +from network.interface import list_interfaces, init_interfaces +from utils.logging.logs import get_download_logs +from utils.logging.logs import set_main_window +from utils.general.shutdown import closehelper +from utils.general.wrappers import run_thread +from utils.general.shutdown import force_exit +from utils.config.config import read_config +from utils.logging.logs import consoleLog +from interface.gui import MainWindow +from utils.data.state import state from PySide6 import QtWidgets from PySide6.QtCore import Qt import qdarktheme diff --git a/src/core/network/__init__.py b/src/network/__init__.py similarity index 100% rename from src/core/network/__init__.py rename to src/network/__init__.py diff --git a/src/core/network/direct_download/__init__.py b/src/network/direct_download/__init__.py similarity index 89% rename from src/core/network/direct_download/__init__.py rename to src/network/direct_download/__init__.py index fdbc305..3d6f8b2 100644 --- a/src/core/network/direct_download/__init__.py +++ b/src/network/direct_download/__init__.py @@ -1,6 +1,7 @@ +from utils.logging.logs import consoleLog, add_download_log +from utils.data.state import state from typing import Optional -from core.utils.data.state import state -from core.utils.logging.logs import consoleLog, add_download_log + from .handle import DirectDownloadHandle from .utils import ( diff --git a/src/core/network/direct_download/handle.py b/src/network/direct_download/handle.py similarity index 98% rename from src/core/network/direct_download/handle.py rename to src/network/direct_download/handle.py index 4b4def6..023191b 100644 --- a/src/core/network/direct_download/handle.py +++ b/src/network/direct_download/handle.py @@ -1,8 +1,8 @@ -from core.utils.logging.logs import consoleLog, update_download_completed +from network.direct_download.status import DirectDownloadStatus, ChunkSpec +from utils.logging.logs import consoleLog, update_download_completed from concurrent.futures import ThreadPoolExecutor, as_completed -from .status import DirectDownloadStatus, ChunkSpec -from core.utils.data.state import state -from .utils import format_size +from network.direct_download.utils import format_size +from utils.data.state import state from typing import Optional import libtorrent as lt import threading diff --git a/src/core/network/direct_download/status.py b/src/network/direct_download/status.py similarity index 100% rename from src/core/network/direct_download/status.py rename to src/network/direct_download/status.py diff --git a/src/core/network/direct_download/utils.py b/src/network/direct_download/utils.py similarity index 97% rename from src/core/network/direct_download/utils.py rename to src/network/direct_download/utils.py index 238c6a7..20948f6 100644 --- a/src/core/network/direct_download/utils.py +++ b/src/network/direct_download/utils.py @@ -1,4 +1,4 @@ -from core.utils.logging.logs import consoleLog +from utils.logging.logs import consoleLog from urllib.parse import urlparse, unquote from typing import Optional import requests diff --git a/src/core/network/interface.py b/src/network/interface.py similarity index 95% rename from src/core/network/interface.py rename to src/network/interface.py index ff07f34..aca0e20 100644 --- a/src/core/network/interface.py +++ b/src/network/interface.py @@ -1,5 +1,5 @@ -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import consoleLog +from utils.data.state import state import psutil def get_net_interfaces(): diff --git a/src/core/network/libtorrent_int.py b/src/network/libtorrent_int.py similarity index 96% rename from src/core/network/libtorrent_int.py rename to src/network/libtorrent_int.py index b199bdd..f741f9e 100644 --- a/src/core/network/libtorrent_int.py +++ b/src/network/libtorrent_int.py @@ -1,7 +1,7 @@ -from core.network.interface import get_interface_ip -from core.utils.general.wrappers import run_thread -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from network.interface import get_interface_ip +from utils.general.wrappers import run_thread +from utils.logging.logs import consoleLog +from utils.data.state import state import libtorrent as lt import threading import platform diff --git a/src/core/network/libtorrent_misc.py b/src/network/libtorrent_misc.py similarity index 95% rename from src/core/network/libtorrent_misc.py rename to src/network/libtorrent_misc.py index 729809c..adfab0c 100644 --- a/src/core/network/libtorrent_misc.py +++ b/src/network/libtorrent_misc.py @@ -1,6 +1,6 @@ -from core.utils.logging.logs import update_download_completed_by_hash -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import update_download_completed_by_hash +from utils.logging.logs import consoleLog +from utils.data.state import state from plyer import notification import libtorrent as lt import time diff --git a/src/core/network/libtorrent_wrapper.py b/src/network/libtorrent_wrapper.py similarity index 68% rename from src/core/network/libtorrent_wrapper.py rename to src/network/libtorrent_wrapper.py index da9f4c2..bcf51a4 100644 --- a/src/core/network/libtorrent_wrapper.py +++ b/src/network/libtorrent_wrapper.py @@ -1,5 +1,5 @@ -from core.network.libtorrent_int import add_download -from core.utils.logging.logs import consoleLog +from network.libtorrent_int import add_download +from utils.logging.logs import consoleLog def add_magnet(uri): diff --git a/src/core/utils/__init__.py b/src/utils/__init__.py similarity index 100% rename from src/core/utils/__init__.py rename to src/utils/__init__.py diff --git a/src/core/utils/config/config.py b/src/utils/config/config.py similarity index 98% rename from src/core/utils/config/config.py rename to src/utils/config/config.py index 651c125..3036ea8 100644 --- a/src/core/utils/config/config.py +++ b/src/utils/config/config.py @@ -1,4 +1,4 @@ -from core.utils.data.state import state +from utils.data.state import state import configparser import platform import os diff --git a/src/core/utils/config/settings.py b/src/utils/config/settings.py similarity index 84% rename from src/core/utils/config/settings.py rename to src/utils/config/settings.py index 5001dc7..1d330fc 100644 --- a/src/core/utils/config/settings.py +++ b/src/utils/config/settings.py @@ -1,7 +1,8 @@ -from core.network.libtorrent_int import update_settings -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state -from core.utils.config.config import create_config +from network.libtorrent_int import update_settings +from utils.config.config import create_config +from utils.logging.logs import consoleLog +from utils.data.state import state + 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): diff --git a/src/core/utils/data/models.py b/src/utils/data/models.py similarity index 100% rename from src/core/utils/data/models.py rename to src/utils/data/models.py diff --git a/src/core/utils/data/state.py b/src/utils/data/state.py similarity index 100% rename from src/core/utils/data/state.py rename to src/utils/data/state.py diff --git a/src/core/utils/data/tracker.py b/src/utils/data/tracker.py similarity index 94% rename from src/core/utils/data/tracker.py rename to src/utils/data/tracker.py index eed9bc3..85ee3f1 100644 --- a/src/core/utils/data/tracker.py +++ b/src/utils/data/tracker.py @@ -1,4 +1,4 @@ -from core.utils.logging.logs import consoleLog +from utils.logging.logs import consoleLog from bs4 import BeautifulSoup import requests diff --git a/src/core/utils/general/shutdown.py b/src/utils/general/shutdown.py similarity index 61% rename from src/core/utils/general/shutdown.py rename to src/utils/general/shutdown.py index 1ec60fd..b3aac38 100644 --- a/src/core/utils/general/shutdown.py +++ b/src/utils/general/shutdown.py @@ -1,6 +1,6 @@ -from core.network.libtorrent_misc import cleanup_session -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from network.libtorrent_misc import cleanup_session +from utils.logging.logs import consoleLog +from utils.data.state import state import os def closehelper(): diff --git a/src/core/utils/general/wrappers.py b/src/utils/general/wrappers.py similarity index 84% rename from src/core/utils/general/wrappers.py rename to src/utils/general/wrappers.py index 8046354..4e9e999 100644 --- a/src/core/utils/general/wrappers.py +++ b/src/utils/general/wrappers.py @@ -1,4 +1,4 @@ -from core.utils.logging.logs import consoleLog +from utils.logging.logs import consoleLog def run_thread(thread): target_name = thread._target.__name__ diff --git a/src/core/utils/logging/loghandler.py b/src/utils/logging/loghandler.py similarity index 85% rename from src/core/utils/logging/loghandler.py rename to src/utils/logging/loghandler.py index c105618..ae52c8b 100644 --- a/src/core/utils/logging/loghandler.py +++ b/src/utils/logging/loghandler.py @@ -1,5 +1,5 @@ -from core.utils.network.download import run_download_direct, seed_magnet -from core.utils.logging.logs import consoleLog, remove_download_log +from utils.network.download import run_download_direct, seed_magnet +from utils.logging.logs import consoleLog, remove_download_log import os def split_data(data): @@ -18,7 +18,7 @@ def check_completed(downloads, resume): run_download_direct(download.magnet_uri, download.title) consoleLog(f"Resuming Magnet: {download.title}") elif download.url: - from core.network.direct_download import add_direct_download + from network.direct_download import add_direct_download add_direct_download(download.url, download.title) consoleLog(f"Resuming Direct Download: {download.title}") diff --git a/src/core/utils/logging/logs.py b/src/utils/logging/logs.py similarity index 97% rename from src/core/utils/logging/logs.py rename to src/utils/logging/logs.py index 036864b..da8ade4 100644 --- a/src/core/utils/logging/logs.py +++ b/src/utils/logging/logs.py @@ -1,5 +1,5 @@ -from core.utils.data.models import Download, DownloadList -from core.utils.data.state import state +from utils.data.models import Download, DownloadList +from utils.data.state import state from dataclasses import asdict from datetime import datetime import json @@ -243,7 +243,7 @@ def set_main_window(window): def flush_log_buffer(): # credits to claude if state.log_buffer: try: - from core.interface.gui import MainWindow + from interface.gui import MainWindow for log_entry in state.log_buffer: MainWindow.add_log(log_entry) state.log_buffer = [] @@ -256,7 +256,7 @@ def consoleLog(text, printAnyways = False): formatted_text = f"[{current_time}] {text}" try: - from core.interface.gui import MainWindow + from interface.gui import MainWindow if not MainWindow.add_log(formatted_text): state.log_buffer.append(formatted_text) except Exception: diff --git a/src/core/utils/network/download.py b/src/utils/network/download.py similarity index 82% rename from src/core/utils/network/download.py rename to src/utils/network/download.py index 5ea18fb..a6b5953 100644 --- a/src/core/utils/network/download.py +++ b/src/utils/network/download.py @@ -1,11 +1,11 @@ -from core.network.direct_download import add_direct_download -from core.network.libtorrent_wrapper import add_magnet -from core.utils.logging.logs import add_download_log -from core.utils.general.wrappers import run_thread -from core.network.libtorrent_int import add_seed +from network.direct_download import add_direct_download +from network.libtorrent_wrapper import add_magnet from PySide6.QtWidgets import QTableWidgetItem -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import add_download_log +from utils.general.wrappers import run_thread +from network.libtorrent_int import add_seed +from utils.logging.logs import consoleLog +from utils.data.state import state from PySide6.QtCore import Qt from typing import Optional import threading diff --git a/src/core/utils/network/jsonhandler.py b/src/utils/network/jsonhandler.py similarity index 100% rename from src/core/utils/network/jsonhandler.py rename to src/utils/network/jsonhandler.py diff --git a/src/core/utils/network/update_checker.py b/src/utils/network/update_checker.py similarity index 93% rename from src/core/utils/network/update_checker.py rename to src/utils/network/update_checker.py index b8ecb24..b7ca658 100644 --- a/src/core/utils/network/update_checker.py +++ b/src/utils/network/update_checker.py @@ -1,5 +1,5 @@ -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from utils.logging.logs import consoleLog +from utils.data.state import state import requests diff --git a/src/core/utils/network/updater.py b/src/utils/network/updater.py similarity index 94% rename from src/core/utils/network/updater.py rename to src/utils/network/updater.py index 7acf76d..118cb4d 100644 --- a/src/core/utils/network/updater.py +++ b/src/utils/network/updater.py @@ -1,6 +1,6 @@ -from core.network.direct_download.handle import DirectDownloadHandle -from core.utils.logging.logs import consoleLog -from core.utils.data.state import state +from network.direct_download.handle import DirectDownloadHandle +from utils.logging.logs import consoleLog +from utils.data.state import state from PySide6.QtCore import Qt from PySide6 import QtWidgets import libtorrent as lt From 2afb3e7716856c073e4fb508815213b7fb7dd3bc Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:22:40 +0100 Subject: [PATCH 04/13] refactor: improve scraper handling by utilizing classes for registration --- src/data/__init__.py | 0 .../provider => hosts}/buzzheavier.py | 0 .../{scrapers/provider => hosts}/gofile.py | 0 src/data/scrapers/__init__.py | 0 src/data/scrapers/monkrus.py | 87 ------------- src/data/scrapers/rutracker.py | 52 -------- src/data/scrapers/steamrip.py | 122 ------------------ src/data/scrapers/uztracker.py | 55 -------- src/data/sources/__init__.py | 12 ++ src/data/sources/monkrus.py | 78 +++++++++++ src/data/sources/rutracker.py | 46 +++++++ src/data/sources/steamrip.py | 107 +++++++++++++++ src/data/sources/uztracker.py | 50 +++++++ src/interface/utils/searchhelper.py | 25 +--- src/network/__init__.py | 0 src/utils/__init__.py | 0 src/utils/data/state.py | 8 +- 17 files changed, 301 insertions(+), 341 deletions(-) delete mode 100644 src/data/__init__.py rename src/data/{scrapers/provider => hosts}/buzzheavier.py (100%) rename src/data/{scrapers/provider => hosts}/gofile.py (100%) delete mode 100644 src/data/scrapers/__init__.py delete mode 100644 src/data/scrapers/monkrus.py delete mode 100644 src/data/scrapers/rutracker.py delete mode 100644 src/data/scrapers/steamrip.py delete mode 100644 src/data/scrapers/uztracker.py create mode 100644 src/data/sources/__init__.py create mode 100644 src/data/sources/monkrus.py create mode 100644 src/data/sources/rutracker.py create mode 100644 src/data/sources/steamrip.py create mode 100644 src/data/sources/uztracker.py delete mode 100644 src/network/__init__.py delete mode 100644 src/utils/__init__.py diff --git a/src/data/__init__.py b/src/data/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/data/scrapers/provider/buzzheavier.py b/src/data/hosts/buzzheavier.py similarity index 100% rename from src/data/scrapers/provider/buzzheavier.py rename to src/data/hosts/buzzheavier.py diff --git a/src/data/scrapers/provider/gofile.py b/src/data/hosts/gofile.py similarity index 100% rename from src/data/scrapers/provider/gofile.py rename to src/data/hosts/gofile.py diff --git a/src/data/scrapers/__init__.py b/src/data/scrapers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/data/scrapers/monkrus.py b/src/data/scrapers/monkrus.py deleted file mode 100644 index 613dc8d..0000000 --- a/src/data/scrapers/monkrus.py +++ /dev/null @@ -1,87 +0,0 @@ -from utils.data.tracker import get_magnet_link -from utils.data.state import state -from bs4 import BeautifulSoup -from typing import Dict -import requests -import time - -cache = { - "data": [], - "last_fetched": 0 -} - -cache_expiry = 300 - -def _get_telegram_posts(): - post = 100 - max_posts = 250 - posts = [] - added = set() - - current_time = time.time() - - if cache["data"] and (current_time - cache["last_fetched"] < cache_expiry): - return cache["data"] - - while post <= max_posts: - url = f"https://t.me/s/real_monkrus/{post}" - response = requests.get(url, timeout=10) - soup = BeautifulSoup(response.text, "html.parser") - bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") - - if not bubbles: - break - - for bubble in bubbles: - post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") - if post_txt is None or post_txt.b is None: - continue - - title = post_txt.b.text - link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) - - if link: - post_url = link["href"] - if post_url not in added: - added.add(post_url) - posts.append(dict( - title=title, - author="m0nkrus", - id=len(posts) + 1, - url=post_url - )) - - post += 22 - - posts.reverse() - - cache["data"] = posts - cache["last_fetched"] = current_time - - return(posts) - -def scrape_m0nkrus(query): - posts = _get_telegram_posts() - filtered_posts = [] - - for post in posts: - if query.lower() in post["title"].lower(): - filtered_post = post.copy() - filtered_post["id"] = len(filtered_posts) + 1 - filtered_posts.append(filtered_post) - - return filtered_posts - -def get_magnet(post: Dict): - return get_magnet_link(post["url"]) - -Metadata = { - "name" : "m0nkrus", - "headers" : ["Post Title", "Author"], - "scrapeFunc" : scrape_m0nkrus, - "linkFunc" : get_magnet, - "isMagnet" : True, -} - -def init_m0nkrus(): - state.trackers.update({Metadata["name"] : Metadata}) diff --git a/src/data/scrapers/rutracker.py b/src/data/scrapers/rutracker.py deleted file mode 100644 index 15acbce..0000000 --- a/src/data/scrapers/rutracker.py +++ /dev/null @@ -1,52 +0,0 @@ -from utils.network.jsonhandler import split_data, format_data -from utils.data.tracker import get_magnet_link -from utils.logging.logs import consoleLog -from utils.data.state import state -from typing import Dict -import requests - -def scrape_rutracker(query): - search = requests.get(f"{state.api_url}/search?q={query}", timeout=15) - consoleLog("Sent request to server") - if search: - _, data, _, success, cached = split_data(search.text) - if cached: - consoleLog("Server response cached") - if success: - sorted_data = [] - - for entry in data: - sorted_data.append( - { - "title" : entry["title"], - "author" : entry["author"], - "seeders" : entry["seeders"], - "leechers" : entry["leechers"], - "url" : entry["url"], - "id" : entry["id"], - } - ) - - return sorted_data - else: - consoleLog("Scraping failed server-side, unable to fetch posts from rutracker") - return [] - else: - consoleLog("No response from server, returning nothing") - return [] - - -def get_magnet(post: Dict): - _, post_links, _, _, _ = format_data([post]) - return get_magnet_link(post_links[0]) - -Metadata = { - "name" : "rutracker", - "headers" : ["Post Title", "Author", "Seeders", "Leechers"], - "scrapeFunc" : scrape_rutracker, - "linkFunc" : get_magnet, - "isMagnet" : True, -} - -def init_rutracker(): - state.trackers.update({Metadata["name"] : Metadata}) diff --git a/src/data/scrapers/steamrip.py b/src/data/scrapers/steamrip.py deleted file mode 100644 index 22f0735..0000000 --- a/src/data/scrapers/steamrip.py +++ /dev/null @@ -1,122 +0,0 @@ -from data.scrapers.provider.buzzheavier import scrape_buzzheavier -from data.scrapers.provider.gofile import scrape_gofile -from utils.logging.logs import consoleLog -from utils.data.state import state -from bs4 import BeautifulSoup -from typing import Dict -import webbrowser -import requests -import time -import re - - -cache = { - "data": [], - "last_fetched": 0 -} - -cache_expiry = 300 - -def get_Metadata(): - return Metadata - -def scrape_steamrip_links(): - - current_time = time.time() - - if cache["data"] != [] and (current_time - cache["last_fetched"] < cache_expiry): - return cache["data"] - - url = f"https://steamrip.com/games-list-page/" - response = requests.get(url) - text = response.text - - soup = BeautifulSoup(text, "html.parser") - games = soup.find_all("li", class_="az-list-item") - - if len(games) == 0: - return [] - - links = [] - names = [] - for gamehtml in games: - link = gamehtml.find("a", href=lambda x: x and x.startswith("/")) - links += re.findall(r'(?<=href=")[^"]*', link.__str__()) - name = gamehtml.find("a", href=lambda x: x and x.startswith("/")) - names += re.findall(r'(?<=\/">)[^<]*', name.__str__()) - - - - # construct the list[dict[str,str]] - - ret = [] - - for i in range(len(names)): - ret.append({"title" : names[i], "url" : links[i]}) - - return ret - -def scrape_steamrip_game_downloads(gamelink): - url = "https://steamrip.com" + gamelink - response = requests.get(url) - soup = BeautifulSoup(response.text, "html.parser") - download_link_elements = soup.find_all("a",class_="shortc-button") - download_links = ["buzzheavier", "gofile"] - - for download_link in download_link_elements: - pure = download_link.attrs.get("href") - if pure[2] == "b": - download_links[0] = "https:" + pure - if pure[2] == "g": - download_links[1] = "https:" + pure - - ret = [] - - for link in download_links: - if len(link) != 1: - ret.append(link) - - cache["data"] = ret - - return ret - -def get_download_link(post: Dict): - url = post["url"] - links = scrape_steamrip_game_downloads(url) - best = "" - for link in links: - if link[0] == "h": - best = link - break - - if links.index(best) == 0: - link = scrape_buzzheavier(best) - return link - elif links.index(best) == 1: - link, headers = scrape_gofile(best) - return link, headers - else: - consoleLog("Unable to retrieve download link due to captcha, launching browser...") - webbrowser.open(best) - return None, None - -def filter_steamrip(query: str): - games = scrape_steamrip_links() - - filtered_games = [ - game for game in games - if query.lower() in game["title"].lower() - ] - - return filtered_games - -Metadata = { - "name" : "steamrip", - "headers" : ["Game"], - "scrapeFunc" : filter_steamrip, - "linkFunc" : get_download_link, - "isMagnet" : False, -} - -def init_steamrip(): - state.trackers.update({Metadata["name"] : Metadata}) diff --git a/src/data/scrapers/uztracker.py b/src/data/scrapers/uztracker.py deleted file mode 100644 index 3c55c72..0000000 --- a/src/data/scrapers/uztracker.py +++ /dev/null @@ -1,55 +0,0 @@ -from utils.data.tracker import get_magnet_link -from utils.logging.logs import consoleLog -from utils.data.state import state -from urllib.parse import urljoin -from bs4 import BeautifulSoup -from typing import Dict -import requests - - -def scrape_uztracker(query): - base_url="https://uztracker.net/" - search_url = f"{base_url.rstrip('/')}/tracker.php?nm={query}" - consoleLog(search_url) - posts = [] - - try: - response = requests.get(search_url) - response.raise_for_status() - soup = BeautifulSoup(response.text, 'html.parser') - links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_')) - for link in links: - - theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) - if not theme_link or not theme_link.b: - continue - url = urljoin(base_url, theme_link['href']) - title = theme_link.b.text - author_link = link.find('a', class_="med") - author = author_link.text.strip() if author_link else "Unknown" - - posts.append(dict( - title=title, - author=author, - url=url, - )) - - return posts - - except requests.RequestException as e: - consoleLog(f"Failed to fetch {search_url}: {e}") - return None - -def get_magnet(post: Dict): - return get_magnet_link(post["url"]) - -Metadata = { - "name" : "uztracker", - "headers" : ["Post Title", "Author"], - "scrapeFunc" : scrape_uztracker, - "linkFunc" : get_magnet, - "isMagnet" : True, -} - -def init_uztracker(): - state.trackers.update({Metadata["name"] : Metadata}) diff --git a/src/data/sources/__init__.py b/src/data/sources/__init__.py new file mode 100644 index 0000000..569e6a1 --- /dev/null +++ b/src/data/sources/__init__.py @@ -0,0 +1,12 @@ +from data.sources.rutracker import RutrackerScraper +from data.sources.uztracker import UztrackerScraper +from data.sources.steamrip import SteamripScraper +from data.sources.monkrus import MonkrusScraper + +# Register Scrapers here +SCRAPERS = [ + RutrackerScraper(), + SteamripScraper(), + MonkrusScraper(), + UztrackerScraper(), +] \ No newline at end of file diff --git a/src/data/sources/monkrus.py b/src/data/sources/monkrus.py new file mode 100644 index 0000000..3386d22 --- /dev/null +++ b/src/data/sources/monkrus.py @@ -0,0 +1,78 @@ +from utils.data.tracker import get_magnet_link +from bs4 import BeautifulSoup +from typing import Dict +import requests +import time + + +class MonkrusScraper: + name = "m0nkrus" + headers = ["Post Title", "Author"] + is_magnet = True + + def __init__(self): + self.cache = { "data": [], "last_fetched": 0 } + self.cache_expiry = 300 + + def _get_telegram_posts(self): + post = 100 + max_posts = 250 + posts = [] + added = set() + + current_time = time.time() + + if self.cache["data"] and (current_time - self.cache["last_fetched"] < self.cache_expiry): + return self.cache["data"] + + while post <= max_posts: + url = f"https://t.me/s/real_monkrus/{post}" + response = requests.get(url, timeout=10) + soup = BeautifulSoup(response.text, "html.parser") + bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") + + if not bubbles: + break + + for bubble in bubbles: + post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") + if post_txt is None or post_txt.b is None: + continue + + title = post_txt.b.text + link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) + + if link: + post_url = link["href"] + if post_url not in added: + added.add(post_url) + posts.append(dict( + title=title, + author="m0nkrus", + id=len(posts) + 1, + url=post_url + )) + + post += 22 + + posts.reverse() + + self.cache["data"] = posts + self.cache["last_fetched"] = current_time + + return(posts) + + def search(self, query): + posts = self._get_telegram_posts() + filtered_posts = [] + + for post in posts: + if query.lower() in post["title"].lower(): + filtered_post = post.copy() + filtered_post["id"] = len(filtered_posts) + 1 + filtered_posts.append(filtered_post) + + return filtered_posts + + def get_magnet(self, post: Dict): + return get_magnet_link(post["url"]) diff --git a/src/data/sources/rutracker.py b/src/data/sources/rutracker.py new file mode 100644 index 0000000..9f48bfc --- /dev/null +++ b/src/data/sources/rutracker.py @@ -0,0 +1,46 @@ +from utils.network.jsonhandler import split_data, format_data +from utils.data.tracker import get_magnet_link +from utils.logging.logs import consoleLog +from utils.data.state import state +from typing import Dict +import requests + +class RutrackerScraper: + name = "rutracker" + headers = ["Post Title", "Author", "Seeders", "Leechers"] + is_magnet = True + + def search(self, query): + search = requests.get(f"{state.api_url}/search?q={query}", timeout=15) + consoleLog("Sent request to server") + if search: + _, data, _, success, cached = split_data(search.text) + if cached: + consoleLog("Server response cached") + if success: + sorted_data = [] + + for entry in data: + sorted_data.append( + { + "title" : entry["title"], + "author" : entry["author"], + "seeders" : entry["seeders"], + "leechers" : entry["leechers"], + "url" : entry["url"], + "id" : entry["id"], + } + ) + + return sorted_data + else: + consoleLog("Scraping failed server-side, unable to fetch posts from rutracker") + return [] + else: + consoleLog("No response from server, returning nothing") + return [] + + def get_magnet(self, post: Dict): + _, post_links, _, _, _ = format_data([post]) + return get_magnet_link(post_links[0]) + diff --git a/src/data/sources/steamrip.py b/src/data/sources/steamrip.py new file mode 100644 index 0000000..71de40e --- /dev/null +++ b/src/data/sources/steamrip.py @@ -0,0 +1,107 @@ +from data.hosts.buzzheavier import scrape_buzzheavier +from data.hosts.gofile import scrape_gofile +from utils.logging.logs import consoleLog +from bs4 import BeautifulSoup +from typing import Dict +import webbrowser +import requests +import time +import re + +class SteamripScraper: + name = "steamrip" + headers = ["Game"] + is_magnet = False + + + def __init__(self): + self.cache = { "data": [], "last_fetched": 0 } + self.cache_expiry = 300 + + def scrape_steamrip_links(self): + + current_time = time.time() + + if self.cache["data"] != [] and (current_time - self.cache["last_fetched"] < self.cache_expiry): + return self.cache["data"] + + url = f"https://steamrip.com/games-list-page/" + response = requests.get(url) + text = response.text + + soup = BeautifulSoup(text, "html.parser") + games = soup.find_all("li", class_="az-list-item") + + if len(games) == 0: + return [] + + links = [] + names = [] + for gamehtml in games: + link = gamehtml.find("a", href=lambda x: x and x.startswith("/")) + links += re.findall(r'(?<=href=")[^"]*', link.__str__()) + name = gamehtml.find("a", href=lambda x: x and x.startswith("/")) + names += re.findall(r'(?<=\/">)[^<]*', name.__str__()) + + # construct the list[dict[str,str]] + + ret = [] + + for i in range(len(names)): + ret.append({"title" : names[i], "url" : links[i]}) + + return ret + + def scrape_steamrip_game_downloads(self, gamelink): + url = "https://steamrip.com" + gamelink + response = requests.get(url) + soup = BeautifulSoup(response.text, "html.parser") + download_link_elements = soup.find_all("a",class_="shortc-button") + download_links = ["buzzheavier", "gofile"] + + for download_link in download_link_elements: + pure = download_link.attrs.get("href") + if pure[2] == "b": + download_links[0] = "https:" + pure + if pure[2] == "g": + download_links[1] = "https:" + pure + + ret = [] + + for link in download_links: + if len(link) != 1: + ret.append(link) + + self.cache["data"] = ret + + return ret + + def get_download_link(self, post: Dict): + url = post["url"] + links = self.scrape_steamrip_game_downloads(url) + best = "" + for link in links: + if link[0] == "h": + best = link + break + + if links.index(best) == 0: + link = scrape_buzzheavier(best) + return link + elif links.index(best) == 1: + link, headers = scrape_gofile(best) + return link, headers + else: + consoleLog("Unable to retrieve download link due to captcha, launching browser...") + webbrowser.open(best) + return None, None + + def search(self, query: str): + games = self.scrape_steamrip_links() + + filtered_games = [ + game for game in games + if query.lower() in game["title"].lower() + ] + + return filtered_games \ No newline at end of file diff --git a/src/data/sources/uztracker.py b/src/data/sources/uztracker.py new file mode 100644 index 0000000..de5930d --- /dev/null +++ b/src/data/sources/uztracker.py @@ -0,0 +1,50 @@ +from utils.data.tracker import get_magnet_link +from utils.logging.logs import consoleLog +from utils.data.state import state +from urllib.parse import urljoin +from bs4 import BeautifulSoup +from typing import Dict +import requests + +class UztrackerScraper: + name = "uztracker" + headers = ["Post Title", "Author"] + is_magnet = True + + + def search(self, query): + base_url="https://uztracker.net/" + search_url = f"{base_url.rstrip('/')}/tracker.php?nm={query}" + consoleLog(search_url) + posts = [] + + try: + response = requests.get(search_url) + response.raise_for_status() + soup = BeautifulSoup(response.text, 'html.parser') + links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_')) + for link in links: + + theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) + if not theme_link or not theme_link.b: + continue + url = urljoin(base_url, theme_link['href']) + title = theme_link.b.text + author_link = link.find('a', class_="med") + author = author_link.text.strip() if author_link else "Unknown" + + posts.append(dict( + title=title, + author=author, + url=url, + )) + + return posts + + except requests.RequestException as e: + consoleLog(f"Failed to fetch {search_url}: {e}") + return None + + def get_magnet(post: Dict): + return get_magnet_link(post["url"]) + diff --git a/src/interface/utils/searchhelper.py b/src/interface/utils/searchhelper.py index ab006b0..24f687c 100644 --- a/src/interface/utils/searchhelper.py +++ b/src/interface/utils/searchhelper.py @@ -1,16 +1,9 @@ -from data.scrapers.rutracker import init_rutracker -from data.scrapers.uztracker import init_uztracker -from data.scrapers.steamrip import init_steamrip -from data.scrapers.monkrus import init_m0nkrus from utils.logging.logs import consoleLog from utils.data.state import state +from data.sources import SCRAPERS -# Call initialization function for each tracker / site -init_rutracker() -init_uztracker() -init_m0nkrus() -init_steamrip() - +for scraper in SCRAPERS: + state.trackers[scraper.name] = scraper def run_search(self) -> None: self.show_empty_results(False) @@ -23,12 +16,8 @@ def run_search(self) -> None: consoleLog(f"User searched for: {search_text}") # Get current tracker and call its search function - tracker = state.trackers[state.currenttracker] - scrapefunc = tracker["scrapeFunc"] - state.posts = scrapefunc(search_text) + scraper = state.trackers[state.currenttracker] + state.posts = scraper.search(search_text) - # Late to prevent circular import - from interface.gui import MainWindow - - # Update GUI Headers - MainWindow._instance.search_results_signal.emit(tracker["headers"]) + # Update GUI headers to match the current scraper + self.search_results_signal.emit(scraper.headers) \ No newline at end of file diff --git a/src/network/__init__.py b/src/network/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/__init__.py b/src/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/data/state.py b/src/utils/data/state.py index de42ee9..75c3e79 100644 --- a/src/utils/data/state.py +++ b/src/utils/data/state.py @@ -34,13 +34,7 @@ class AppState(QObject): self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers self.currenttracker: str = "rutracker" self.api_url: str = "https://api.michijackson.xyz" - self.trackers: Dict[str,Dict[str,Any]] = {} # each tracker should add itself here - # an example: - # "rutracker" : { - # "name" : "rutracker", - # "headers" : ["author", "title"], - # "scrapeFunc" : function, - # } + self.trackers: Dict[str,Dict[str,Any]] = {} # LibTorrent / Download related stuff self.dl_session: Any = None From c6940f017896eff51fedbb425ea2cba3185d09df Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:26:42 +0100 Subject: [PATCH 05/13] fix: add self argument to function --- src/data/sources/uztracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/sources/uztracker.py b/src/data/sources/uztracker.py index de5930d..4b4dfd7 100644 --- a/src/data/sources/uztracker.py +++ b/src/data/sources/uztracker.py @@ -45,6 +45,6 @@ class UztrackerScraper: consoleLog(f"Failed to fetch {search_url}: {e}") return None - def get_magnet(post: Dict): + def get_magnet(self, post: Dict): return get_magnet_link(post["url"]) From c0f50604512bb8cbd884f9b1c124a434d775328b Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:51:28 +0100 Subject: [PATCH 06/13] fix: searchbar disabling itself when searching for nothing --- src/interface/gui.py | 1 - src/interface/utils/searchhelper.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interface/gui.py b/src/interface/gui.py index 7eb289b..73140bf 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -292,7 +292,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): table.setItemDelegateForColumn(i, self._tracker_hover_delegate) def _start_search(self): - self.searchbar.setEnabled(False) def _search_thread(): try: run_search(self) diff --git a/src/interface/utils/searchhelper.py b/src/interface/utils/searchhelper.py index 24f687c..ca4bc60 100644 --- a/src/interface/utils/searchhelper.py +++ b/src/interface/utils/searchhelper.py @@ -13,6 +13,8 @@ def run_search(self) -> None: if search_text == "": consoleLog("Error: Can't search for nothing") return + + self.searchbar.setEnabled(False) consoleLog(f"User searched for: {search_text}") # Get current tracker and call its search function From 0a591e1cda08917655ca7873564427440e8af0e3 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sat, 21 Mar 2026 00:27:56 +0100 Subject: [PATCH 07/13] refactor: update progress dialog and tidy UI --- src/interface/dialogs/settings.py | 41 +++++++++-------------------- src/interface/dialogs/update.py | 16 ++++++++++- src/interface/gui.py | 2 +- src/network/libtorrent_int.py | 2 -- src/network/libtorrent_misc.py | 3 --- src/utils/network/update_checker.py | 1 - 6 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/interface/dialogs/settings.py b/src/interface/dialogs/settings.py index 56e6761..3267730 100644 --- a/src/interface/dialogs/settings.py +++ b/src/interface/dialogs/settings.py @@ -46,7 +46,7 @@ def settings_dialog(self): update_checkbox_container = QWidget() update_checkbox_layout = QHBoxLayout() - # ignore updates checkbox + # Ignore updates checkbox if platform.system() == "Windows": update_checkbox = QCheckBox() @@ -57,7 +57,7 @@ def settings_dialog(self): update_checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked)) update_checkbox_layout.addWidget(update_checkbox) - # auto-resume downloads checkbox + # Auto-resume downloads checkbox autoresume_container = QWidget() autoresume_layout = QHBoxLayout() @@ -71,7 +71,7 @@ def settings_dialog(self): autoresume_layout.addWidget(autoresume_checkbox) - # transparent window checkbox + # Transparent window checkbox transparent_window_container = QWidget() transparent_window_layout = QHBoxLayout() @@ -84,10 +84,8 @@ def settings_dialog(self): transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked)) transparent_window_layout.addWidget(transparent_window_checkbox) - ################## - # SERVER SETTING # - ################## + # API Server Setting api_url_container = QWidget() api_url_layout = QHBoxLayout() @@ -98,10 +96,8 @@ def settings_dialog(self): api_url_container.setLayout(api_url_layout) api_url.setText(state.api_url) - ################# - # DOWNLOAD PATH # - ################# + # Download Path download_path_container = QWidget() download_path_layout = QHBoxLayout() @@ -133,10 +129,8 @@ def settings_dialog(self): download_path_layout.addWidget(browse_button) browse_button.clicked.connect(browse_download_path) - ############### - # IMAGE PATH # - ############### + # Image Path image_path_container = QWidget() image_path_layout = QHBoxLayout() @@ -167,10 +161,8 @@ def settings_dialog(self): image_path_layout.addWidget(browse_button) browse_button.clicked.connect(browse_image_path) - ################## - # SPEED LIMITING # - ################## + # Speed Limiting down_speed_limit_container = QWidget() down_speed_limit_layout = QHBoxLayout() @@ -197,10 +189,8 @@ def settings_dialog(self): up_speed_limit.setFixedWidth(180) up_speed_limit.setFixedHeight(30) - ###################### - # CONNECTION CONFIGS # - ###################### - + + # Connection Configs max_connections_container = QWidget() max_connections_layout = QHBoxLayout() @@ -214,10 +204,8 @@ def settings_dialog(self): max_connections.setFixedWidth(180) max_connections.setFixedHeight(30) - #################### - # DOWNLOAD CONFIGS # - #################### + # Download Configs max_downloads_container = QWidget() max_downloads_layout = QHBoxLayout() @@ -231,10 +219,8 @@ def settings_dialog(self): max_downloads.setFixedWidth(180) max_downloads.setFixedHeight(30) - ##################### - # INTERFACE BINDING # - ##################### + # Interface Binding interface_container = QWidget() interface_layout = QHBoxLayout() @@ -257,10 +243,7 @@ def settings_dialog(self): interface_container.setLayout(interface_layout) - ############### - # SAVE/CANCEL # - ############### - + # Save / Cancel buttons layout = QHBoxLayout() save_btn = QPushButton("Save") diff --git a/src/interface/dialogs/update.py b/src/interface/dialogs/update.py index a45a329..f332391 100644 --- a/src/interface/dialogs/update.py +++ b/src/interface/dialogs/update.py @@ -1,8 +1,10 @@ +from PySide6.QtWidgets import QDialog, QMessageBox, QProgressDialog from utils.network.update_checker import get_updates -from PySide6.QtWidgets import QDialog, QMessageBox from utils.network.updater import download_update from utils.logging.logs import consoleLog from utils.data.state import state +from PySide6 import QtWidgets +from PySide6.QtCore import Qt from pathlib import Path import platform import json @@ -47,3 +49,15 @@ class UpdateDialog(QDialog): if response == QMessageBox.StandardButton.Ok: download_update(assets) +class UpdateProgressDialog(QProgressDialog): + def __init__(self, parent=None): + self.progress = QtWidgets.QProgressDialog("Downloading update... (0.0 MB/s)", None, 0, 100) + self.progress.setWindowTitle("Updating") + self.progress.setWindowModality(Qt.WindowModality.ApplicationModal) + self.progress.setCancelButton(None) + self.progress.setMinimumDuration(0) + self.progress.setAutoClose(False) + self.progress.setAutoReset(False) + self.progress.setValue(0) + self.progress.show() + QtWidgets.QApplication.processEvents() \ No newline at end of file diff --git a/src/interface/gui.py b/src/interface/gui.py index 73140bf..a7f47d9 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -232,7 +232,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.statusbar.addPermanentWidget(self.version, Qt.AlignmentFlag.AlignLeft) # (\u2193) Arrow down, (\u2191) Arrow up - self.speed_label = QLabel("\u2193 0.0 kB/s \u2191 0.0 kB/s") + self.speed_label = QLabel("↓ 0.0 kB/s ↑ 0.0 kB/s") self.speed_label.setStyleSheet("padding-right: 6px;") self.statusbar.addPermanentWidget(self.speed_label) diff --git a/src/network/libtorrent_int.py b/src/network/libtorrent_int.py index f741f9e..42350ec 100644 --- a/src/network/libtorrent_int.py +++ b/src/network/libtorrent_int.py @@ -60,7 +60,6 @@ def add_download(magnet_uri): state.active_downloads = {} init_session() - free_space = get_free_space_mb(state.download_path) if magnet_uri in state.active_downloads: @@ -91,7 +90,6 @@ def add_download(magnet_uri): params.save_path = state.download_path handle = state.dl_session.add_torrent(params) - while not handle.has_metadata(): time.sleep(1) diff --git a/src/network/libtorrent_misc.py b/src/network/libtorrent_misc.py index adfab0c..eeb471e 100644 --- a/src/network/libtorrent_misc.py +++ b/src/network/libtorrent_misc.py @@ -17,9 +17,6 @@ def cleanup_session(): state.dl_session = None state.active_downloads.clear() - - - def send_notification(shutdown_event): notified = set() while not shutdown_event.is_set(): diff --git a/src/utils/network/update_checker.py b/src/utils/network/update_checker.py index b7ca658..efd574c 100644 --- a/src/utils/network/update_checker.py +++ b/src/utils/network/update_checker.py @@ -3,7 +3,6 @@ from utils.data.state import state import requests - def get_updates(): url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases/latest" try: From 23e44cb128d90920461b46864a37c04d5df4a7a7 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 21 Mar 2026 00:39:28 +0100 Subject: [PATCH 08/13] remove qt widget from updater.py and use class instead --- src/utils/network/updater.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/utils/network/updater.py b/src/utils/network/updater.py index 118cb4d..d9cbb67 100644 --- a/src/utils/network/updater.py +++ b/src/utils/network/updater.py @@ -1,7 +1,6 @@ from network.direct_download.handle import DirectDownloadHandle from utils.logging.logs import consoleLog from utils.data.state import state -from PySide6.QtCore import Qt from PySide6 import QtWidgets import libtorrent as lt import subprocess @@ -38,16 +37,8 @@ def download_update(assets: list): installer_path = os.path.join(tempfile.gettempdir(), filename) - progress = QtWidgets.QProgressDialog("Downloading update... (0.0 MB/s)", None, 0, 100) - progress.setWindowTitle("Updating") - progress.setWindowModality(Qt.WindowModality.ApplicationModal) - progress.setCancelButton(None) - progress.setMinimumDuration(0) - progress.setAutoClose(False) - progress.setAutoReset(False) - progress.setValue(0) - progress.show() - QtWidgets.QApplication.processEvents() + from interface.dialogs.update import UpdateProgressDialog + progress = UpdateProgressDialog() original_limit = state.down_speed_limit state.down_speed_limit = 0 @@ -93,4 +84,4 @@ def download_update(assets: list): subprocess.Popen([installer_path, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/SP-", "/CLOSEAPPLICATIONS"]) time.sleep(1) - sys.exit(0) \ No newline at end of file + sys.exit(0) From 4d4187ce6368766109eac8a27a1f8792ff7d8d27 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sat, 21 Mar 2026 01:09:31 +0100 Subject: [PATCH 09/13] fix: revert update.py changes --- src/interface/dialogs/update.py | 16 +--------------- src/utils/network/updater.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/interface/dialogs/update.py b/src/interface/dialogs/update.py index f332391..a45a329 100644 --- a/src/interface/dialogs/update.py +++ b/src/interface/dialogs/update.py @@ -1,10 +1,8 @@ -from PySide6.QtWidgets import QDialog, QMessageBox, QProgressDialog from utils.network.update_checker import get_updates +from PySide6.QtWidgets import QDialog, QMessageBox from utils.network.updater import download_update from utils.logging.logs import consoleLog from utils.data.state import state -from PySide6 import QtWidgets -from PySide6.QtCore import Qt from pathlib import Path import platform import json @@ -49,15 +47,3 @@ class UpdateDialog(QDialog): if response == QMessageBox.StandardButton.Ok: download_update(assets) -class UpdateProgressDialog(QProgressDialog): - def __init__(self, parent=None): - self.progress = QtWidgets.QProgressDialog("Downloading update... (0.0 MB/s)", None, 0, 100) - self.progress.setWindowTitle("Updating") - self.progress.setWindowModality(Qt.WindowModality.ApplicationModal) - self.progress.setCancelButton(None) - self.progress.setMinimumDuration(0) - self.progress.setAutoClose(False) - self.progress.setAutoReset(False) - self.progress.setValue(0) - self.progress.show() - QtWidgets.QApplication.processEvents() \ No newline at end of file diff --git a/src/utils/network/updater.py b/src/utils/network/updater.py index d9cbb67..118cb4d 100644 --- a/src/utils/network/updater.py +++ b/src/utils/network/updater.py @@ -1,6 +1,7 @@ from network.direct_download.handle import DirectDownloadHandle from utils.logging.logs import consoleLog from utils.data.state import state +from PySide6.QtCore import Qt from PySide6 import QtWidgets import libtorrent as lt import subprocess @@ -37,8 +38,16 @@ def download_update(assets: list): installer_path = os.path.join(tempfile.gettempdir(), filename) - from interface.dialogs.update import UpdateProgressDialog - progress = UpdateProgressDialog() + progress = QtWidgets.QProgressDialog("Downloading update... (0.0 MB/s)", None, 0, 100) + progress.setWindowTitle("Updating") + progress.setWindowModality(Qt.WindowModality.ApplicationModal) + progress.setCancelButton(None) + progress.setMinimumDuration(0) + progress.setAutoClose(False) + progress.setAutoReset(False) + progress.setValue(0) + progress.show() + QtWidgets.QApplication.processEvents() original_limit = state.down_speed_limit state.down_speed_limit = 0 @@ -84,4 +93,4 @@ def download_update(assets: list): subprocess.Popen([installer_path, "/VERYSILENT", "/SUPPRESSMSGBOXES", "/SP-", "/CLOSEAPPLICATIONS"]) time.sleep(1) - sys.exit(0) + sys.exit(0) \ No newline at end of file From 9eb70af1a170e9ad4047bb6e8fcebcfc061785de Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 21 Mar 2026 01:14:05 +0100 Subject: [PATCH 10/13] refactored tabhelper function --- src/interface/dialogs/settings.py | 13 +++---- src/interface/gui.py | 4 +-- src/interface/utils/tabhelper.py | 58 ++++++------------------------- 3 files changed, 18 insertions(+), 57 deletions(-) diff --git a/src/interface/dialogs/settings.py b/src/interface/dialogs/settings.py index 3267730..795a9c8 100644 --- a/src/interface/dialogs/settings.py +++ b/src/interface/dialogs/settings.py @@ -1,7 +1,5 @@ -from interface.utils.tabhelper import general_tab -from interface.utils.tabhelper import network_tab +from interface.utils.tabhelper import create_tab from utils.config.settings import save_settings -from interface.utils.tabhelper import paths_tab from interface.utils.svghelper import svg_icon from utils.logging.logs import consoleLog from PySide6.QtCore import Qt, QSize @@ -19,7 +17,6 @@ from PySide6.QtWidgets import ( QCheckBox, QFileDialog, QComboBox, - QTabWidget, ) import platform @@ -264,10 +261,10 @@ def settings_dialog(self): layout.addWidget(cancel_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) + self.tabs = QtWidgets.QTabWidget() + create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container], tabs=self.tabs) + create_tab("Paths", [download_path_container, image_path_container], tabs=self.tabs) + create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=self.tabs) dialog_layout.addWidget(self.tabs) dialog_layout.addLayout(layout) diff --git a/src/interface/gui.py b/src/interface/gui.py index a7f47d9..2c95efb 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -164,8 +164,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.horizontal_layout.addWidget(state.trackertable) # Tabs - self.tab1 = create_tab("Search", self.searchbar, state.trackertable, self.tabs, self.dlbutton, self.horizontal_layout) - self.tab2 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None) + create_tab("Search", [self.searchbar, state.trackertable, self.tabs, self.dlbutton, self.horizontal_layout], tabs=self.tabs) + create_tab("Downloads", [self.emptyDownload, self.downloadList], tabs=self.tabs) # Corner Widget (Settings Button, Tracker list, Tab button container) self.corner_widget = QWidget() diff --git a/src/interface/utils/tabhelper.py b/src/interface/utils/tabhelper.py index f22304b..d517835 100644 --- a/src/interface/utils/tabhelper.py +++ b/src/interface/utils/tabhelper.py @@ -1,54 +1,18 @@ -from PySide6.QtWidgets import QWidget, QVBoxLayout +from PySide6.QtWidgets import QWidget, QVBoxLayout, QLayout # Tab creation helper function -def create_tab(title, searchbar, software_list, tabs, dlbutton, layout2): - tab = QWidget() - layout = QVBoxLayout() - if searchbar: - layout.addWidget(searchbar) - if layout2: - layout.addLayout(layout2) - else: - layout.addWidget(software_list) - if dlbutton: - layout.addWidget(dlbutton) - tab.setLayout(layout) - tabs.addTab(tab, title) - return tab - -# Settings Tabs -def general_tab(title, autoresume, update_checkbox, transparent_window, tabs): +def create_tab(title, items, *, tabs, stretch=False): 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() + for item in items: + if item is None: + continue + if isinstance(item, QLayout): + layout.addLayout(item) + else: + layout.addWidget(item) + if stretch: + layout.addStretch() tab.setLayout(layout) tabs.addTab(tab, title) return tab From 616552063dbd3de4faf09addcfd889f9a04fa467 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 21 Mar 2026 01:47:46 +0100 Subject: [PATCH 11/13] fix gui not starting, caused by tabhelper --- src/interface/dialogs/settings.py | 10 +++++----- src/interface/gui.py | 4 ++-- src/interface/utils/tabhelper.py | 10 ++++++++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/interface/dialogs/settings.py b/src/interface/dialogs/settings.py index 795a9c8..0682323 100644 --- a/src/interface/dialogs/settings.py +++ b/src/interface/dialogs/settings.py @@ -261,12 +261,12 @@ def settings_dialog(self): layout.addWidget(cancel_btn) layout.addWidget(save_btn) - self.tabs = QtWidgets.QTabWidget() - create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container], tabs=self.tabs) - create_tab("Paths", [download_path_container, image_path_container], tabs=self.tabs) - create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=self.tabs) + tabs = QtWidgets.QTabWidget() + create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_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) - dialog_layout.addWidget(self.tabs) + dialog_layout.addWidget(tabs) dialog_layout.addLayout(layout) dialog.exec() diff --git a/src/interface/gui.py b/src/interface/gui.py index 2c95efb..851e115 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -164,8 +164,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.horizontal_layout.addWidget(state.trackertable) # Tabs - create_tab("Search", [self.searchbar, state.trackertable, self.tabs, self.dlbutton, self.horizontal_layout], tabs=self.tabs) - create_tab("Downloads", [self.emptyDownload, self.downloadList], tabs=self.tabs) + create_tab("Search", [self.searchbar, state.trackertable, self.dlbutton, self.horizontal_layout], tabs=self.tabs, stretch=False) + create_tab("Downloads", [self.emptyDownload, self.downloadList], tabs=self.tabs, stretch=False) # Corner Widget (Settings Button, Tracker list, Tab button container) self.corner_widget = QWidget() diff --git a/src/interface/utils/tabhelper.py b/src/interface/utils/tabhelper.py index d517835..a328979 100644 --- a/src/interface/utils/tabhelper.py +++ b/src/interface/utils/tabhelper.py @@ -1,18 +1,24 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QLayout # Tab creation helper function -def create_tab(title, items, *, tabs, stretch=False): +def create_tab(title, items, tabs, stretch): tab = QWidget() layout = QVBoxLayout() + for item in items: if item is None: continue + if isinstance(item, QLayout): - layout.addLayout(item) + wrapper = QWidget() + wrapper.setLayout(item) + layout.addWidget(wrapper) else: layout.addWidget(item) + if stretch: layout.addStretch() + tab.setLayout(layout) tabs.addTab(tab, title) return tab From 9e7607dd361de524d5992a381ddd0b9f7177a293 Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sat, 21 Mar 2026 02:03:49 +0100 Subject: [PATCH 12/13] refactor: get_magnet to get_download_link Rename scraper method get_magnet -> get_download_link in monkrus, rutracker and uztracker. Update downloader code to call .get_download_link and use the .is_magnet flag on tracker objects (replace dict access). Switch direct_download package imports to absolute paths and perform a small import reorder in direct_download/utils.py. These changes unify the download API and adapt call sites to the new tracker interface. --- src/data/sources/monkrus.py | 2 +- src/data/sources/rutracker.py | 2 +- src/data/sources/uztracker.py | 2 +- src/network/direct_download/__init__.py | 4 ++-- src/network/direct_download/utils.py | 2 +- src/utils/network/download.py | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/data/sources/monkrus.py b/src/data/sources/monkrus.py index 3386d22..f45e32a 100644 --- a/src/data/sources/monkrus.py +++ b/src/data/sources/monkrus.py @@ -74,5 +74,5 @@ class MonkrusScraper: return filtered_posts - def get_magnet(self, post: Dict): + def get_download_link(self, post: Dict): return get_magnet_link(post["url"]) diff --git a/src/data/sources/rutracker.py b/src/data/sources/rutracker.py index 9f48bfc..73b6a4b 100644 --- a/src/data/sources/rutracker.py +++ b/src/data/sources/rutracker.py @@ -40,7 +40,7 @@ class RutrackerScraper: consoleLog("No response from server, returning nothing") return [] - def get_magnet(self, post: Dict): + def get_download_link(self, post: Dict): _, post_links, _, _, _ = format_data([post]) return get_magnet_link(post_links[0]) diff --git a/src/data/sources/uztracker.py b/src/data/sources/uztracker.py index 4b4dfd7..027394e 100644 --- a/src/data/sources/uztracker.py +++ b/src/data/sources/uztracker.py @@ -45,6 +45,6 @@ class UztrackerScraper: consoleLog(f"Failed to fetch {search_url}: {e}") return None - def get_magnet(self, post: Dict): + def get_download_link(self, post: Dict): return get_magnet_link(post["url"]) diff --git a/src/network/direct_download/__init__.py b/src/network/direct_download/__init__.py index 3d6f8b2..a082b51 100644 --- a/src/network/direct_download/__init__.py +++ b/src/network/direct_download/__init__.py @@ -3,8 +3,8 @@ from utils.data.state import state from typing import Optional -from .handle import DirectDownloadHandle -from .utils import ( +from network.direct_download.handle import DirectDownloadHandle +from network.direct_download.utils import ( sanitize_filename, extract_filename_from_url, detect_filename_from_headers, diff --git a/src/network/direct_download/utils.py b/src/network/direct_download/utils.py index 20948f6..2b70b58 100644 --- a/src/network/direct_download/utils.py +++ b/src/network/direct_download/utils.py @@ -1,5 +1,5 @@ -from utils.logging.logs import consoleLog from urllib.parse import urlparse, unquote +from utils.logging.logs import consoleLog from typing import Optional import requests import os diff --git a/src/utils/network/download.py b/src/utils/network/download.py index a6b5953..86a8152 100644 --- a/src/utils/network/download.py +++ b/src/utils/network/download.py @@ -31,9 +31,9 @@ def download_selected(items: list[QTableWidgetItem]): run_thread(threading.Thread(target=run_download, args=(post,))) def run_download(post, headers: Optional[dict] = None): - linkfunc = state.trackers[state.currenttracker]["linkFunc"] - ismagnet = state.trackers[state.currenttracker]["isMagnet"] - result = linkfunc(post) + link_method = state.trackers[state.currenttracker].get_download_link + ismagnet = state.trackers[state.currenttracker].is_magnet + result = link_method(post) if ismagnet: link = result From 77c2e9cd9ecbfae6aa083cee54761d3f6d43f68a Mon Sep 17 00:00:00 2001 From: Vxrtrauter <101264710+Vxrtrauter@users.noreply.github.com> Date: Sat, 21 Mar 2026 02:48:48 +0100 Subject: [PATCH 13/13] fix: tab layout, clear search state, and cleanup UI: remove trackertable from the Search tab and reorder widgets to correct layout. Tab helper: add QLayout directly instead of wrapping it in a temporary QWidget to avoid extra nesting. Search helper: clear the trackertable rows and reset state.posts when starting a new search to ensure fresh results. Minor: import check_space in main and simplify an unused loop variable in libtorrent_int for clarity/linting. --- src/interface/gui.py | 2 +- src/interface/utils/searchhelper.py | 2 ++ src/interface/utils/tabhelper.py | 4 +--- src/main.py | 1 + src/network/libtorrent_int.py | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/interface/gui.py b/src/interface/gui.py index 851e115..1b048ef 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -164,7 +164,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.horizontal_layout.addWidget(state.trackertable) # Tabs - create_tab("Search", [self.searchbar, state.trackertable, self.dlbutton, self.horizontal_layout], tabs=self.tabs, stretch=False) + create_tab("Search", [self.searchbar, self.horizontal_layout, self.dlbutton], tabs=self.tabs, stretch=False) create_tab("Downloads", [self.emptyDownload, self.downloadList], tabs=self.tabs, stretch=False) # Corner Widget (Settings Button, Tracker list, Tab button container) diff --git a/src/interface/utils/searchhelper.py b/src/interface/utils/searchhelper.py index ca4bc60..eeefdf3 100644 --- a/src/interface/utils/searchhelper.py +++ b/src/interface/utils/searchhelper.py @@ -6,6 +6,7 @@ for scraper in SCRAPERS: state.trackers[scraper.name] = scraper def run_search(self) -> None: + state.trackertable.setRowCount(0) self.show_empty_results(False) search_text = self.searchbar.text() @@ -14,6 +15,7 @@ def run_search(self) -> None: consoleLog("Error: Can't search for nothing") return + state.posts = [] self.searchbar.setEnabled(False) consoleLog(f"User searched for: {search_text}") diff --git a/src/interface/utils/tabhelper.py b/src/interface/utils/tabhelper.py index a328979..5a61aa7 100644 --- a/src/interface/utils/tabhelper.py +++ b/src/interface/utils/tabhelper.py @@ -10,9 +10,7 @@ def create_tab(title, items, tabs, stretch): continue if isinstance(item, QLayout): - wrapper = QWidget() - wrapper.setLayout(item) - layout.addWidget(wrapper) + layout.addLayout(item) else: layout.addWidget(item) diff --git a/src/main.py b/src/main.py index 4cdcf70..b7b3405 100644 --- a/src/main.py +++ b/src/main.py @@ -3,6 +3,7 @@ from utils.logging.loghandler import split_data, check_completed, check_download from network.interface import list_interfaces, init_interfaces from utils.logging.logs import get_download_logs from utils.logging.logs import set_main_window +from network.libtorrent_int import check_space from utils.general.shutdown import closehelper from utils.general.wrappers import run_thread from utils.general.shutdown import force_exit diff --git a/src/network/libtorrent_int.py b/src/network/libtorrent_int.py index c1bb694..db31889 100644 --- a/src/network/libtorrent_int.py +++ b/src/network/libtorrent_int.py @@ -23,7 +23,7 @@ def get_free_space_mb(dirname): def check_space(): while not state.shutdown_event.is_set(): - for magnet_uri, magnetdl in list(state.active_downloads.items()): + for _, magnetdl in list(state.active_downloads.items()): try: status = magnetdl.status() except RuntimeError: