Compare commits

..

8 Commits

Author SHA1 Message Date
Vxrtrauter 5af9e79ecc Refractor searchhelper to finally be readable, reverd uztracker scraper to old format since i didn't like it 2025-10-16 22:55:52 +02:00
shayaa 4dadc9ba50 Add debug feature 2025-10-16 15:55:39 +02:00
Vxrtrauter 719c4e86e8 improved cache debug 2025-10-16 15:53:05 +02:00
Vxrtrauter 999c8e9ba4 improved debug output 2025-10-16 15:49:00 +02:00
Vxrtrauter 3f6889da56 improve debug messages 2025-10-16 15:40:43 +02:00
Vxrtrauter 69ac739be6 add debug arg 2025-10-16 15:36:10 +02:00
shayaa 141cc8fadc Update README.md 2025-10-14 23:13:14 +02:00
shayaa 80c6ece72e Update README.md 2025-10-14 23:12:34 +02:00
10 changed files with 108 additions and 69 deletions
+11
View File
@@ -27,6 +27,17 @@ SoftwareManager is a Python-based GUI/TUI tool that simplifies downloading and m
- If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of its branch. - If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of its branch.
- This tool is for educational purposes only. Use it responsibly. - This tool is for educational purposes only. Use it responsibly.
## Star History
<a href="https://www.star-history.com/#KeksPirates/SoftwareManager&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&legend=top-left" />
</picture>
</a>
--- ---
**Disclaimer:** SoftwareManager is intended for legal and ethical use only. Ensure compliance with applicable laws and regulations when using this tool. **Disclaimer:** SoftwareManager is intended for legal and ethical use only. Ensure compliance with applicable laws and regulations when using this tool.
+2
View File
@@ -4,6 +4,8 @@ from core.utils.data.state import state
def scrape_rutracker(search_text): def scrape_rutracker(search_text):
search = requests.get(f"{state.api_url}/search/{search_text}") search = requests.get(f"{state.api_url}/search/{search_text}")
if state.debug:
print("Sent request to server")
if search: if search:
try: try:
return search.text return search.text
+37 -42
View File
@@ -17,62 +17,56 @@ async def init_uztracker():
up = True up = True
else: else:
up = False up = False
print(f"Uztracker seems down, status code: {response.status_code}") if state.debug:
print(f"Uztracker seems down, status code: {response.status_code}")
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print(f"\nRequest Exception on {url_uztracker}:") if state.debug:
print(e) print(f"\nRequest Exception on {url_uztracker}:")
print("\nIs the Site down?") print(e)
print("\nIs the Site down?")
up = False up = False
asyncio.run(init_uztracker()) asyncio.run(init_uztracker())
async def scrape_uztracker(search, max_results=450): def scrape_uztracker(search):
if up: if up:
search_url = url_uztracker + search
if state.debug:
print(search_url)
result = False result = False
global results global results
global resulttitles global resulttitles
results = [] results = []
resulttitles = [] resulttitles = []
per_page = 50 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
for start in range(0, max_results, per_page): if result:
search_url = url_uztracker + search + f"&start={start}" return resulttitles, results
if state.debug:
print(search_url)
async with aiohttp.ClientSession() as session: if not result:
try: # add popup in gui for no result
async with session.get(search_url) as response: return None
response.raise_for_status()
text = await response.text()
soup = BeautifulSoup(text, 'html.parser')
links = soup.find_all('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
resultCount = 0
for link in links:
if link.b:
resultCount += 1
results.append(link['href'])
resulttitles.append(link.b.text)
result = True
if resultCount == 0:
# add popup in gui for no result
break
except aiohttp.ClientError as e:
if result:
print(f"Failed to fetch {search_url}: {e}")
return None
else: except requests.RequestException as e:
if result and state.debug:
print(f"Failed to fetch {search_url}: {e}")
return None
else:
if state.debug:
print("Error: Uztracker down") print("Error: Uztracker down")
return None, None return None, None
if not results:
return None, None
return resulttitles, results
def get_post_title(post_url): def get_post_title(post_url):
if up: if up:
@@ -85,7 +79,8 @@ def get_post_title(post_url):
if state.debug: if state.debug:
print("Program not found") print("Program not found")
else: else:
print("Error: Uztracker down") if state.debug:
print("Error: Uztracker down")
return None return None
-1
View File
@@ -114,7 +114,6 @@ def settings_dialog(self):
save_btn.clicked.connect(lambda: save_settings(thread_box.value(), close_settings, api_url.text(), download_path.text(), speed_limit.value())) save_btn.clicked.connect(lambda: save_settings(thread_box.value(), close_settings, api_url.text(), download_path.text(), speed_limit.value()))
cancel_btn.clicked.connect(dialog.reject) cancel_btn.clicked.connect(dialog.reject)
print(thread_box.value())
layout.addWidget(cancel_btn) layout.addWidget(cancel_btn)
layout.addWidget(save_btn) layout.addWidget(save_btn)
+36 -20
View File
@@ -5,29 +5,41 @@ from core.utils.network.jsonhandler import split_data, format_data
from core.utils.data.state import state from core.utils.data.state import state
def return_pressed(self): def return_pressed(self):
search_text = self.searchbar.text() search_text = self.searchbar.text()
if search_text == "": if search_text == "":
if state.debug:
print("Error: Can't search for nothing")
return
if state.debug: if state.debug:
print("User searched for:", search_text) print("Error: Can't search for nothing")
if state.tracker == "uztracker": return
response = asyncio.run(scrape_uztracker(search_text)) if state.debug:
if state.tracker == "rutracker": print("User searched for:", search_text)
response = scrape_rutracker(search_text)
if state.tracker == "uztracker":
response = scrape_uztracker(search_text)
if response: if response:
if state.tracker == "uztracker": state.post_titles, state.post_urls = response
state.post_titles, state.post_urls, = response self.softwareList.clear()
if state.post_titles and len(state.post_titles) > 0:
self.softwareList.addItems(state.post_titles)
self.show_empty_results(False)
self.post_author_list.clear()
else:
if state.debug:
print(f"No Results for {search_text}")
self.softwareList.clear() self.softwareList.clear()
if state.post_titles: self.show_empty_results(True)
self.softwareList.addItems(state.post_titles) else:
self.post_author_list.addItems(state.post_author) if state.debug:
if state.tracker == "rutracker": print(f"No response from uztracker")
_, state.posts, _, _, cached = split_data(response) self.softwareList.clear()
self.show_empty_results(True)
elif state.tracker == "rutracker":
response = scrape_rutracker(search_text)
if response:
_, state.posts, _, _, cached = split_data(response)
if state.posts == []: if state.posts == []:
if state.debug: if state.debug:
print(f"No Results found for \"{search_text}\"") print(f"No Results for {search_text}")
self.softwareList.clear() self.softwareList.clear()
self.show_empty_results(True) self.show_empty_results(True)
else: else:
@@ -38,5 +50,9 @@ def return_pressed(self):
self.softwareList.clear() self.softwareList.clear()
self.softwareList.addItems(state.post_titles) self.softwareList.addItems(state.post_titles)
if state.debug == True: if state.debug == True:
print(f"Cached: {cached}") print(f"Response Cached: {cached}")
else:
if state.debug:
print(f"No response from rutracker")
self.softwareList.clear()
self.show_empty_results(True)
+5
View File
@@ -1,11 +1,16 @@
from .aria2_integration import run_aria2p from .aria2_integration import run_aria2p
from core.utils.data.state import state
def start_client(): def start_client():
global aria2 global aria2
aria2 = run_aria2p() aria2 = run_aria2p()
if state.debug:
print("Started Aria2p")
def add_magnet(uri): def add_magnet(uri):
aria2.add_magnet(uri) aria2.add_magnet(uri)
if state.debug:
print("Magnet URI added to Aria2")
+8 -1
View File
@@ -5,16 +5,21 @@ from core.utils.network.jsonhandler import format_data
def get_item_url(item, posts, post_titles): # oftwarelist currentitem, post list (dict), post titles list def get_item_url(item, posts, post_titles): # softwarelist currentitem, post list (dict), post titles list
post_index = post_titles.index(item) post_index = post_titles.index(item)
if 0 <= post_index < len(post_titles): if 0 <= post_index < len(post_titles):
if state.tracker == "uztracker": if state.tracker == "uztracker":
item = "https://uztracker.net/" + state.post_urls[post_index].lstrip("./") item = "https://uztracker.net/" + state.post_urls[post_index].lstrip("./")
if state.debug:
print("Found post URL: ", item)
return item return item
if state.tracker == "rutracker": if state.tracker == "rutracker":
item_dict = posts[post_index] item_dict = posts[post_index]
_, post_links, _ = format_data([item_dict]) _, post_links, _ = format_data([item_dict])
if state.debug:
print("Found post URL: ", post_links[0])
return post_links[0] return post_links[0]
return None return None
@@ -22,6 +27,8 @@ def get_item_url(item, posts, post_titles): # oftwarelist currentitem, post list
def get_magnet_link(post_url): def get_magnet_link(post_url):
try: try:
response = requests.get(post_url) # eventually impl. cloudscraper response = requests.get(post_url) # eventually impl. cloudscraper
if state.debug:
print("Sent Request to retrieve Magnet Link...")
response.raise_for_status() response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser') soup = BeautifulSoup(response.text, 'html.parser')
magnet_link = soup.find('a', href=lambda x: x and x.startswith('magnet:')) magnet_link = soup.find('a', href=lambda x: x and x.startswith('magnet:'))
+1 -1
View File
@@ -22,7 +22,7 @@ def kill_aria2server():
if proc.name() == process: if proc.name() == process:
proc.kill() proc.kill()
if state.debug: if state.debug:
print("Killed Aria2c") print(f"Killed Aria2c (PID {proc.pid})")
except psutil.NoSuchProcess: except psutil.NoSuchProcess:
pass pass
+1 -1
View File
@@ -10,7 +10,7 @@ import threading
def download_selected(item, posts, post_titles): def download_selected(item, posts, post_titles):
if item is not None: if item is not None:
if state.debug: if state.debug:
print(f"network {item.text()}") print(f"Downloading {item.text()}")
run_thread(threading.Thread(target=run_download, args=(item.text(), posts, post_titles))) run_thread(threading.Thread(target=run_download, args=(item.text(), posts, post_titles)))
else: else:
+7 -3
View File
@@ -10,11 +10,14 @@ import qdarktheme
import darkdetect import darkdetect
import threading import threading
import signal import signal
import atexit import argparse
import sys import sys
# Debug Output
state.debug = True
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action="store_true")
args = parser.parse_args()
def run_gui(): def run_gui():
@@ -36,6 +39,7 @@ def keyboardinterrupthandler(signum, frame):
if __name__ == "__main__": if __name__ == "__main__":
read_config() read_config()
state.debug = args.debug # override of read_config
if state.debug: if state.debug:
print("Starting Aria2 Server") print("Starting Aria2 Server")
state.aria2process = run_aria2server() state.aria2process = run_aria2server()