From 69ac739be65e0f9cae8002b640281bfed034add2 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Thu, 16 Oct 2025 15:36:10 +0200 Subject: [PATCH 01/45] add debug arg --- main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 1e0baad..eb75bb9 100644 --- a/main.py +++ b/main.py @@ -10,11 +10,14 @@ import qdarktheme import darkdetect import threading import signal -import atexit +import argparse import sys -# Debug Output -state.debug = True + + +parser = argparse.ArgumentParser() +parser.add_argument("--debug", action="store_true") +args = parser.parse_args() def run_gui(): @@ -36,6 +39,7 @@ def keyboardinterrupthandler(signum, frame): if __name__ == "__main__": read_config() + state.debug = args.debug # override of read_config if state.debug: print("Starting Aria2 Server") state.aria2process = run_aria2server() From 3f6889da5604a0c820ef37085bf0d4f244138934 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Thu, 16 Oct 2025 15:40:43 +0200 Subject: [PATCH 02/45] improve debug messages --- core/data/scrapers/rutracker.py | 2 ++ core/data/scrapers/uztracker.py | 18 +++++++++++------- core/interface/dialogs/settings.py | 1 - 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/data/scrapers/rutracker.py b/core/data/scrapers/rutracker.py index 84e702f..5baf263 100644 --- a/core/data/scrapers/rutracker.py +++ b/core/data/scrapers/rutracker.py @@ -4,6 +4,8 @@ from core.utils.data.state import state def scrape_rutracker(search_text): search = requests.get(f"{state.api_url}/search/{search_text}") + if state.debug: + print("Sent request to server") if search: try: return search.text diff --git a/core/data/scrapers/uztracker.py b/core/data/scrapers/uztracker.py index 00cf00f..c7aa4e0 100644 --- a/core/data/scrapers/uztracker.py +++ b/core/data/scrapers/uztracker.py @@ -17,11 +17,13 @@ async def init_uztracker(): up = True else: 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: - print(f"\nRequest Exception on {url_uztracker}:") - print(e) - print("\nIs the Site down?") + if state.debug: + print(f"\nRequest Exception on {url_uztracker}:") + print(e) + print("\nIs the Site down?") up = False asyncio.run(init_uztracker()) @@ -61,12 +63,13 @@ async def scrape_uztracker(search, max_results=450): break except aiohttp.ClientError as e: - if result: + if result and state.debug: print(f"Failed to fetch {search_url}: {e}") return None else: - print("Error: Uztracker down") + if state.debug: + print("Error: Uztracker down") return None, None if not results: @@ -85,7 +88,8 @@ def get_post_title(post_url): if state.debug: print("Program not found") else: - print("Error: Uztracker down") + if state.debug: + print("Error: Uztracker down") return None diff --git a/core/interface/dialogs/settings.py b/core/interface/dialogs/settings.py index fb16321..cba35fc 100644 --- a/core/interface/dialogs/settings.py +++ b/core/interface/dialogs/settings.py @@ -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())) cancel_btn.clicked.connect(dialog.reject) - print(thread_box.value()) layout.addWidget(cancel_btn) layout.addWidget(save_btn) From 999c8e9ba4e8bb086e4d24bfcc047caad1fd25ab Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Thu, 16 Oct 2025 15:49:00 +0200 Subject: [PATCH 03/45] improved debug output --- core/network/aria2_wrapper.py | 5 +++++ core/utils/data/tracker.py | 9 ++++++++- core/utils/general/shutdown.py | 2 +- core/utils/network/download.py | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/network/aria2_wrapper.py b/core/network/aria2_wrapper.py index c3be92c..9bf5d8b 100644 --- a/core/network/aria2_wrapper.py +++ b/core/network/aria2_wrapper.py @@ -1,11 +1,16 @@ from .aria2_integration import run_aria2p +from core.utils.data.state import state def start_client(): global aria2 aria2 = run_aria2p() + if state.debug: + print("Started Aria2p") def add_magnet(uri): aria2.add_magnet(uri) + if state.debug: + print("Magnet URI added to Aria2") diff --git a/core/utils/data/tracker.py b/core/utils/data/tracker.py index 27e7f27..dbd5090 100644 --- a/core/utils/data/tracker.py +++ b/core/utils/data/tracker.py @@ -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) if 0 <= post_index < len(post_titles): if state.tracker == "uztracker": item = "https://uztracker.net/" + state.post_urls[post_index].lstrip("./") + if state.debug: + print("Found post URL: ", item) return item if state.tracker == "rutracker": item_dict = posts[post_index] _, post_links, _ = format_data([item_dict]) + if state.debug: + print("Found post URL: ", post_links[0]) return post_links[0] + return None @@ -22,6 +27,8 @@ def get_item_url(item, posts, post_titles): # oftwarelist currentitem, post list def get_magnet_link(post_url): try: response = requests.get(post_url) # eventually impl. cloudscraper + if state.debug: + print("Sent Request to retrieve Magnet Link...") response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') magnet_link = soup.find('a', href=lambda x: x and x.startswith('magnet:')) diff --git a/core/utils/general/shutdown.py b/core/utils/general/shutdown.py index 38bf9fe..7642e4f 100644 --- a/core/utils/general/shutdown.py +++ b/core/utils/general/shutdown.py @@ -22,7 +22,7 @@ def kill_aria2server(): if proc.name() == process: proc.kill() if state.debug: - print("Killed Aria2c") + print(f"Killed Aria2c (PID {proc.pid})") except psutil.NoSuchProcess: pass diff --git a/core/utils/network/download.py b/core/utils/network/download.py index 9c6f719..dbf7ef5 100644 --- a/core/utils/network/download.py +++ b/core/utils/network/download.py @@ -10,7 +10,7 @@ import threading def download_selected(item, posts, post_titles): if item is not None: 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))) else: From 719c4e86e81a8f63a07c5f510611829827a40a34 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Thu, 16 Oct 2025 15:53:05 +0200 Subject: [PATCH 04/45] improved cache debug --- core/interface/utils/searchhelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/interface/utils/searchhelper.py b/core/interface/utils/searchhelper.py index 73e2754..936f255 100644 --- a/core/interface/utils/searchhelper.py +++ b/core/interface/utils/searchhelper.py @@ -38,5 +38,5 @@ def return_pressed(self): self.softwareList.clear() self.softwareList.addItems(state.post_titles) if state.debug == True: - print(f"Cached: {cached}") + print(f"Response Cached: {cached}") From 5af9e79ecc36414cbf71b67f392b947526b0c107 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Thu, 16 Oct 2025 22:55:52 +0200 Subject: [PATCH 05/45] Refractor searchhelper to finally be readable, reverd uztracker scraper to old format since i didn't like it --- core/data/scrapers/uztracker.py | 69 ++++++++++++---------------- core/interface/utils/searchhelper.py | 54 ++++++++++++++-------- 2 files changed, 65 insertions(+), 58 deletions(-) diff --git a/core/data/scrapers/uztracker.py b/core/data/scrapers/uztracker.py index c7aa4e0..d39bbaf 100644 --- a/core/data/scrapers/uztracker.py +++ b/core/data/scrapers/uztracker.py @@ -28,54 +28,45 @@ async def init_uztracker(): asyncio.run(init_uztracker()) -async def scrape_uztracker(search, max_results=450): +def scrape_uztracker(search): if up: + search_url = url_uztracker + search + if state.debug: + print(search_url) result = False global results global resulttitles - results = [] + results = [] 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): - search_url = url_uztracker + search + f"&start={start}" - if state.debug: - print(search_url) + if result: + return resulttitles, results - async with aiohttp.ClientSession() as session: - try: - async with session.get(search_url) as response: - 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 and state.debug: - print(f"Failed to fetch {search_url}: {e}") - return None + if not result: + # add popup in gui for no result + return None - else: - if state.debug: - print("Error: Uztracker down") - return None, None - - if not results: - return None, None - - return resulttitles, results + 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") + return None, None def get_post_title(post_url): if up: diff --git a/core/interface/utils/searchhelper.py b/core/interface/utils/searchhelper.py index 936f255..6fdd3dc 100644 --- a/core/interface/utils/searchhelper.py +++ b/core/interface/utils/searchhelper.py @@ -5,29 +5,41 @@ from core.utils.network.jsonhandler import split_data, format_data from core.utils.data.state import state def return_pressed(self): - search_text = self.searchbar.text() - if search_text == "": - if state.debug: - print("Error: Can't search for nothing") - return + search_text = self.searchbar.text() + if search_text == "": if state.debug: - print("User searched for:", search_text) - if state.tracker == "uztracker": - response = asyncio.run(scrape_uztracker(search_text)) - if state.tracker == "rutracker": - response = scrape_rutracker(search_text) + print("Error: Can't search for nothing") + return + if state.debug: + print("User searched for:", search_text) + + if state.tracker == "uztracker": + response = scrape_uztracker(search_text) 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() - if state.post_titles: - self.softwareList.addItems(state.post_titles) - self.post_author_list.addItems(state.post_author) - if state.tracker == "rutracker": - _, state.posts, _, _, cached = split_data(response) + self.show_empty_results(True) + else: + if state.debug: + print(f"No response from uztracker") + 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.debug: - print(f"No Results found for \"{search_text}\"") + print(f"No Results for {search_text}") self.softwareList.clear() self.show_empty_results(True) else: @@ -39,4 +51,8 @@ def return_pressed(self): self.softwareList.addItems(state.post_titles) if state.debug == True: print(f"Response Cached: {cached}") - + else: + if state.debug: + print(f"No response from rutracker") + self.softwareList.clear() + self.show_empty_results(True) \ No newline at end of file From 2f78b812baeaf681320e96476384f081e3a365da Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Fri, 17 Oct 2025 20:23:44 +0200 Subject: [PATCH 06/45] add besic scraping with message retrieving logic; still featuring full testing & debug outpus --- core/data/scrapers/monkrus.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/data/scrapers/monkrus.py diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py new file mode 100644 index 0000000..64c25ad --- /dev/null +++ b/core/data/scrapers/monkrus.py @@ -0,0 +1,18 @@ +from bs4 import BeautifulSoup +import requests + + + +def scrape_monkrus_telegram(): + telegram_channel = "https://t.me/s/real_monkrus/rss" + + response = requests.get(telegram_channel) + print(response.status_code) # rm later + + soup = BeautifulSoup(response.text, 'html.parser') + posts_txt = soup.find_all('div', class_=["tgme_widget_message_wrap js-widget_message_wrap", "tgme_widget_message_wrap js-widget_message_wrap date_visible"]) + + print(posts_txt) + + +scrape_monkrus_telegram() \ No newline at end of file From 5637e8065703f226761f80d18b33e93694a5b0fc Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Fri, 17 Oct 2025 21:23:17 +0200 Subject: [PATCH 07/45] added some more testing stuff --- core/data/scrapers/monkrus.py | 14 +++++++++++--- core/utils/data/state.py | 7 +++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index 64c25ad..3b60645 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -1,18 +1,26 @@ +from core.utils.data.state import state, Post from bs4 import BeautifulSoup import requests def scrape_monkrus_telegram(): - telegram_channel = "https://t.me/s/real_monkrus/rss" + telegram_channel = "https://t.me/s/real_monkrus/" + posts: list[Post] = [] response = requests.get(telegram_channel) print(response.status_code) # rm later - soup = BeautifulSoup(response.text, 'html.parser') - posts_txt = soup.find_all('div', class_=["tgme_widget_message_wrap js-widget_message_wrap", "tgme_widget_message_wrap js-widget_message_wrap date_visible"]) + soup = BeautifulSoup(response.text, "html.parser") + posts = soup.find_all("div", class_=["tgme_widget_message_wrap js-widget_message_wrap", "tgme_widget_message_wrap js-widget_message_wrap date_visible"]) + + for post in posts: + posts_txt = post.find("div", class_="tgme_widget_message_text js-message_text") # this doesnt work + + print(posts_txt) + scrape_monkrus_telegram() \ No newline at end of file diff --git a/core/utils/data/state.py b/core/utils/data/state.py index 633415e..deb9558 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -20,3 +20,10 @@ class AppState: aria2_threads: int = 4 state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloads=[], download_path="") + +@dataclass +class Post: + id: int + title: str + url: str + author: str From 1cab671a130aa7de9ee191be6e35e1fc8419a8bb Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 01:08:36 +0200 Subject: [PATCH 08/45] add more testing stuff --- core/data/scrapers/monkrus.py | 11 ++++++----- core/utils/data/state.py | 5 +---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index 3b60645..ea4bf8c 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -12,14 +12,15 @@ def scrape_monkrus_telegram(): print(response.status_code) # rm later soup = BeautifulSoup(response.text, "html.parser") - posts = soup.find_all("div", class_=["tgme_widget_message_wrap js-widget_message_wrap", "tgme_widget_message_wrap js-widget_message_wrap date_visible"]) + bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") - for post in posts: - posts_txt = post.find("div", class_="tgme_widget_message_text js-message_text") # this doesnt work + for bubble in bubbles: + post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") # this doesnt work + if post_txt.b: + posts.append(Post(text=post_txt.get_text())) - - print(posts_txt) + print(post_txt.b.text) diff --git a/core/utils/data/state.py b/core/utils/data/state.py index deb9558..ebedd07 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -23,7 +23,4 @@ state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloa @dataclass class Post: - id: int - title: str - url: str - author: str + text: str From 7e5e3033e6f3b52d557c45ccce5e0322e4289a39 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 01:22:11 +0200 Subject: [PATCH 09/45] add scraping logic --- core/data/scrapers/monkrus.py | 18 ++++++++++++++---- core/utils/data/state.py | 5 ++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index ea4bf8c..d3cdff4 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -15,12 +15,22 @@ def scrape_monkrus_telegram(): bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") for bubble in bubbles: - post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") # this doesnt work - if post_txt.b: - posts.append(Post(text=post_txt.get_text())) + post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") + + title = post_txt.b.text + url = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) - print(post_txt.b.text) + + posts.append(Post( + id = len(posts) + 1, + title = title, + url = url, + author = "m0nkrus" + + )) + + print(posts) diff --git a/core/utils/data/state.py b/core/utils/data/state.py index ebedd07..deb9558 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -23,4 +23,7 @@ state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloa @dataclass class Post: - text: str + id: int + title: str + url: str + author: str From 1822a150d7beafb3a13dc0455f50797d671285a6 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 01:33:12 +0200 Subject: [PATCH 10/45] finnish monkrus scraper (probably) --- core/data/scrapers/monkrus.py | 22 +++++++--------------- core/utils/data/state.py | 7 ------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index d3cdff4..aad7aef 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -1,4 +1,4 @@ -from core.utils.data.state import state, Post +from core.utils.data.state import state from bs4 import BeautifulSoup import requests @@ -6,11 +6,9 @@ import requests def scrape_monkrus_telegram(): telegram_channel = "https://t.me/s/real_monkrus/" - posts: list[Post] = [] + posts: list[dict] = [] response = requests.get(telegram_channel) - print(response.status_code) # rm later - soup = BeautifulSoup(response.text, "html.parser") bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") @@ -20,18 +18,12 @@ def scrape_monkrus_telegram(): title = post_txt.b.text url = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) - - - posts.append(Post( + posts.append(dict( + author = "m0nkrus", id = len(posts) + 1, title = title, - url = url, - author = "m0nkrus" - + url = url["href"] )) - print(posts) - - - -scrape_monkrus_telegram() \ No newline at end of file + posts.reverse() # reverse to show most recent posts (bubbles) at top of list + return(posts) \ No newline at end of file diff --git a/core/utils/data/state.py b/core/utils/data/state.py index deb9558..633415e 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -20,10 +20,3 @@ class AppState: aria2_threads: int = 4 state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloads=[], download_path="") - -@dataclass -class Post: - id: int - title: str - url: str - author: str From c99ce339e07325834617a643aa228a101cb01f36 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 01:49:00 +0200 Subject: [PATCH 11/45] fully impl. monkrus scraper (+ add search logic to scraper) --- core/data/scrapers/monkrus.py | 16 ++++++++-------- core/interface/gui.py | 2 +- core/interface/utils/searchhelper.py | 20 ++++++++++++++++++-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index aad7aef..e38a791 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -1,10 +1,9 @@ -from core.utils.data.state import state from bs4 import BeautifulSoup import requests -def scrape_monkrus_telegram(): +def scrape_monkrus_telegram(query): telegram_channel = "https://t.me/s/real_monkrus/" posts: list[dict] = [] @@ -18,12 +17,13 @@ def scrape_monkrus_telegram(): title = post_txt.b.text url = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) - posts.append(dict( - author = "m0nkrus", - id = len(posts) + 1, - title = title, - url = url["href"] - )) + if query.lower() in title.lower(): + posts.append(dict( + author = "m0nkrus", + id = len(posts) + 1, + title = title, + url = url["href"] + )) posts.reverse() # reverse to show most recent posts (bubbles) at top of list return(posts) \ No newline at end of file diff --git a/core/interface/gui.py b/core/interface/gui.py index 1c8caf7..6b4e5bd 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -139,7 +139,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): toolbar.setLayoutDirection(Qt.RightToLeft) self.tracker_list = QComboBox() - self.tracker_list.addItems(["rutracker", "uztracker"]) + self.tracker_list.addItems(["rutracker", "uztracker", "m0nkrus"]) self.tracker_list.activated.connect(self.set_tracker) diff --git a/core/interface/utils/searchhelper.py b/core/interface/utils/searchhelper.py index 6fdd3dc..cf6f22a 100644 --- a/core/interface/utils/searchhelper.py +++ b/core/interface/utils/searchhelper.py @@ -1,6 +1,6 @@ -import asyncio 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.utils.network.jsonhandler import split_data, format_data from core.utils.data.state import state @@ -55,4 +55,20 @@ def return_pressed(self): if state.debug: print(f"No response from rutracker") self.softwareList.clear() - self.show_empty_results(True) \ No newline at end of file + self.show_empty_results(True) + + elif state.tracker == "m0nkrus": + state.posts = scrape_monkrus_telegram(search_text) + + if state.posts == []: + if state.debug: + print(f"No Results for {search_text}") + self.softwareList.clear() + self.show_empty_results(True) + else: + state.post_titles, _, state.post_author = format_data(state.posts) + self.show_empty_results(False) + self.post_author_list.clear() + self.post_author_list.addItems(state.post_author) + self.softwareList.clear() + self.softwareList.addItems(state.post_titles) From bd8320a26ccca6a85434477a0a0c6f28d3bc32e9 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 02:25:15 +0200 Subject: [PATCH 12/45] improve monkrus scraper for more results --- core/data/scrapers/monkrus.py | 58 +++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/core/data/scrapers/monkrus.py b/core/data/scrapers/monkrus.py index e38a791..6bfd1ea 100644 --- a/core/data/scrapers/monkrus.py +++ b/core/data/scrapers/monkrus.py @@ -1,29 +1,41 @@ from bs4 import BeautifulSoup import requests - - def scrape_monkrus_telegram(query): - telegram_channel = "https://t.me/s/real_monkrus/" + post = 100 + max_posts = 250 posts: list[dict] = [] + added = set() + + while post <= max_posts: + url = f"https://t.me/s/real_monkrus/{post}" + response = requests.get(url) + 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 not post_txt or not post_txt.b: + continue + + title = post_txt.b.text + link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) + + if query.lower() in title.lower() and link: + post_url = link["href"] + if post_url not in added: + added.add(post_url) + posts.append(dict( + author="m0nkrus", + id=len(posts) + 1, + title=title, + url=post_url + )) + + post += 22 - response = requests.get(telegram_channel) - soup = BeautifulSoup(response.text, "html.parser") - bubbles = soup.find_all("div", class_="tgme_widget_message_bubble") - - for bubble in bubbles: - post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text") - - title = post_txt.b.text - url = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net")) - - if query.lower() in title.lower(): - posts.append(dict( - author = "m0nkrus", - id = len(posts) + 1, - title = title, - url = url["href"] - )) - - posts.reverse() # reverse to show most recent posts (bubbles) at top of list - return(posts) \ No newline at end of file + posts.reverse() + return(posts) From 10c935e852d618194839dc4576b9ce3df73b1aaa Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 02:31:00 +0200 Subject: [PATCH 13/45] fix tracker.py to handle m0nkrus scraper for downloads --- core/utils/data/tracker.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/utils/data/tracker.py b/core/utils/data/tracker.py index dbd5090..172788d 100644 --- a/core/utils/data/tracker.py +++ b/core/utils/data/tracker.py @@ -19,6 +19,12 @@ def get_item_url(item, posts, post_titles): # softwarelist currentitem, post lis if state.debug: print("Found post URL: ", post_links[0]) return post_links[0] + if state.tracker == "m0nkrus": + item_dict = posts[post_index] + _, post_links, _ = format_data([item_dict]) + if state.debug: + print("Found post URL: ", post_links[0]) + return post_links[0] return None From 2a86e4b774adb8a8a50ea96f53c725e585a862ad Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 15:50:16 +0200 Subject: [PATCH 14/45] update workflows --- .github/workflows/dev.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 976f996..c7ea42d 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -25,14 +25,14 @@ jobs: matrix: include: - os: ubuntu-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-linux - asset_name: SoftwareManager-dev-${{ github.sha }}-linux + artifact_name: SoftwareManager-dev-linux + asset_name: SoftwareManager-dev-linux - os: windows-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-windows.exe - asset_name: SoftwareManager-dev-${{ github.sha }}-windows.exe + artifact_name: SoftwareManager-dev-windows.exe + asset_name: SoftwareManager-dev-windows.exe - os: macos-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-macos - asset_name: SoftwareManager-dev-${{ github.sha }}-macos + artifact_name: SoftwareManager-dev-macos + asset_name: SoftwareManager-dev-macos runs-on: ${{ matrix.os }} @@ -134,7 +134,7 @@ jobs: tag_name: ${{ steps.version.outputs.version }} name: ${{ steps.version.outputs.version }} draft: false - prerelease: true + prerelease: false files: | release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-linux release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-windows.exe From bd71b0e6457f8cc186cb55e0afe9eba9b4f09126 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 15:55:48 +0200 Subject: [PATCH 15/45] update workflows --- .github/workflows/dev.yml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index c7ea42d..6d057f1 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -25,14 +25,14 @@ jobs: matrix: include: - os: ubuntu-latest - artifact_name: SoftwareManager-dev-linux - asset_name: SoftwareManager-dev-linux + artifact_name: SoftwareManager-dev-${{ github.sha }}-linux + asset_name: SoftwareManager-dev-${{ github.sha }}-linux - os: windows-latest - artifact_name: SoftwareManager-dev-windows.exe - asset_name: SoftwareManager-dev-windows.exe + artifact_name: SoftwareManager-dev-${{ github.sha }}-windows.exe + asset_name: SoftwareManager-dev-${{ github.sha }}-windows.exe - os: macos-latest - artifact_name: SoftwareManager-dev-macos - asset_name: SoftwareManager-dev-macos + artifact_name: SoftwareManager-dev-${{ github.sha }}-macos + asset_name: SoftwareManager-dev-${{ github.sha }}-macos runs-on: ${{ matrix.os }} @@ -121,11 +121,10 @@ jobs: - name: Prepare release files run: | mkdir -p release - SHORT_SHA="${{ steps.version.outputs.short_sha }}" - cp artifacts/ubuntu-latest-build/SoftwareManager-dev-*-linux release/SoftwareManager-dev-${SHORT_SHA}-linux - cp artifacts/windows-latest-build/SoftwareManager-dev-*-windows.exe release/SoftwareManager-dev-${SHORT_SHA}-windows.exe - cp artifacts/macos-latest-build/SoftwareManager-dev-*-macos release/SoftwareManager-dev-${SHORT_SHA}-macos - chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos + cp artifacts/ubuntu-latest-build/SoftwareManager-dev-linux release/SoftwareManager-dev-linux + cp artifacts/windows-latest-build/SoftwareManager-dev-windows.exe release/SoftwareManager-dev-windows.exe + cp artifacts/macos-latest-build/SoftwareManager-dev-macos release/SoftwareManager-dev-macos + chmod +x release/SoftwareManager-dev-linux release/SoftwareManager-dev-macos ls -la release/ - name: Create Prerelease (Public) From 55e4f86a39ce76607e288d61942ab01332bed751 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 15:59:50 +0200 Subject: [PATCH 16/45] update workflows --- .github/workflows/dev.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 6d057f1..f709e95 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -25,14 +25,14 @@ jobs: matrix: include: - os: ubuntu-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-linux - asset_name: SoftwareManager-dev-${{ github.sha }}-linux + artifact_name: SoftwareManager-dev-linux + asset_name: SoftwareManager-dev-linux - os: windows-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-windows.exe - asset_name: SoftwareManager-dev-${{ github.sha }}-windows.exe + artifact_name: SoftwareManager-dev-windows.exe + asset_name: SoftwareManager-dev-windows.exe - os: macos-latest - artifact_name: SoftwareManager-dev-${{ github.sha }}-macos - asset_name: SoftwareManager-dev-${{ github.sha }}-macos + artifact_name: SoftwareManager-dev-macos + asset_name: SoftwareManager-dev-macos runs-on: ${{ matrix.os }} @@ -127,7 +127,7 @@ jobs: chmod +x release/SoftwareManager-dev-linux release/SoftwareManager-dev-macos ls -la release/ - - name: Create Prerelease (Public) + - name: Create Release (Public) uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.version.outputs.version }} @@ -135,8 +135,8 @@ jobs: draft: false prerelease: false files: | - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-linux - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-windows.exe - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-macos + release/SoftwareManager-dev-linux + release/SoftwareManager-dev-windows.exe + release/SoftwareManager-dev-macos env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 7c505f14023e2bc1fe3b36cf9884533e291db132 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 16:45:09 +0200 Subject: [PATCH 17/45] this is a test --- test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..90bfcb5 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +this is a test From c8a1e721075f21ade7d78926f133e7e53b3e3068 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 22:09:22 +0200 Subject: [PATCH 18/45] remove test.txt --- test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test.txt diff --git a/test.txt b/test.txt deleted file mode 100644 index 90bfcb5..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -this is a test From 76b26664f2f34984fc28b121bb5690d6f1d1c736 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 22:36:40 +0200 Subject: [PATCH 19/45] add updater for windows (experimental) --- core/interface/gui.py | 42 +++++++++++++++++++++++++++++- core/utils/config/config.py | 6 ++--- core/utils/config/settings.py | 49 +++++++++++++++++++++++++++-------- core/utils/data/state.py | 3 ++- main.py | 4 +-- 5 files changed, 85 insertions(+), 19 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 6b4e5bd..32f6d3e 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -14,26 +14,67 @@ from PySide6.QtWidgets import ( QTabWidget, QProgressBar, QHeaderView, + QMessageBox, ) from PySide6.QtGui import QIcon, QAction, QCloseEvent import darkdetect import threading +import platform +import requests as r +import os +import subprocess +import time +import sys +from core.utils.config.settings import save_settings from core.utils.general.wrappers import run_thread from core.utils.data.state import state from core.utils.network.download import download_selected +from core.utils.network.update_checker import check_for_updates from core.utils.general.shutdown import closehelper from core.interface.utils.tabhelper import create_tab from core.interface.utils.searchhelper import return_pressed from core.interface.dialogs.settings import settings_dialog from core.network.aria2_integration import dlprogress +def download_update(latest_version): + filename = "SoftwareManager-dev-windows.exe" + url = "https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-windows.exe" + print("Downloading update...") + if os.path.exists(filename): + os.remove(filename) + response = r.get(url, allow_redirects=True) + with open(filename, "wb") as f: + f.write(response.content) + if not os.path.exists(filename): + raise FileNotFoundError(f"Executable not found") + subprocess.Popen([filename], shell=True) + time.sleep(0.5) + save_settings(version=latest_version) + sys.exit(0) class MainWindow(QtWidgets.QMainWindow, QWidget): def __init__(self): super().__init__() searchresults = [] + # Check for updates on Windows + if platform.system() == "Windows": + result = check_for_updates() + assets, latest_version = result + + if assets: + msg = QMessageBox() + msg.setIcon(QMessageBox.Information) + msg.setWindowTitle("Update Available") + msg.setText(f"A new version is available.") + msg.setInformativeText("Please visit the GitHub releases page to download the latest version.") + msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Ignore) + + response = msg.exec_() + if response == QMessageBox.Ok: + download_update(latest_version) + self.setWindowTitle("Software Manager") self.setGeometry(100, 100, 800, 600) @@ -142,7 +183,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.tracker_list.addItems(["rutracker", "uztracker", "m0nkrus"]) self.tracker_list.activated.connect(self.set_tracker) - if darkdetect.isDark(): settings_action = QAction(QIcon("core/interface/assets/settings_dark.png"), "Settings", self) else: diff --git a/core/utils/config/config.py b/core/utils/config/config.py index 1bc75f7..0016fe8 100644 --- a/core/utils/config/config.py +++ b/core/utils/config/config.py @@ -3,11 +3,11 @@ import platform import configparser from core.utils.data.state import state - def create_config(): config = configparser.ConfigParser() - config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}"} + config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}", + "version": f"{state.version}"} if platform.system() == "Windows": config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming")) @@ -42,4 +42,4 @@ def read_config(): state.aria2_threads = config.getint("General", "aria2_threads") state.download_path = config.get("General", "download_path") state.speed_limit = config.getint("General", "speed_limit") - + state.version = config.get("General", "version") diff --git a/core/utils/config/settings.py b/core/utils/config/settings.py index 2095581..b284f5e 100644 --- a/core/utils/config/settings.py +++ b/core/utils/config/settings.py @@ -5,18 +5,45 @@ def restart_aria2c(): import main # had to do this because of circle import :( import signal import atexit - main.kill_aria2server() - state.aria2process.wait() - state.aria2process = main.run_aria2server() - signal.signal(signal.SIGINT, main.keyboardinterrupthandler) - atexit.unregister(main.kill_aria2server) - atexit.register(main.kill_aria2server) + import time -def save_settings(thread_count, close, apiurl, download_path, speed_limit): - state.aria2_threads = thread_count - state.api_url = apiurl - state.download_path = download_path - state.speed_limit = speed_limit + try: + main.kill_aria2server() + except Exception as e: + print(f"Warning: aria2server kill failed: {e}") + + try: + if state.aria2process: + state.aria2process.wait(timeout=3) + except Exception as e: + print(f"Warning: aria2process wait failed: {e}") + + time.sleep(0.5) + + try: + state.aria2process = main.run_aria2server() + except Exception as e: + print(f"Error: failed to restart aria2server: {e}") + return + + try: + signal.signal(signal.SIGINT, main.keyboardinterrupthandler) + atexit.unregister(main.kill_aria2server) + atexit.register(main.kill_aria2server) + except Exception as e: + print(f"Warning: signal/atexit setup failed: {e}") + +def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None, version=None): + if thread_count is not None: + state.aria2_threads = thread_count + if apiurl is not None: + state.api_url = apiurl + if download_path is not None: + state.download_path = download_path + if speed_limit is not None: + state.speed_limit = speed_limit + if version is not None: + state.version = version create_config() restart_aria2c() diff --git a/core/utils/data/state.py b/core/utils/data/state.py index 633415e..17e7e3d 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -9,6 +9,7 @@ class AppState: post_urls: List[str] post_author: List[str] downloads: List[str] + version: str debug: bool = False tracker: str = "rutracker" api_url: str = "https://api.michijackson.xyz" @@ -19,4 +20,4 @@ class AppState: aria2p: Any = None aria2_threads: int = 4 -state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloads=[], download_path="") +state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloads=[], download_path="", version="") diff --git a/main.py b/main.py index eb75bb9..baab2d4 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ from core.interface.gui import MainWindow from core.utils.data.state import state from core.network.aria2_integration import aria2server from core.network.aria2_integration import send_notification -from core.utils.general.shutdown import kill_aria2server, closehelper, shutdown_event +from core.utils.general.shutdown import closehelper, shutdown_event from core.utils.general.wrappers import run_thread from core.utils.config.config import read_config from PySide6 import QtWidgets @@ -14,12 +14,10 @@ import argparse import sys - parser = argparse.ArgumentParser() parser.add_argument("--debug", action="store_true") args = parser.parse_args() - def run_gui(): app = QtWidgets.QApplication([]) if darkdetect.isDark: From a37a47b65bcec5e0c00e33a7d925417a25faff0a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 18 Oct 2025 22:40:25 +0200 Subject: [PATCH 20/45] add update checker file for windows --- core/utils/network/update_checker.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 core/utils/network/update_checker.py diff --git a/core/utils/network/update_checker.py b/core/utils/network/update_checker.py new file mode 100644 index 0000000..479f965 --- /dev/null +++ b/core/utils/network/update_checker.py @@ -0,0 +1,28 @@ +import requests +from core.utils.data.state import state + +def check_for_updates(): + url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases" + response = requests.get(url) + + if response.status_code != 200: + print(f"Failed to fetch releases: {response.status_code}") + exit(1) + + releases = response.json() + releases.sort(key=lambda r: r["published_at"], reverse=True) + + latest_release = releases[0] + latest_version = latest_release["name"] + assets = latest_release["assets"] + + if latest_version != state.version: + print(f"New release available: {latest_version}") + if assets: + print("Assets:") + for asset in assets: + print(f" - {asset['name']}: {asset['browser_download_url']}") + return assets, latest_version + else: + print("Already up-to-date.") + return None, None From 3960612f9481038db90a80c216bae1c47ec77976 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 23:47:54 +0200 Subject: [PATCH 21/45] fix settings icon not showing up --- core/interface/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 32f6d3e..fad8f0b 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -184,9 +184,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.tracker_list.activated.connect(self.set_tracker) if darkdetect.isDark(): - settings_action = QAction(QIcon("core/interface/assets/settings_dark.png"), "Settings", self) + settings_action = QAction(QIcon("core/interface/assets/settings_white.png"), "Settings", self) else: - settings_action = QAction(QIcon("core/interface/assets/settings.png"), "Settings", self) + settings_action = QAction(QIcon("core/interface/assets/settings_black.png"), "Settings", self) settings_action.triggered.connect(lambda: settings_dialog(self)) toolbar.addAction(settings_action) From d5d99d6a0e40c49a183a0ad639cfd04b9e042d8a Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sat, 18 Oct 2025 23:55:25 +0200 Subject: [PATCH 22/45] fix debug outputs for update checker --- core/utils/network/update_checker.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/utils/network/update_checker.py b/core/utils/network/update_checker.py index 479f965..9396fbb 100644 --- a/core/utils/network/update_checker.py +++ b/core/utils/network/update_checker.py @@ -17,12 +17,15 @@ def check_for_updates(): assets = latest_release["assets"] if latest_version != state.version: - print(f"New release available: {latest_version}") + if state.debug: + print(f"New release available: {latest_version}") if assets: - print("Assets:") - for asset in assets: - print(f" - {asset['name']}: {asset['browser_download_url']}") + if state.debug: + print("Assets:") + for asset in assets: + print(f" - {asset['name']}: {asset['browser_download_url']}") return assets, latest_version else: - print("Already up-to-date.") + if state.debug: + print("Already up-to-date.") return None, None From 3290ff3fac8872f79df4ae29b1495afc5a9b12a3 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 02:55:32 +0200 Subject: [PATCH 23/45] switch to spreadsheet view in softwareList QTableWidget --- core/interface/gui.py | 32 +++++++++++++--------- core/interface/utils/searchhelper.py | 40 +++++++++++++++++----------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index fad8f0b..e2a010c 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -15,6 +15,8 @@ from PySide6.QtWidgets import ( QProgressBar, QHeaderView, QMessageBox, + QTableWidget, + QTableWidgetItem, ) from PySide6.QtGui import QIcon, QAction, QCloseEvent import darkdetect @@ -56,7 +58,6 @@ def download_update(latest_version): class MainWindow(QtWidgets.QMainWindow, QWidget): def __init__(self): super().__init__() - searchresults = [] # Check for updates on Windows if platform.system() == "Windows": @@ -89,8 +90,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter self.dlbutton = QtWidgets.QPushButton("Download") - self.softwareList = QListWidget() - self.post_author_list = QListWidget() self.libraryList = QListWidget() self.emptyResults = QLabel("No Results") @@ -103,14 +102,22 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.emptyLibrary = QLabel("No items in library.") self.emptyDownload = QLabel("No items in downloads.") self.progressbar = QProgressBar() + self.qtablewidget = QTableWidget() + + self.qtablewidget.setColumnCount(2) + self.qtablewidget.setRowCount(100) + self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.qtablewidget.verticalHeader().setVisible(False) + self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + self.qtablewidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) + header = self.qtablewidget.horizontalHeader() + header.setSectionResizeMode(1, QHeaderView.Fixed) + header.resizeSection(1, 500) container = QWidget() containerLayout = QVBoxLayout() containerLayout.addWidget(self.searchbar) - - containerLayout.addWidget(self.softwareList) - containerLayout.addWidget(self.post_author_list) - self.softwareList.addItems(searchresults) + containerLayout.addWidget(self.qtablewidget) class DownloadModel(QAbstractTableModel): def __init__(self): @@ -154,7 +161,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.downloadList.setSelectionBehavior(QTableView.SelectRows) # download button triggers - self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.softwareList.currentItem(), state.posts, state.post_titles)))) + self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.qtablewidget.currentItem(), state.posts, state.post_titles)))) container.setLayout(containerLayout) self.setCentralWidget(container) @@ -163,11 +170,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.tabs = QTabWidget() self.horizontal_layout = QHBoxLayout() - self.horizontal_layout.addWidget(self.softwareList, stretch=3) self.horizontal_layout.addWidget(self.emptyResults, stretch=3) - self.horizontal_layout.addWidget(self.post_author_list) + self.horizontal_layout.addWidget(self.qtablewidget) - self.tab1 = create_tab("Search", self.searchbar, self.softwareList, self.tabs, self.dlbutton, self.horizontal_layout) + self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout) self.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None) self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None) @@ -223,8 +229,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): def show_empty_results(self, show: bool): if show: - self.softwareList.hide() + self.qtablewidget.hide() self.emptyResults.show() else: self.emptyResults.hide() - self.softwareList.show() + self.qtablewidget.show() diff --git a/core/interface/utils/searchhelper.py b/core/interface/utils/searchhelper.py index cf6f22a..c7aa082 100644 --- a/core/interface/utils/searchhelper.py +++ b/core/interface/utils/searchhelper.py @@ -1,3 +1,4 @@ +from PySide6.QtWidgets import QTableWidgetItem 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 @@ -17,20 +18,23 @@ def return_pressed(self): response = scrape_uztracker(search_text) if response: state.post_titles, state.post_urls = response - self.softwareList.clear() + self.qtablewidget.clear() if state.post_titles and len(state.post_titles) > 0: - self.softwareList.addItems(state.post_titles) + self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + for i, author in enumerate(state.post_author): + self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) + for i, title in enumerate(state.post_titles): + self.qtablewidget.setItem(i, 0, QTableWidgetItem(title)) 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.qtablewidget.clear() self.show_empty_results(True) else: if state.debug: print(f"No response from uztracker") - self.softwareList.clear() + self.qtablewidget.clear() self.show_empty_results(True) elif state.tracker == "rutracker": @@ -40,21 +44,23 @@ def return_pressed(self): if state.posts == []: if state.debug: print(f"No Results for {search_text}") - self.softwareList.clear() + self.qtablewidget.clear() self.show_empty_results(True) else: state.post_titles, _, state.post_author = format_data(state.posts) self.show_empty_results(False) - self.post_author_list.clear() - self.post_author_list.addItems(state.post_author) - self.softwareList.clear() - self.softwareList.addItems(state.post_titles) + self.qtablewidget.clear() + self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + for i, author in enumerate(state.post_author): + self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) + for i, title in enumerate(state.post_titles): + self.qtablewidget.setItem(i, 0, QTableWidgetItem(title)) if state.debug == True: print(f"Response Cached: {cached}") else: if state.debug: print(f"No response from rutracker") - self.softwareList.clear() + self.qtablewidget.clear() self.show_empty_results(True) elif state.tracker == "m0nkrus": @@ -63,12 +69,14 @@ def return_pressed(self): if state.posts == []: if state.debug: print(f"No Results for {search_text}") - self.softwareList.clear() + self.qtablewidget.clear() self.show_empty_results(True) else: state.post_titles, _, state.post_author = format_data(state.posts) self.show_empty_results(False) - self.post_author_list.clear() - self.post_author_list.addItems(state.post_author) - self.softwareList.clear() - self.softwareList.addItems(state.post_titles) + self.qtablewidget.clear() + self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + for i, author in enumerate(state.post_author): + self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) + for i, title in enumerate(state.post_titles): + self.qtablewidget.setItem(i, 0, QTableWidgetItem(title)) From 10e80ab4b6207a600803a97181200a163496ab9f Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 02:58:02 +0200 Subject: [PATCH 24/45] fix row count --- core/interface/gui.py | 1 - core/interface/utils/searchhelper.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index e2a010c..0c30af4 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -105,7 +105,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.qtablewidget = QTableWidget() self.qtablewidget.setColumnCount(2) - self.qtablewidget.setRowCount(100) self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.qtablewidget.verticalHeader().setVisible(False) self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) diff --git a/core/interface/utils/searchhelper.py b/core/interface/utils/searchhelper.py index c7aa082..ac3d2bc 100644 --- a/core/interface/utils/searchhelper.py +++ b/core/interface/utils/searchhelper.py @@ -21,6 +21,7 @@ def return_pressed(self): self.qtablewidget.clear() if state.post_titles and len(state.post_titles) > 0: self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + self.qtablewidget.setRowCount(len(state.post_titles)) for i, author in enumerate(state.post_author): self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) for i, title in enumerate(state.post_titles): @@ -51,6 +52,7 @@ def return_pressed(self): self.show_empty_results(False) self.qtablewidget.clear() self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + self.qtablewidget.setRowCount(len(state.post_titles)) for i, author in enumerate(state.post_author): self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) for i, title in enumerate(state.post_titles): @@ -76,6 +78,7 @@ def return_pressed(self): self.show_empty_results(False) self.qtablewidget.clear() self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) + self.qtablewidget.setRowCount(len(state.post_titles)) for i, author in enumerate(state.post_author): self.qtablewidget.setItem(i, 1, QTableWidgetItem(author)) for i, title in enumerate(state.post_titles): From dcc78ade4447f461cfbcd36a9c0421e558af7757 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sun, 19 Oct 2025 03:13:08 +0200 Subject: [PATCH 25/45] fixed qtablewidget colors --- core/interface/gui.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 0c30af4..44ed640 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -16,9 +16,10 @@ from PySide6.QtWidgets import ( QHeaderView, QMessageBox, QTableWidget, - QTableWidgetItem, + QTableWidgetItem ) -from PySide6.QtGui import QIcon, QAction, QCloseEvent + +from PySide6.QtGui import QIcon, QAction, QCloseEvent, QPalette import darkdetect import threading import platform @@ -109,6 +110,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.qtablewidget.verticalHeader().setVisible(False) self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) self.qtablewidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) + self.qtablewidget.setAttribute(Qt.WA_TranslucentBackground) + self.qtablewidget.viewport().setAttribute(Qt.WA_TranslucentBackground) + header = self.qtablewidget.horizontalHeader() header.setSectionResizeMode(1, QHeaderView.Fixed) header.resizeSection(1, 500) @@ -158,6 +162,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.downloadList.horizontalHeader().setStretchLastSection(False) self.downloadList.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.downloadList.setSelectionBehavior(QTableView.SelectRows) + self.downloadList.setAttribute(Qt.WA_TranslucentBackground) + self.downloadList.viewport().setAttribute(Qt.WA_TranslucentBackground) # download button triggers self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.qtablewidget.currentItem(), state.posts, state.post_titles)))) @@ -175,6 +181,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout) self.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None) self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None) + + containerLayout.addWidget(self.tabs) From a11c1d712912a7dc6adb1ac063f18819bed98d80 Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sun, 19 Oct 2025 03:23:37 +0200 Subject: [PATCH 26/45] added dev release for version check --- core/utils/network/update_checker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/utils/network/update_checker.py b/core/utils/network/update_checker.py index 9396fbb..24ac0b2 100644 --- a/core/utils/network/update_checker.py +++ b/core/utils/network/update_checker.py @@ -16,7 +16,12 @@ def check_for_updates(): latest_version = latest_release["name"] assets = latest_release["assets"] - if latest_version != state.version: + + if state.version == "dev": + if state.debug: + print("Dev release detected, skipping version check") + return None, None + elif latest_version != state.version: if state.debug: print(f"New release available: {latest_version}") if assets: From deb5ce5b88e747c6a0c0194fe7a8d8f8d33c523d Mon Sep 17 00:00:00 2001 From: Vxrtrauter Date: Sun, 19 Oct 2025 03:53:52 +0200 Subject: [PATCH 27/45] fixed aria2 killing issues on settings close --- core/utils/config/settings.py | 50 +++++++++-------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/core/utils/config/settings.py b/core/utils/config/settings.py index b284f5e..6d122a5 100644 --- a/core/utils/config/settings.py +++ b/core/utils/config/settings.py @@ -1,49 +1,23 @@ from core.utils.data.state import state +from core.utils.general.shutdown import kill_aria2server from .config import create_config def restart_aria2c(): import main # had to do this because of circle import :( import signal import atexit - import time + kill_aria2server() + state.aria2process.wait() + state.aria2process = main.run_aria2server() + signal.signal(signal.SIGINT, main.keyboardinterrupthandler) + atexit.unregister(kill_aria2server) + atexit.register(kill_aria2server) - try: - main.kill_aria2server() - except Exception as e: - print(f"Warning: aria2server kill failed: {e}") - - try: - if state.aria2process: - state.aria2process.wait(timeout=3) - except Exception as e: - print(f"Warning: aria2process wait failed: {e}") - - time.sleep(0.5) - - try: - state.aria2process = main.run_aria2server() - except Exception as e: - print(f"Error: failed to restart aria2server: {e}") - return - - try: - signal.signal(signal.SIGINT, main.keyboardinterrupthandler) - atexit.unregister(main.kill_aria2server) - atexit.register(main.kill_aria2server) - except Exception as e: - print(f"Warning: signal/atexit setup failed: {e}") - -def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None, version=None): - if thread_count is not None: - state.aria2_threads = thread_count - if apiurl is not None: - state.api_url = apiurl - if download_path is not None: - state.download_path = download_path - if speed_limit is not None: - state.speed_limit = speed_limit - if version is not None: - state.version = version +def save_settings(thread_count, close, apiurl, download_path, speed_limit): + state.aria2_threads = thread_count + state.api_url = apiurl + state.download_path = download_path + state.speed_limit = speed_limit create_config() restart_aria2c() From 813d2b31c4375178145c01a4dba377a614f832b3 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 04:05:57 +0200 Subject: [PATCH 28/45] add setting to ignore updates on windows --- core/interface/dialogs/settings.py | 21 ++++++++++++++++----- core/interface/gui.py | 27 ++++++++++++++------------- core/utils/config/config.py | 3 ++- core/utils/data/state.py | 1 + 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/core/interface/dialogs/settings.py b/core/interface/dialogs/settings.py index cba35fc..038af6d 100644 --- a/core/interface/dialogs/settings.py +++ b/core/interface/dialogs/settings.py @@ -10,12 +10,10 @@ from PySide6.QtWidgets import ( QLabel, QHBoxLayout, QSpinBox, + QCheckBox, ) - - - def settings_dialog(self): if state.debug: @@ -29,7 +27,20 @@ def settings_dialog(self): def close_settings(): dialog.reject() - + + checkbox_container = QWidget() + checkbox_layout = QHBoxLayout() + + # ignore updates checkbox + checkbox = QCheckBox() + checkbox_container.setLayout(checkbox_layout) + checkbox_layout.addWidget(QLabel("Ignore Updates: ")) + checkbox_layout.addStretch() + checkbox.setChecked(state.ignore_updates) + checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked)) + checkbox_layout.addWidget(checkbox) + dialog.layout().addWidget(checkbox_container) + ################## # THREAD SETTING # ################## @@ -119,4 +130,4 @@ def settings_dialog(self): dialog.layout().addLayout(layout) - dialog.exec() \ No newline at end of file + dialog.exec() diff --git a/core/interface/gui.py b/core/interface/gui.py index 44ed640..d206c3d 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -61,21 +61,22 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): super().__init__() # Check for updates on Windows - if platform.system() == "Windows": - result = check_for_updates() - assets, latest_version = result + if state.ignore_updates is False: + if platform.system() == "Windows": + result = check_for_updates() + assets, latest_version = result - if assets: - msg = QMessageBox() - msg.setIcon(QMessageBox.Information) - msg.setWindowTitle("Update Available") - msg.setText(f"A new version is available.") - msg.setInformativeText("Please visit the GitHub releases page to download the latest version.") - msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Ignore) + if assets: + msg = QMessageBox() + msg.setIcon(QMessageBox.Information) + msg.setWindowTitle("Update Available") + msg.setText(f"A new version is available.") + msg.setInformativeText("Please visit the GitHub releases page to download the latest version.") + msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Ignore) - response = msg.exec_() - if response == QMessageBox.Ok: - download_update(latest_version) + response = msg.exec_() + if response == QMessageBox.Ok: + download_update(latest_version) self.setWindowTitle("Software Manager") self.setGeometry(100, 100, 800, 600) diff --git a/core/utils/config/config.py b/core/utils/config/config.py index 0016fe8..c816d3e 100644 --- a/core/utils/config/config.py +++ b/core/utils/config/config.py @@ -7,7 +7,7 @@ def create_config(): config = configparser.ConfigParser() config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}", - "version": f"{state.version}"} + "version": f"{state.version}", "ignore_updates": f"{state.ignore_updates}"} if platform.system() == "Windows": config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming")) @@ -43,3 +43,4 @@ def read_config(): state.download_path = config.get("General", "download_path") state.speed_limit = config.getint("General", "speed_limit") state.version = config.get("General", "version") + state.ignore_updates = config.getboolean("General", "ignore_updates") diff --git a/core/utils/data/state.py b/core/utils/data/state.py index 17e7e3d..19159fe 100644 --- a/core/utils/data/state.py +++ b/core/utils/data/state.py @@ -10,6 +10,7 @@ class AppState: post_author: List[str] downloads: List[str] version: str + ignore_updates: bool = False debug: bool = False tracker: str = "rutracker" api_url: str = "https://api.michijackson.xyz" From 95a687ddd925b3dcaae8b70b136b90b193b33f9e Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 04:07:41 +0200 Subject: [PATCH 29/45] fix updater --- core/utils/config/settings.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/core/utils/config/settings.py b/core/utils/config/settings.py index 6d122a5..6f6f1e7 100644 --- a/core/utils/config/settings.py +++ b/core/utils/config/settings.py @@ -13,11 +13,17 @@ def restart_aria2c(): atexit.unregister(kill_aria2server) atexit.register(kill_aria2server) -def save_settings(thread_count, close, apiurl, download_path, speed_limit): - state.aria2_threads = thread_count - state.api_url = apiurl - state.download_path = download_path - state.speed_limit = speed_limit +def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None, version=None): + if thread_count is not None: + state.aria2_threads = thread_count + if apiurl is not None: + state.api_url = apiurl + if download_path is not None: + state.download_path = download_path + if speed_limit is not None: + state.speed_limit = speed_limit + if version is not None: + state.version = version create_config() restart_aria2c() From 87c974f1248a8f8b63de388c7cdc44a4c01190ce Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 15:58:16 +0200 Subject: [PATCH 30/45] add build_info.json to workflow for win updater --- .github/workflows/dev.yml | 6 +++++- core/interface/gui.py | 7 ++++++- core/utils/config/config.py | 5 ++--- core/utils/config/settings.py | 4 +--- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f709e95..e2c3779 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -66,8 +66,12 @@ jobs: pip install -r requirements.txt pip install pyinstaller + - name: Inject commit hash into code + run: | + echo "version = '${GITHUB_SHA}'" > build_info.json + - name: Build executable with PyInstaller - run: pyinstaller --onefile --windowed --name ${{ env.APP_NAME }} main.py + run: pyinstaller --onefile --windowed --add-data "build_info.json:." --name ${{ env.APP_NAME }} main.py - name: Make executable (Unix) if: runner.os != 'Windows' diff --git a/core/interface/gui.py b/core/interface/gui.py index d206c3d..5fae629 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -28,6 +28,7 @@ import os import subprocess import time import sys +import json from core.utils.config.settings import save_settings from core.utils.general.wrappers import run_thread from core.utils.data.state import state @@ -53,13 +54,17 @@ def download_update(latest_version): raise FileNotFoundError(f"Executable not found") subprocess.Popen([filename], shell=True) time.sleep(0.5) - save_settings(version=latest_version) sys.exit(0) class MainWindow(QtWidgets.QMainWindow, QWidget): def __init__(self): super().__init__() + build_info_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "build_info.json") + with open(build_info_path, "r") as f: + build_info = json.load(f) + state.version = build_info.get("version", "dev") + # Check for updates on Windows if state.ignore_updates is False: if platform.system() == "Windows": diff --git a/core/utils/config/config.py b/core/utils/config/config.py index c816d3e..7da80cf 100644 --- a/core/utils/config/config.py +++ b/core/utils/config/config.py @@ -6,8 +6,8 @@ from core.utils.data.state import state def create_config(): config = configparser.ConfigParser() - config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}", - "version": f"{state.version}", "ignore_updates": f"{state.ignore_updates}"} + config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}", + "ignore_updates": f"{state.ignore_updates}"} if platform.system() == "Windows": config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming")) @@ -42,5 +42,4 @@ def read_config(): state.aria2_threads = config.getint("General", "aria2_threads") state.download_path = config.get("General", "download_path") state.speed_limit = config.getint("General", "speed_limit") - state.version = config.get("General", "version") state.ignore_updates = config.getboolean("General", "ignore_updates") diff --git a/core/utils/config/settings.py b/core/utils/config/settings.py index 6f6f1e7..25bb68a 100644 --- a/core/utils/config/settings.py +++ b/core/utils/config/settings.py @@ -13,7 +13,7 @@ def restart_aria2c(): atexit.unregister(kill_aria2server) atexit.register(kill_aria2server) -def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None, version=None): +def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None): if thread_count is not None: state.aria2_threads = thread_count if apiurl is not None: @@ -22,8 +22,6 @@ def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_p state.download_path = download_path if speed_limit is not None: state.speed_limit = speed_limit - if version is not None: - state.version = version create_config() restart_aria2c() From 05a5b045079144d2cd226080f217d93920510c02 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 16:22:49 +0200 Subject: [PATCH 31/45] fix error if build_info doesnt exist in source code --- core/interface/gui.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 5fae629..be62e73 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -40,7 +40,7 @@ from core.interface.utils.searchhelper import return_pressed from core.interface.dialogs.settings import settings_dialog from core.network.aria2_integration import dlprogress -def download_update(latest_version): +def download_update(): filename = "SoftwareManager-dev-windows.exe" url = "https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-windows.exe" @@ -61,9 +61,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): super().__init__() build_info_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "build_info.json") - with open(build_info_path, "r") as f: - build_info = json.load(f) - state.version = build_info.get("version", "dev") + if 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", "dev") # Check for updates on Windows if state.ignore_updates is False: From 2eb384f7cfd1fd6f7a472c86d190d06d2f336312 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 17:51:21 +0200 Subject: [PATCH 32/45] fix build_info.json --- .github/workflows/dev.yml | 4 +++- core/interface/gui.py | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index e2c3779..1f60a3a 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -68,7 +68,9 @@ jobs: - name: Inject commit hash into code run: | - echo "version = '${GITHUB_SHA}'" > build_info.json + echo '{' > build_info.json + echo " \"version\": \"${GITHUB_SHA}\"" >> build_info.json + echo '}' >> build_info.json - name: Build executable with PyInstaller run: pyinstaller --onefile --windowed --add-data "build_info.json:." --name ${{ env.APP_NAME }} main.py diff --git a/core/interface/gui.py b/core/interface/gui.py index be62e73..70ae479 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -16,10 +16,9 @@ from PySide6.QtWidgets import ( QHeaderView, QMessageBox, QTableWidget, - QTableWidgetItem ) -from PySide6.QtGui import QIcon, QAction, QCloseEvent, QPalette +from PySide6.QtGui import QIcon, QAction, QCloseEvent import darkdetect import threading import platform @@ -29,7 +28,6 @@ import subprocess import time import sys import json -from core.utils.config.settings import save_settings from core.utils.general.wrappers import run_thread from core.utils.data.state import state from core.utils.network.download import download_selected @@ -64,7 +62,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): if 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", "dev") + state.version = build_info.get("version") # Check for updates on Windows if state.ignore_updates is False: From a2cff15d0d41cf6adea5581f302ca0bc80ba1ff4 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 19:39:33 +0200 Subject: [PATCH 33/45] fix workflows (hopefully) --- .github/workflows/dev.yml | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 1f60a3a..0a155f1 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -66,11 +66,26 @@ jobs: pip install -r requirements.txt pip install pyinstaller - - name: Inject commit hash into code + - name: Generate version + id: version run: | - echo '{' > build_info.json - echo " \"version\": \"${GITHUB_SHA}\"" >> build_info.json - echo '}' >> build_info.json + COMMIT_SHA=${{ github.sha }} + SHORT_SHA=${COMMIT_SHA:0:7} + VERSION="${SHORT_SHA}-dev" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + echo "Generated version: ${VERSION}" + + - name: Inject commit hash into code (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $json = @" + { + "version": "$env:VERSION" + } + "@ + $json | Out-File -Encoding utf8 build_info.json - name: Build executable with PyInstaller run: pyinstaller --onefile --windowed --add-data "build_info.json:." --name ${{ env.APP_NAME }} main.py @@ -114,16 +129,6 @@ jobs: find artifacts -type f ls -la artifacts/ - - name: Generate version - id: version - run: | - COMMIT_SHA=${{ github.sha }} - SHORT_SHA=${COMMIT_SHA:0:7} - VERSION="${SHORT_SHA}-dev" - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - echo "Generated version: ${VERSION}" - - name: Prepare release files run: | mkdir -p release From a7132ed1cdbaf5c1de93ed058959cdad918f9a3a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sun, 19 Oct 2025 19:45:19 +0200 Subject: [PATCH 34/45] fix workflows (again) --- .github/workflows/dev.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 0a155f1..4daee5b 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -81,10 +81,10 @@ jobs: shell: pwsh run: | $json = @" - { - "version": "$env:VERSION" - } - "@ + { + "version": "$env:VERSION" + } + "@ $json | Out-File -Encoding utf8 build_info.json - name: Build executable with PyInstaller From c93f6345c88b711e58df29cfda03f15cf7b68f6a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 16:36:10 +0200 Subject: [PATCH 35/45] fix workflows (again) --- .github/workflows/dev.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 4daee5b..6e16752 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -87,9 +87,14 @@ jobs: "@ $json | Out-File -Encoding utf8 build_info.json - - name: Build executable with PyInstaller + - name: Build executable with PyInstaller (Windows) + if: runner.os == 'Windows' run: pyinstaller --onefile --windowed --add-data "build_info.json:." --name ${{ env.APP_NAME }} main.py + - name: Build executable with PyInstaller (Unix) + if: runner.os != 'Windows' + run: pyinstaller --onefile --windowed --name ${{ env.APP_NAME }} main.py + - name: Make executable (Unix) if: runner.os != 'Windows' run: chmod +x dist/${{ env.APP_NAME }} From 80750f010f0a24e0728ac81f03812783bec683eb Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:00:14 +0200 Subject: [PATCH 36/45] fix workflows (again) --- .github/workflows/dev.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 6e16752..7a70adf 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -68,13 +68,15 @@ jobs: - name: Generate version id: version + shell: bash run: | COMMIT_SHA=${{ github.sha }} SHORT_SHA=${COMMIT_SHA:0:7} VERSION="${SHORT_SHA}-dev" - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - echo "Generated version: ${VERSION}" + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT + echo "Generated version: $VERSION" - name: Inject commit hash into code (Windows) if: runner.os == 'Windows' From 7d0c8d16ece79ebe14476d2c65d6527b4de420f1 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:21:47 +0200 Subject: [PATCH 37/45] fix workflows (again) --- .github/workflows/dev.yml | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 7a70adf..9fac84a 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -35,6 +35,8 @@ jobs: asset_name: SoftwareManager-dev-macos runs-on: ${{ matrix.os }} + outputs: + version: ${{ steps.version.outputs.version }} steps: - name: Checkout code @@ -83,10 +85,10 @@ jobs: shell: pwsh run: | $json = @" - { - "version": "$env:VERSION" - } - "@ +{ + "version": "$env:VERSION" +} +"@ $json | Out-File -Encoding utf8 build_info.json - name: Build executable with PyInstaller (Windows) @@ -122,36 +124,25 @@ jobs: if: success() steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 with: path: artifacts - - name: Display downloaded artifacts - run: | - echo "Downloaded artifacts:" - find artifacts -type f - ls -la artifacts/ - - name: Prepare release files run: | mkdir -p release - cp artifacts/ubuntu-latest-build/SoftwareManager-dev-linux release/SoftwareManager-dev-linux - cp artifacts/windows-latest-build/SoftwareManager-dev-windows.exe release/SoftwareManager-dev-windows.exe - cp artifacts/macos-latest-build/SoftwareManager-dev-macos release/SoftwareManager-dev-macos + cp artifacts/ubuntu-latest-build/SoftwareManager-dev-linux release/ + cp artifacts/windows-latest-build/SoftwareManager-dev-windows.exe release/ + cp artifacts/macos-latest-build/SoftwareManager-dev-macos release/ chmod +x release/SoftwareManager-dev-linux release/SoftwareManager-dev-macos ls -la release/ - name: Create Release (Public) uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.version.outputs.version }} - name: ${{ steps.version.outputs.version }} - draft: false - prerelease: false + tag_name: ${{ needs.build.outputs.version }} + name: ${{ needs.build.outputs.version }} files: | release/SoftwareManager-dev-linux release/SoftwareManager-dev-windows.exe From dd6d16d833c7b9821db6a7fd54891adb1811c81f Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:24:04 +0200 Subject: [PATCH 38/45] fix workflows (again) --- .github/workflows/dev.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 9fac84a..e6eaf64 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -84,11 +84,9 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | - $json = @" -{ - "version": "$env:VERSION" -} -"@ + $json = '{ + "version": "' + $env:VERSION + '" + }' $json | Out-File -Encoding utf8 build_info.json - name: Build executable with PyInstaller (Windows) From de2e49e60bb225f593b9d2ed8218f2da738ba9b0 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:40:39 +0200 Subject: [PATCH 39/45] add back commit sha to release file name --- .github/workflows/dev.yml | 19 ++++++++++--------- core/interface/gui.py | 17 +++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index e6eaf64..1237063 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -25,14 +25,14 @@ jobs: matrix: include: - os: ubuntu-latest - artifact_name: SoftwareManager-dev-linux - asset_name: SoftwareManager-dev-linux + artifact_name: SoftwareManager-dev-${{ github.sha }}-linux + asset_name: SoftwareManager-dev-${{ github.sha }}-linux - os: windows-latest - artifact_name: SoftwareManager-dev-windows.exe - asset_name: SoftwareManager-dev-windows.exe + artifact_name: SoftwareManager-dev-${{ github.sha }}-windows.exe + asset_name: SoftwareManager-dev-${{ github.sha }}-windows.exe - os: macos-latest - artifact_name: SoftwareManager-dev-macos - asset_name: SoftwareManager-dev-macos + artifact_name: SoftwareManager-dev-${{ github.sha }}-macos + asset_name: SoftwareManager-dev-${{ github.sha }}-macos runs-on: ${{ matrix.os }} outputs: @@ -141,9 +141,10 @@ jobs: with: tag_name: ${{ needs.build.outputs.version }} name: ${{ needs.build.outputs.version }} + prerelease: true files: | - release/SoftwareManager-dev-linux - release/SoftwareManager-dev-windows.exe - release/SoftwareManager-dev-macos + release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-linux + release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-windows.exe + release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-macos env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/core/interface/gui.py b/core/interface/gui.py index 70ae479..4f5c87e 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -38,19 +38,20 @@ from core.interface.utils.searchhelper import return_pressed from core.interface.dialogs.settings import settings_dialog from core.network.aria2_integration import dlprogress -def download_update(): - filename = "SoftwareManager-dev-windows.exe" - url = "https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-windows.exe" +def download_update(latest_version): + old_filename = f"SoftwareManager-dev-{state.version}-windows.exe" + new_filename = f"SoftwareManager-dev-{latest_version}-windows.exe" + url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version}-windows.exe" print("Downloading update...") - if os.path.exists(filename): - os.remove(filename) + if os.path.exists(old_filename): + os.remove(old_filename) response = r.get(url, allow_redirects=True) - with open(filename, "wb") as f: + with open(new_filename, "wb") as f: f.write(response.content) - if not os.path.exists(filename): + if not os.path.exists(new_filename): raise FileNotFoundError(f"Executable not found") - subprocess.Popen([filename], shell=True) + subprocess.Popen([new_filename], shell=True) time.sleep(0.5) sys.exit(0) From 41d28095c2e087218a2715d7a54662f2dd06cd7a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:53:35 +0200 Subject: [PATCH 40/45] fix workflows --- .github/workflows/dev.yml | 9 +++++---- core/interface/gui.py | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 1237063..17e10e8 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -130,10 +130,11 @@ jobs: - name: Prepare release files run: | mkdir -p release - cp artifacts/ubuntu-latest-build/SoftwareManager-dev-linux release/ - cp artifacts/windows-latest-build/SoftwareManager-dev-windows.exe release/ - cp artifacts/macos-latest-build/SoftwareManager-dev-macos release/ - chmod +x release/SoftwareManager-dev-linux release/SoftwareManager-dev-macos + SHORT_SHA="${{ steps.version.outputs.short_sha }}" + cp artifacts/ubuntu-latest-build/SoftwareManager-dev-*-linux release/SoftwareManager-dev-${SHORT_SHA}-linux + cp artifacts/windows-latest-build/SoftwareManager-dev-*-windows.exe release/SoftwareManager-dev-${SHORT_SHA}-windows.exe + cp artifacts/macos-latest-build/SoftwareManager-dev-*-macos release/SoftwareManager-dev-${SHORT_SHA}-macos + chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos ls -la release/ - name: Create Release (Public) diff --git a/core/interface/gui.py b/core/interface/gui.py index 4f5c87e..65c7a73 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -38,6 +38,8 @@ from core.interface.utils.searchhelper import return_pressed from core.interface.dialogs.settings import settings_dialog from core.network.aria2_integration import dlprogress +print(os.path.abspath(__file__)) + def download_update(latest_version): old_filename = f"SoftwareManager-dev-{state.version}-windows.exe" new_filename = f"SoftwareManager-dev-{latest_version}-windows.exe" From 83fa23df58e3bb3857c2dfba4d43c8b7a84a64a2 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 17:58:40 +0200 Subject: [PATCH 41/45] fix release file names --- .github/workflows/dev.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 17e10e8..f267b4c 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -37,6 +37,7 @@ jobs: runs-on: ${{ matrix.os }} outputs: version: ${{ steps.version.outputs.version }} + short_sha: ${{ steps.version.outputs.short_sha }} steps: - name: Checkout code @@ -130,7 +131,7 @@ jobs: - name: Prepare release files run: | mkdir -p release - SHORT_SHA="${{ steps.version.outputs.short_sha }}" + SHORT_SHA="${{ needs.build.outputs.short_sha }}" cp artifacts/ubuntu-latest-build/SoftwareManager-dev-*-linux release/SoftwareManager-dev-${SHORT_SHA}-linux cp artifacts/windows-latest-build/SoftwareManager-dev-*-windows.exe release/SoftwareManager-dev-${SHORT_SHA}-windows.exe cp artifacts/macos-latest-build/SoftwareManager-dev-*-macos release/SoftwareManager-dev-${SHORT_SHA}-macos @@ -144,8 +145,8 @@ jobs: name: ${{ needs.build.outputs.version }} prerelease: true files: | - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-linux - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-windows.exe - release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-macos + release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-linux + release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-windows.exe + release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-macos env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 91482760b6d7c5638a9540a08eb8a8941567e66f Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 18:14:09 +0200 Subject: [PATCH 42/45] fix updater filename --- core/interface/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 65c7a73..e8a6311 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -41,8 +41,8 @@ from core.network.aria2_integration import dlprogress print(os.path.abspath(__file__)) def download_update(latest_version): - old_filename = f"SoftwareManager-dev-{state.version}-windows.exe" - new_filename = f"SoftwareManager-dev-{latest_version}-windows.exe" + old_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" + new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version}-windows.exe" print("Downloading update...") From 2bab6c33210dd208cdc3041d583823dfe37df289 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 18:22:16 +0200 Subject: [PATCH 43/45] fix old filename for updater --- core/interface/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index e8a6311..19cab46 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -41,7 +41,7 @@ from core.network.aria2_integration import dlprogress print(os.path.abspath(__file__)) def download_update(latest_version): - old_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" + old_filename = f"SoftwareManager-dev-{state.version.replace('-dev', '')}-windows.exe" new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version}-windows.exe" From 4c69a17f0ea905f61b8a19561a8c6bfb36624e3a Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 18:44:42 +0200 Subject: [PATCH 44/45] change back to releases for github workflow --- .github/workflows/dev.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f267b4c..e0cda20 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -143,7 +143,6 @@ jobs: with: tag_name: ${{ needs.build.outputs.version }} name: ${{ needs.build.outputs.version }} - prerelease: true files: | release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-linux release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-windows.exe From 2354adb34975ac9e1244e7f59fac209694508c87 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Mon, 20 Oct 2025 19:10:49 +0200 Subject: [PATCH 45/45] update updater download url --- core/interface/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/interface/gui.py b/core/interface/gui.py index 19cab46..8822028 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -43,7 +43,7 @@ print(os.path.abspath(__file__)) def download_update(latest_version): old_filename = f"SoftwareManager-dev-{state.version.replace('-dev', '')}-windows.exe" new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" - url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version}-windows.exe" + url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe" print("Downloading update...") if os.path.exists(old_filename):