diff --git a/src/core/data/scrapers/monkrus.py b/src/core/data/scrapers/monkrus.py index 7acac5e..67e64f3 100644 --- a/src/core/data/scrapers/monkrus.py +++ b/src/core/data/scrapers/monkrus.py @@ -57,7 +57,7 @@ def _get_telegram_posts(): return(posts) -def scrape_monkrus_telegram(query): +def scrape_m0nkrus(query): posts = _get_telegram_posts() filtered_posts = [] diff --git a/src/core/data/scrapers/rutracker.py b/src/core/data/scrapers/rutracker.py index 58aecf7..bf67bb5 100644 --- a/src/core/data/scrapers/rutracker.py +++ b/src/core/data/scrapers/rutracker.py @@ -2,9 +2,8 @@ import requests from core.utils.data.state import state from core.utils.logging.logs import consoleLog - -def scrape_rutracker(search_text): - search = requests.get(f"{state.api_url}/search?q={search_text}") +def scrape_rutracker(query): + search = requests.get(f"{state.api_url}/search?q={query}") consoleLog("Sent request to server") if search: try: @@ -12,10 +11,9 @@ def scrape_rutracker(search_text): except Exception: consoleLog("No results found / No response from server") return None - else: return None -# This function utilizes the SoftwareManager server - source code can be found under the "server" branch. +# This function utilizes the SoftwareManager server - source code can be found under the SoftwareManager-Server repository. diff --git a/src/core/data/scrapers/uztracker.py b/src/core/data/scrapers/uztracker.py index 1531f9c..956629f 100644 --- a/src/core/data/scrapers/uztracker.py +++ b/src/core/data/scrapers/uztracker.py @@ -1,83 +1,35 @@ import requests -import threading from bs4 import BeautifulSoup -from core.utils.data.state import state +from urllib.parse import urljoin from core.utils.logging.logs import consoleLog -from core.utils.general.wrappers import run_thread +def scrape_uztracker(query): + base_url="https://uztracker.net/" + search_url = f"{base_url.rstrip('/')}/tracker.php?nm={query}" + consoleLog(search_url) + posts = [] -global url_uztracker -url_uztracker = "https://uztracker.net/tracker.php?nm=" - -def init_uztracker(): - global up - global soup try: - response = requests.get(url_uztracker, timeout=10) - if response.status_code == 200: - up = True - else: - up = False - consoleLog(f"Uztracker seems down, status code: {response.status_code}") - except requests.exceptions.RequestException as e: - consoleLog(f"Request Exception on {url_uztracker}:") - consoleLog(str(e)) - consoleLog("Is the Site down?") - up = False - -run_thread(threading.Thread(target=init_uztracker)) - -def scrape_uztracker(search): - if up: - search_url = url_uztracker + search - consoleLog(search_url) - result = False - global results - global resulttitles - results = [] - resulttitles = [] - - try: - resultCount = 0 - response = requests.get(search_url) - response.raise_for_status() - soup = BeautifulSoup(response.text, 'html.parser') - links = soup.find_all('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) - for link in links: - if link.b: - resultCount += 1 - results.append(link['href']) - resulttitles.append(link.b.text) - result = True - - if result: - return resulttitles, results - - if not result: - # add popup in gui for no result - return None - - except requests.RequestException as e: - if result: - consoleLog(f"Failed to fetch {search_url}: {e}") - return None - else: - consoleLog("Error: Uztracker down") - return None, None - -def get_post_title(post_url): - if up: - response = requests.get(post_url) + response = requests.get(search_url) + response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') - maintitle = soup.find(class_='tt-text') - if maintitle: - return maintitle.text - else: - consoleLog("Program not found") - else: - consoleLog("Error: Uztracker down") - return None + 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')) + 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, + url=url, + author=author + )) + return posts + + except requests.RequestException as e: + consoleLog(f"Failed to fetch {search_url}: {e}") + return None \ No newline at end of file diff --git a/src/core/interface/gui.py b/src/core/interface/gui.py index f1efad8..7639c6d 100644 --- a/src/core/interface/gui.py +++ b/src/core/interface/gui.py @@ -18,7 +18,7 @@ from PySide6.QtWidgets import ( QStyledItemDelegate, ) -from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent +from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent, QGuiApplication import darkdetect import threading import platform @@ -67,6 +67,9 @@ def download_update(latest_version): sys.exit(0) +def windowCloseHelper(): + QGuiApplication.quit() + class MainWindow(QtWidgets.QMainWindow, QWidget): log_signal = Signal(str) # Thread-safe signal for logging @@ -506,12 +509,12 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): event.accept() def set_tracker(self, _): + old_tracker = state.tracker state.tracker = self.tracker_list.currentText() - while self.horizontal_layout.count(): - item = self.horizontal_layout.takeAt(0) - if item.widget(): - item.widget().setParent(None) + old_widget = state.tracker_list[old_tracker] + self.horizontal_layout.removeWidget(old_widget) + old_widget.setParent(None) tracker_widget = state.tracker_list[state.tracker] self.horizontal_layout.addWidget(tracker_widget) diff --git a/src/core/interface/utils/searchhelper.py b/src/core/interface/utils/searchhelper.py index ee32ac7..d3e4f14 100644 --- a/src/core/interface/utils/searchhelper.py +++ b/src/core/interface/utils/searchhelper.py @@ -2,12 +2,16 @@ from PySide6.QtWidgets import QTableWidgetItem from core.utils.logging.logs import consoleLog from core.data.scrapers.uztracker import scrape_uztracker from core.data.scrapers.rutracker import scrape_rutracker -from core.data.scrapers.monkrus import scrape_monkrus_telegram -from core.data.scrapers.steamrip import scrape_steamrip_links, scrape_steamrip_game_downloads -from core.utils.network.jsonhandler import split_data, format_data, format_data_m0nkrus +from core.data.scrapers.monkrus import scrape_m0nkrus from core.utils.data.state import state import fuzzyfinder +scrapers = { + "uztracker": scrape_uztracker, + "rutracker": scrape_rutracker, + "m0nkrus": scrape_m0nkrus +} + def return_pressed(self): search_text = self.searchbar.text() if search_text == "": @@ -15,29 +19,7 @@ def return_pressed(self): return consoleLog(f"User searched for: {search_text}") - if state.tracker == "uztracker": - response = scrape_uztracker(search_text) - if response: - state.post_titles, state.post_urls = response - state.tracker_list[state.tracker].clear() - if state.post_titles and len(state.post_titles) > 0: - state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"]) - state.tracker_list[state.tracker].setRowCount(len(state.post_titles)) - for i, author in enumerate(state.post_author): - state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author)) - for i, title in enumerate(state.post_titles): - state.tracker_list[state.tracker].setItem(i, 0, QTableWidgetItem(title)) - self.show_empty_results(False) - else: - consoleLog(f"No Results for {search_text}") - state.tracker_list[state.tracker].clear() - self.show_empty_results(True) - else: - consoleLog(f"No response from uztracker") - state.tracker_list[state.tracker].clear() - self.show_empty_results(True) - - elif state.tracker == "rutracker": + if state.tracker == "rutracker": response = scrape_rutracker(search_text) if response: _, state.posts, _, _, cached = split_data(response) @@ -66,15 +48,14 @@ def return_pressed(self): state.tracker_list[state.tracker].clear() self.show_empty_results(True) - elif state.tracker == "m0nkrus": - state.posts = scrape_monkrus_telegram(search_text) - + elif state.tracker is not None: + state.posts = scrapers[state.tracker](search_text) if state.posts == []: consoleLog(f"No Results for {search_text}") state.tracker_list[state.tracker].clear() self.show_empty_results(True) else: - state.post_titles, _, state.post_author = format_data_m0nkrus(state.posts) + state.post_author, state.post_titles, state.post_urls = format_data_minimal(state.posts) self.show_empty_results(False) state.tracker_list[state.tracker].clear() state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"]) diff --git a/src/core/utils/data/state.py b/src/core/utils/data/state.py index 86732fc..ee5fa8c 100644 --- a/src/core/utils/data/state.py +++ b/src/core/utils/data/state.py @@ -8,7 +8,7 @@ class AppState(QObject): def __init__(self): super().__init__() - self.posts: List[Dict[str, Any]] = [] # title, url, author, seeders, leachers + self.posts: list[Any] | None = None self.post_titles: Optional[List] = None self.post_urls: Optional[List] = None self.post_author: List[str] = [] diff --git a/src/core/utils/logging/loghandler.py b/src/core/utils/logging/loghandler.py index 9a424a3..9569a20 100644 --- a/src/core/utils/logging/loghandler.py +++ b/src/core/utils/logging/loghandler.py @@ -1,6 +1,8 @@ -from core.utils.logging.logs import consoleLog +from core.utils.logging.logs import consoleLog, remove_download_log from core.utils.network.download import run_download_direct +import os + def split_data(data): count = data.count @@ -16,6 +18,11 @@ def check_completed(downloads, resume): run_download_direct(download.magnet_uri) consoleLog(f"Resuming {download.title}") - - - +def check_downloads(downloads): + for download in downloads: + if os.path.exists(download.path): + consoleLog(f"Existing Download: {download.title}") + else: + consoleLog(f"Inexistent Download: {download.title}") + remove_download_log(download.magnet_uri) + \ No newline at end of file diff --git a/src/core/utils/logging/logs.py b/src/core/utils/logging/logs.py index 7fd0ca9..b7a43c2 100644 --- a/src/core/utils/logging/logs.py +++ b/src/core/utils/logging/logs.py @@ -77,10 +77,12 @@ def remove_download_log(magnet_uri) -> DownloadList: else: downloads = [] + magnet_link = (magnet_uri or "").strip() + title = next((getattr(d, 'title', 'Unknown') for d in downloads if (getattr(d, 'magnet_uri', None) or '').strip() == magnet_link or (getattr(d, 'url', None) or '').strip() == magnet_link)) downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link] - consoleLog(f"Removed {magnet_link} from Log File") + consoleLog(f"Removed {title} from Log File") download_list = DownloadList(data=downloads, count=len(downloads)) with open(downloads_file, "w") as file: diff --git a/src/core/utils/network/download.py b/src/core/utils/network/download.py index 31b6bf0..26d866a 100644 --- a/src/core/utils/network/download.py +++ b/src/core/utils/network/download.py @@ -1,12 +1,9 @@ -from core.utils.data.state import state from core.utils.logging.logs import consoleLog from core.utils.data.tracker import get_item_url from core.utils.data.tracker import get_magnet_link from core.network.libtorrent_wrapper import add_download from core.utils.general.wrappers import run_thread from core.utils.logging.logs import add_download_log -import time - import threading @@ -26,7 +23,4 @@ def run_download(item, posts, post_titles): add_download_log(item, post_url, magnet_uri, False) def run_download_direct(magnet_uri): - add_download(magnet_uri) - - - + add_download(magnet_uri) \ No newline at end of file diff --git a/src/core/utils/network/jsonhandler.py b/src/core/utils/network/jsonhandler.py index bd8805f..9b205fe 100644 --- a/src/core/utils/network/jsonhandler.py +++ b/src/core/utils/network/jsonhandler.py @@ -20,12 +20,12 @@ def format_data(data): return post_titles, post_links, post_author, post_seeders, post_leechers -def format_data_m0nkrus(data): +def format_data_minimal(data): post_author = [post["author"] for post in data] post_titles = [post["title"] for post in data] post_links = [post["url"] for post in data] - return post_titles, post_links, post_author + return post_author, post_titles, post_links diff --git a/src/main.py b/src/main.py index 0f263c0..1743652 100644 --- a/src/main.py +++ b/src/main.py @@ -1,11 +1,11 @@ -from core.interface.gui import MainWindow +from core.interface.gui import MainWindow, windowCloseHelper from core.utils.data.state import state from core.utils.logging.logs import consoleLog from core.network.libtorrent_misc import send_notification, update_log from core.utils.logging.logs import get_download_logs from core.utils.general.shutdown import closehelper, shutdown_event from core.utils.general.wrappers import run_thread -from core.utils.logging.loghandler import split_data, check_completed +from core.utils.logging.loghandler import split_data, check_completed, check_downloads from core.network.interface import list_interfaces, init_interfaces from core.utils.config.config import read_config from PySide6 import QtWidgets @@ -14,15 +14,9 @@ import qdarktheme import threading import platform import signal -import argparse import sys import time - -parser = argparse.ArgumentParser() -parser.add_argument("--debug", action="store_true") -args = parser.parse_args() - def run_gui(): app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv) qdarktheme.setup_theme("auto") @@ -39,14 +33,13 @@ def run_gui(): def keyboardinterrupthandler(signum, frame): closehelper() + windowCloseHelper() -if __name__ == "__main__": +def main(): start_time = time.perf_counter() read_config() logs = get_download_logs() _, downloads = split_data(logs) - if args.debug: - state.debug = args.debug # override of read_config signal.signal(signal.SIGINT, keyboardinterrupthandler) consoleLog("Starting SoftwareManager...") consoleLog("Fetching Network Interfaces...") @@ -60,7 +53,10 @@ if __name__ == "__main__": consoleLog("Started Thread: update_log") run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume))) consoleLog("Started Thread: check_completed") + check_downloads(downloads) elapsed = time.perf_counter() - start_time consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI") run_gui() +if __name__ == "__main__": + main()