Compare commits

...

10 Commits

12 changed files with 103 additions and 130 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ def _get_telegram_posts():
return(posts) return(posts)
def scrape_monkrus_telegram(query): def scrape_m0nkrus(query):
posts = _get_telegram_posts() posts = _get_telegram_posts()
filtered_posts = [] filtered_posts = []
+3 -5
View File
@@ -2,9 +2,8 @@ import requests
from core.utils.data.state import state from core.utils.data.state import state
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
def scrape_rutracker(query):
def scrape_rutracker(search_text): search = requests.get(f"{state.api_url}/search?q={query}")
search = requests.get(f"{state.api_url}/search?q={search_text}")
consoleLog("Sent request to server") consoleLog("Sent request to server")
if search: if search:
try: try:
@@ -12,10 +11,9 @@ def scrape_rutracker(search_text):
except Exception: except Exception:
consoleLog("No results found / No response from server") consoleLog("No results found / No response from server")
return None return None
else: else:
return None 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.
+27 -75
View File
@@ -1,83 +1,35 @@
import requests import requests
import threading
from bs4 import BeautifulSoup 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.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: try:
response = requests.get(url_uztracker, timeout=10) response = requests.get(search_url)
if response.status_code == 200: response.raise_for_status()
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)
soup = BeautifulSoup(response.text, 'html.parser') soup = BeautifulSoup(response.text, 'html.parser')
maintitle = soup.find(class_='tt-text') links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_'))
if maintitle: for link in links:
return maintitle.text
else: theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
consoleLog("Program not found") url = urljoin(base_url, theme_link['href'])
else: title = theme_link.b.text
consoleLog("Error: Uztracker down") 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 return None
+5 -5
View File
@@ -157,7 +157,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
header = table.horizontalHeader() header = table.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
header.resizeSection(1, 200) header.resizeSection(1, 500)
header.setStretchLastSection(False) header.setStretchLastSection(False)
return table return table
@@ -513,12 +513,12 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
event.accept() event.accept()
def set_tracker(self, _): def set_tracker(self, _):
old_tracker = state.tracker
state.tracker = self.tracker_list.currentText() state.tracker = self.tracker_list.currentText()
while self.horizontal_layout.count(): old_widget = state.tracker_list[old_tracker]
item = self.horizontal_layout.takeAt(0) self.horizontal_layout.removeWidget(old_widget)
if item.widget(): old_widget.setParent(None)
item.widget().setParent(None)
tracker_widget = state.tracker_list[state.tracker] tracker_widget = state.tracker_list[state.tracker]
self.horizontal_layout.addWidget(tracker_widget) self.horizontal_layout.addWidget(tracker_widget)
+12 -29
View File
@@ -2,10 +2,16 @@ from PySide6.QtWidgets import QTableWidgetItem
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
from core.data.scrapers.uztracker import scrape_uztracker from core.data.scrapers.uztracker import scrape_uztracker
from core.data.scrapers.rutracker import scrape_rutracker from core.data.scrapers.rutracker import scrape_rutracker
from core.data.scrapers.monkrus import scrape_monkrus_telegram from core.data.scrapers.monkrus import scrape_m0nkrus
from core.utils.network.jsonhandler import split_data, format_data, format_data_m0nkrus from core.utils.network.jsonhandler import split_data, format_data, format_data_minimal
from core.utils.data.state import state from core.utils.data.state import state
scrapers = {
"uztracker": scrape_uztracker,
"rutracker": scrape_rutracker,
"m0nkrus": scrape_m0nkrus
}
def return_pressed(self): def return_pressed(self):
search_text = self.searchbar.text() search_text = self.searchbar.text()
if search_text == "": if search_text == "":
@@ -13,29 +19,7 @@ def return_pressed(self):
return return
consoleLog(f"User searched for: {search_text}") consoleLog(f"User searched for: {search_text}")
if state.tracker == "uztracker": if state.tracker == "rutracker":
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":
response = scrape_rutracker(search_text) response = scrape_rutracker(search_text)
if response: if response:
_, state.posts, _, _, cached = split_data(response) _, state.posts, _, _, cached = split_data(response)
@@ -64,15 +48,14 @@ def return_pressed(self):
state.tracker_list[state.tracker].clear() state.tracker_list[state.tracker].clear()
self.show_empty_results(True) self.show_empty_results(True)
elif state.tracker == "m0nkrus": elif state.tracker is not None:
state.posts = scrape_monkrus_telegram(search_text) state.posts = scrapers[state.tracker](search_text)
if state.posts == []: if state.posts == []:
consoleLog(f"No Results for {search_text}") consoleLog(f"No Results for {search_text}")
state.tracker_list[state.tracker].clear() state.tracker_list[state.tracker].clear()
self.show_empty_results(True) self.show_empty_results(True)
else: 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) self.show_empty_results(False)
state.tracker_list[state.tracker].clear() state.tracker_list[state.tracker].clear()
state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"]) state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"])
+2 -2
View File
@@ -19,7 +19,7 @@ def get_active_interfaces():
for addr in addr_list: for addr in addr_list:
if addr.family == 2: # ipv4 if addr.family == 2: # ipv4
ipv4 = addr.address ipv4 = addr.address
if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up == True: if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up:
active.append(interface) active.append(interface)
consoleLog(f"Found Active: {interface}") consoleLog(f"Found Active: {interface}")
@@ -33,7 +33,7 @@ def list_interfaces() -> None:
for addr in addr_list: for addr in addr_list:
if addr.family == 2: # ipv4 if addr.family == 2: # ipv4
ipv4 = addr.address ipv4 = addr.address
if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up == True: if not ipv4.startswith("127.") and not ipv4.startswith("169.254") and up:
status = "ACTIVE" status = "ACTIVE"
else: else:
status = "INACTIVE" status = "INACTIVE"
+22 -2
View File
@@ -1,4 +1,5 @@
import time import time
import os
from core.utils.general.wrappers import run_thread from core.utils.general.wrappers import run_thread
from core.network.interface import get_interface_ip from core.network.interface import get_interface_ip
import threading import threading
@@ -51,8 +52,26 @@ def add_download(magnet_uri, dl_path=state.download_path):
init_session() init_session()
if magnet_uri in state.active_downloads: if magnet_uri in state.active_downloads:
consoleLog("Skipping Downloading, download already running...") try:
return handle = state.active_downloads[magnet_uri]
status = handle.status()
except RuntimeError as e:
consoleLog(f"Error in LibTorrent Handle: {e}")
if status.has_metadata:
filepath = os.path.join(status.save_path, status.name)
if not os.path.exists(filepath):
consoleLog(f"File Deleted, redownloading: {status.name}")
state.dl_session.remove_torrent(handle)
del state.active_downloads[magnet_uri]
else:
consoleLog("Skipping Downloading, download already running...")
return False
else:
consoleLog("Skipping Downloading, download already running... ")
return False
magnetdl = lt.parse_magnet_uri(magnet_uri) magnetdl = lt.parse_magnet_uri(magnet_uri)
magnetdl.save_path = dl_path magnetdl.save_path = dl_path
@@ -62,6 +81,7 @@ def add_download(magnet_uri, dl_path=state.download_path):
consoleLog(f"Added {magnet_uri} to downloads") consoleLog(f"Added {magnet_uri} to downloads")
run_thread(threading.Thread(target=dl_status_loop)) run_thread(threading.Thread(target=dl_status_loop))
return True
+22
View File
@@ -3,6 +3,7 @@ from core.utils.logging.logs import update_download_completed_by_hash
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
from plyer import notification from plyer import notification
import libtorrent as lt import libtorrent as lt
import os
import time import time
@@ -58,3 +59,24 @@ def update_log(shutdown_event):
except Exception: except Exception:
pass pass
time.sleep(5) time.sleep(5)
def check_deleted_files(shutdown_event):
while not shutdown_event.is_set():
try:
for magnet_uri, magnetdl in list(state.active_downloads.items()):
if isinstance(magnetdl, dict):
continue
status = magnetdl.status()
if status.state == lt.torrent_status.seeding and status.has_metadata:
file_path = os.path.join(status.save_path, status.name)
if not os.path.exists(file_path):
consoleLog(f"Registered File Deletion: {status.name}")
state.dl_session.remove_torrent(magnetdl)
del state.active_downloads[magnet_uri]
except Exception:
pass
time.sleep(5)
+1 -1
View File
@@ -8,7 +8,7 @@ class AppState(QObject):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.posts: List[Dict[str, Any]] = [] self.posts: list[Any] | None = None
self.post_titles: Optional[List] = None self.post_titles: Optional[List] = None
self.post_urls: Optional[List] = None self.post_urls: Optional[List] = None
self.post_author: List[str] = [] self.post_author: List[str] = []
+4 -8
View File
@@ -1,12 +1,9 @@
from core.utils.data.state import state
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
from core.utils.data.tracker import get_item_url from core.utils.data.tracker import get_item_url
from core.utils.data.tracker import get_magnet_link from core.utils.data.tracker import get_magnet_link
from core.network.libtorrent_wrapper import add_download from core.network.libtorrent_wrapper import add_download
from core.utils.general.wrappers import run_thread from core.utils.general.wrappers import run_thread
from core.utils.logging.logs import add_download_log from core.utils.logging.logs import add_download_log
import time
import threading import threading
@@ -22,11 +19,10 @@ def run_download(item, posts, post_titles):
consoleLog(f"Selected URL: {post_url}") consoleLog(f"Selected URL: {post_url}")
magnet_uri = get_magnet_link(post_url) magnet_uri = get_magnet_link(post_url)
add_download(magnet_uri) if add_download(magnet_uri):
add_download_log(item, post_url, magnet_uri, False) add_download_log(item, post_url, magnet_uri, False)
def run_download_direct(magnet_uri): def run_download_direct(magnet_uri):
consoleLog(f"Direct download: {magnet_uri[:60]}")
add_download(magnet_uri) add_download(magnet_uri)
add_download_log("Direct Download", "", magnet_uri, False)
+2 -2
View File
@@ -20,12 +20,12 @@ def format_data(data):
return post_titles, post_links, post_author, post_seeders, post_leechers 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_author = [post["author"] for post in data]
post_titles = [post["title"] for post in data] post_titles = [post["title"] for post in data]
post_links = [post["url"] 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
+3 -1
View File
@@ -1,7 +1,7 @@
from core.interface.gui import MainWindow, windowCloseHelper from core.interface.gui import MainWindow, windowCloseHelper
from core.utils.data.state import state from core.utils.data.state import state
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
from core.network.libtorrent_misc import send_notification, update_log from core.network.libtorrent_misc import send_notification, update_log, check_deleted_files
from core.utils.logging.logs import get_download_logs from core.utils.logging.logs import get_download_logs
from core.utils.general.shutdown import closehelper, shutdown_event from core.utils.general.shutdown import closehelper, shutdown_event
from core.utils.general.wrappers import run_thread from core.utils.general.wrappers import run_thread
@@ -53,6 +53,8 @@ def main():
consoleLog("Started Thread: update_log") consoleLog("Started Thread: update_log")
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume))) run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
consoleLog("Started Thread: check_completed") consoleLog("Started Thread: check_completed")
run_thread(threading.Thread(target=check_deleted_files, args=(shutdown_event,), daemon=True))
consoleLog("Started Thread: check_deleted_files")
check_downloads(downloads) check_downloads(downloads)
elapsed = time.perf_counter() - start_time elapsed = time.perf_counter() - start_time
consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI") consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI")