diff --git a/core/interface/gui.py b/core/interface/gui.py index cee4ddf..2c1c5c6 100644 --- a/core/interface/gui.py +++ b/core/interface/gui.py @@ -230,8 +230,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_black.png"), "Settings", self) settings_action = QAction(QIcon("core/interface/assets/settings_white.png"), "Settings", self) + else: + settings_action = QAction(QIcon("core/interface/assets/settings_black.png"), "Settings", self) settings_action.triggered.connect(lambda: settings_dialog(self)) toolbar.addAction(settings_action) diff --git a/core/network/aria2_integration.py b/core/network/aria2_integration.py index 0a85d6a..e76fd02 100644 --- a/core/network/aria2_integration.py +++ b/core/network/aria2_integration.py @@ -1,6 +1,7 @@ import aria2p import subprocess from core.utils.data.state import state +from core.utils.general.logs import update_download_completed_by_hash from plyer import notification import time import sys @@ -72,4 +73,24 @@ def send_notification(shutdown_event): state.downloads = [d for d in state.aria2.get_downloads() if d.is_active and not d.is_metadata] except Exception: pass + time.sleep(5) + +def update_log(shutdown_event): + updated = set() + while not shutdown_event.is_set(): + try: + for d in state.aria2.get_downloads(): + if d.is_metadata: + continue + if d.progress == 100 and d.gid not in updated: + if state.debug: + print(f"Marking {d.name} as completed") + update_download_completed_by_hash(d.info_hash, True) + updated.add(d.gid) + state.downloads = [d for d in state.aria2.get_downloads() if d.is_active and not d.is_metadata] + except Exception as e: + if state.debug: + print(f"Error in update_log: {e}") + import traceback + traceback.print_exc() time.sleep(5) \ No newline at end of file diff --git a/core/utils/general/logs.py b/core/utils/general/logs.py index 724a236..0a53854 100644 --- a/core/utils/general/logs.py +++ b/core/utils/general/logs.py @@ -3,6 +3,7 @@ from core.utils.data.state import state from dataclasses import asdict import json import os +import re def add_download_log(title, url, magnet_uri, completed) -> DownloadList: @@ -18,12 +19,22 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList: else: downloads = [] + + if any(d.magnet_uri == magnet_uri or d.url == url for d in downloads): + if state.debug: + print("Skipped Logging, download already in file") + return DownloadList(data=downloads, count=len(downloads)) # thanks again claude (im stupid) + + downloads.append(Download( title=title, url=url, magnet_uri=magnet_uri, - completed=completed + completed=completed, )) + + if state.debug: + print(f"Added {title} to Log File") download_list = DownloadList(data=downloads, count=len(downloads)) with open(downloads_file, "w") as file: @@ -31,6 +42,64 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList: return download_list +def update_download_completed(magnet_uri, completed) -> DownloadList: + downloads_file = os.path.join(state.settings_path, "downloads.json") + + if os.path.exists(downloads_file) and os.path.getsize(downloads_file) > 0: + try: + with open(downloads_file, "r") as file: + existing_data = json.load(file) + downloads = [Download(**d) for d in existing_data.get("data", [])] + except (json.JSONDecodeError, TypeError) as e: + if state.debug: + print(f"Error loading downloads.json: {e}") + downloads = [] + else: + downloads = [] + + identifier = (magnet_uri or "").strip() + if state.debug: + print(f"Updating download completed for identifier: {identifier!r}") + print(f"Total downloads in file: {len(downloads)}") + for i, d in enumerate(downloads): + try: + mag = (getattr(d, 'magnet_uri', None) or '')[:50] + url = (getattr(d, 'url', None) or '')[:50] + print(f" [{i}] magnet: {mag}... url: {url}...") + except Exception as e: + print(f" [{i}] Error reading download: {e}") + + found = False + for download in downloads: + try: + stored_magnet = (getattr(download, 'magnet_uri', None) or "").strip() + stored_url = (getattr(download, 'url', None) or "").strip() + if state.debug: + print(f"Comparing with magnet: {stored_magnet[:50] if stored_magnet else 'None'}...") + if identifier and (stored_magnet == identifier or stored_url == identifier): + if state.debug: + print(f"Match found! Setting completed={completed}") + download.completed = completed + found = True + except Exception as e: + if state.debug: + print(f"Error comparing download: {e}") + + if not found: + if state.debug: + print("No matching download found to update") + return DownloadList(data=downloads, count=len(downloads)) + + download_list = DownloadList(data=downloads, count=len(downloads)) + with open(downloads_file, "w") as file: + json.dump(asdict(download_list), file, indent=4) + + if state.debug: + print("Updated download log") + + return download_list + + def get_download_logs() -> DownloadList: downloads_file = os.path.join(state.settings_path, "downloads.json") @@ -44,4 +113,59 @@ def get_download_logs() -> DownloadList: else: downloads = [] - return DownloadList(data=downloads, count=len(downloads)) \ No newline at end of file + return DownloadList(data=downloads, count=len(downloads)) + + +def extract_hash_from_magnet(magnet_uri): # full credits to claude for this + match = re.search(r'urn:btih:([A-F0-9]+)', magnet_uri, re.IGNORECASE) + if match: + return match.group(1).upper() + return None + + +def update_download_completed_by_hash(info_hash, completed) -> DownloadList: + downloads_file = os.path.join(state.settings_path, "downloads.json") + + if os.path.exists(downloads_file) and os.path.getsize(downloads_file) > 0: + try: + with open(downloads_file, "r") as file: + existing_data = json.load(file) + downloads = [Download(**d) for d in existing_data.get("data", [])] + except (json.JSONDecodeError, TypeError) as e: + if state.debug: + print(f"Error loading downloads.json: {e}") + downloads = [] + else: + downloads = [] + + info_hash_upper = (info_hash or "").upper().strip() + if state.debug: + print(f"Updating download by hash: {info_hash_upper}") + print(f"Total downloads in file: {len(downloads)}") + + found = False + for download in downloads: + try: + magnet_uri = getattr(download, 'magnet_uri', None) + if magnet_uri: + stored_hash = extract_hash_from_magnet(magnet_uri) + if stored_hash and stored_hash == info_hash_upper: + download.completed = completed + found = True + except Exception as e: + if state.debug: + print(f"Error comparing download: {e}") + + if not found: + if state.debug: + print("No matching download found to update") + return DownloadList(data=downloads, count=len(downloads)) + + download_list = DownloadList(data=downloads, count=len(downloads)) + with open(downloads_file, "w") as file: + json.dump(asdict(download_list), file, indent=4) + + if state.debug: + print("Updated download log") + + return download_list \ No newline at end of file diff --git a/main.py b/main.py index 9ca1363..30dc17d 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,10 @@ 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.network.aria2_integration import send_notification, update_log 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 core.utils.general.logs import get_download_logs from PySide6 import QtWidgets import qdarktheme import darkdetect @@ -45,6 +44,7 @@ if __name__ == "__main__": state.aria2process = run_aria2server() signal.signal(signal.SIGINT, keyboardinterrupthandler) run_thread(threading.Thread(target=send_notification, args=(shutdown_event,), daemon=True)) + run_thread(threading.Thread(target=update_log, args=(shutdown_event,), daemon=True)) if state.debug: print("Launching GUI") run_gui()