Merge pull request #57 from KeksPirates/fix/improve-memory-and-quality

Enhance thread-safety, logging, and image handling performance
This commit is contained in:
shayaa
2026-04-08 02:03:35 +02:00
committed by GitHub
13 changed files with 217 additions and 159 deletions
+3 -2
View File
@@ -50,6 +50,9 @@ class SteamripScraper:
for i in range(len(names)): for i in range(len(names)):
ret.append({"title" : names[i], "url" : links[i]}) ret.append({"title" : names[i], "url" : links[i]})
self.cache["data"] = ret
self.cache["last_fetched"] = current_time
return ret return ret
def scrape_steamrip_game_downloads(self, gamelink): def scrape_steamrip_game_downloads(self, gamelink):
@@ -72,8 +75,6 @@ class SteamripScraper:
if len(link) != 1: if len(link) != 1:
ret.append(link) ret.append(link)
self.cache["data"] = ret
return ret return ret
def get_download_link(self, post: Dict): def get_download_link(self, post: Dict):
+1 -1
View File
@@ -43,7 +43,7 @@ class UztrackerScraper:
except requests.RequestException as e: except requests.RequestException as e:
consoleLog(f"Failed to fetch {search_url}: {e}") consoleLog(f"Failed to fetch {search_url}: {e}")
return None return []
def get_download_link(self, post: Dict): def get_download_link(self, post: Dict):
return get_magnet_link(post["url"]) return get_magnet_link(post["url"])
+13 -8
View File
@@ -34,10 +34,12 @@ class ContextMenu:
if not hasattr(self, '_context_menu_row'): if not hasattr(self, '_context_menu_row'):
return return
row = self._context_menu_row row = self._context_menu_row
if row < 0 or row >= len(state.active_downloads): with state.downloads_lock:
return if row < 0 or row >= len(state.active_downloads):
magnet_link = list(state.active_downloads.keys())[row] return
magnetdl = state.active_downloads[magnet_link] keys = list(state.active_downloads.keys())
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link]
download_path = magnetdl.save_path() download_path = magnetdl.save_path()
if download_path and os.path.exists(download_path): if download_path and os.path.exists(download_path):
if platform.system() == "Windows": if platform.system() == "Windows":
@@ -51,9 +53,11 @@ class ContextMenu:
if not hasattr(self, '_context_menu_row'): if not hasattr(self, '_context_menu_row'):
return return
row = self._context_menu_row row = self._context_menu_row
if row < 0 or row >= len(state.active_downloads): with state.downloads_lock:
return if row < 0 or row >= len(state.active_downloads):
magnet_link = list(state.active_downloads.keys())[row] return
keys = list(state.active_downloads.keys())
magnet_link = keys[row]
clipboard = QtWidgets.QApplication.clipboard() clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(magnet_link) clipboard.setText(magnet_link)
@@ -64,7 +68,8 @@ class ContextMenu:
with state.downloads_lock: with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads): if row < 0 or row >= len(state.active_downloads):
return return
magnet_link = list(state.active_downloads.keys())[row] keys = list(state.active_downloads.keys())
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link] magnetdl = state.active_downloads[magnet_link]
# Cache the name BEFORE removing from session # Cache the name BEFORE removing from session
+4 -2
View File
@@ -34,7 +34,8 @@ class DownloadModel(QAbstractTableModel):
return None return None
try: try:
magnet_link = list(state.active_downloads.keys())[index.row()] keys = list(state.active_downloads.keys())
magnet_link = keys[index.row()]
magnetdl = state.active_downloads[magnet_link] magnetdl = state.active_downloads[magnet_link]
status = magnetdl.status() status = magnetdl.status()
except (IndexError, KeyError, RuntimeError): except (IndexError, KeyError, RuntimeError):
@@ -101,7 +102,8 @@ class DownloadModel(QAbstractTableModel):
if row >= len(state.active_downloads) or row < 0: if row >= len(state.active_downloads) or row < 0:
return return
try: try:
magnet_link = list(state.active_downloads.keys())[row] keys = list(state.active_downloads.keys())
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link] magnetdl = state.active_downloads[magnet_link]
status = magnetdl.status() status = magnetdl.status()
except (IndexError, KeyError, RuntimeError): except (IndexError, KeyError, RuntimeError):
+68 -4
View File
@@ -1,5 +1,5 @@
from PySide6.QtWidgets import QLabel, QGraphicsOpacityEffect, QStackedWidget from PySide6.QtWidgets import QLabel, QGraphicsOpacityEffect, QStackedWidget
from PySide6.QtCore import Qt, QSize, QEvent, QObject from PySide6.QtCore import Qt, QSize, QEvent, QObject, QTimer
from PySide6.QtGui import QImage, QPixmap from PySide6.QtGui import QImage, QPixmap
from utils.data.state import state from utils.data.state import state
import darkdetect import darkdetect
@@ -17,8 +17,15 @@ class Image(QObject):
self.overlay_label.setGraphicsEffect(self.opacity_effect) self.overlay_label.setGraphicsEffect(self.opacity_effect)
self._current_image_path = None self._current_image_path = None
self._cached_qimage = None
self._wallpaper_active = False self._wallpaper_active = False
self._original_stylesheets = {} self._original_stylesheets = {}
self._resize_timer = QTimer(self)
self._resize_timer.setSingleShot(True)
self._resize_timer.setInterval(100)
self._resize_timer.timeout.connect(self._on_resize_finished)
parent.installEventFilter(self) parent.installEventFilter(self)
state.image_changed.connect(self.update_image_overlay) state.image_changed.connect(self.update_image_overlay)
@@ -28,9 +35,14 @@ class Image(QObject):
def eventFilter(self, obj, event): def eventFilter(self, obj, event):
if obj == self.application and event.type() == QEvent.Type.Resize: if obj == self.application and event.type() == QEvent.Type.Resize:
if self._current_image_path: if self._current_image_path:
self._load_and_display(self._current_image_path) self._apply_layout_fast()
self._resize_timer.start()
return False return False
def _on_resize_finished(self):
if self._current_image_path:
self._apply_layout()
def _set_wallpaper_transparency(self, enabled): def _set_wallpaper_transparency(self, enabled):
if self._wallpaper_active == enabled: if self._wallpaper_active == enabled:
return return
@@ -117,12 +129,62 @@ class Image(QObject):
self._set_wallpaper_transparency(False) self._set_wallpaper_transparency(False)
return return
self._current_image_path = image_path self._current_image_path = image_path
# Only reload from disk when the path actually changed
if self._cached_qimage is None or image_path != getattr(self, '_cached_path', None):
image = QImage(image_path)
if image.isNull():
self.overlay_label.hide()
self._cached_qimage = None
return
self._cached_qimage = image
self._cached_path = image_path
self._apply_layout()
def _apply_layout_fast(self):
if self._cached_qimage is None or not state.image_enabled:
return
parent = self.application parent = self.application
image = QImage(image_path) image = self._cached_qimage
if image.isNull():
if getattr(state, "image_as_wallpaper", False):
scaled = image.scaled(
parent.size(),
Qt.AspectRatioMode.KeepAspectRatioByExpanding,
Qt.TransformationMode.FastTransformation
)
cx = (scaled.width() - parent.width()) // 2
cy = (scaled.height() - parent.height()) // 2
cropped = scaled.copy(cx, cy, parent.width(), parent.height())
self.overlay_label.setPixmap(QPixmap.fromImage(cropped))
self.overlay_label.setGeometry(0, 0, parent.width(), parent.height())
else:
w = self.overlay_label.width()
h = self.overlay_label.height()
pos = state.image_position
off = int(state.image_offset)
if pos == "top-left":
x, y = off, off
elif pos == "top-right":
x, y = parent.width() - w - off, off
elif pos == "bottom-left":
x, y = off, parent.height() - h - off
elif pos == "center":
x, y = (parent.width() - w) // 2, (parent.height() - h) // 2
else:
x, y = parent.width() - w - off, parent.height() - h - off
self.overlay_label.move(x, y)
def _apply_layout(self):
if self._cached_qimage is None or not state.image_enabled:
self.overlay_label.hide() self.overlay_label.hide()
return return
parent = self.application
image = self._cached_qimage
if getattr(state, "image_as_wallpaper", False): if getattr(state, "image_as_wallpaper", False):
scaled_image = image.scaled( scaled_image = image.scaled(
parent.size(), parent.size(),
@@ -184,4 +246,6 @@ class Image(QObject):
self.overlay_label.show() self.overlay_label.show()
def update_image_overlay(self, new_image_path): def update_image_overlay(self, new_image_path):
if new_image_path != getattr(self, '_cached_path', None):
self._cached_qimage = None
self._load_and_display(new_image_path) self._load_and_display(new_image_path)
+9
View File
@@ -315,6 +315,15 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
def _on_log_signal(self, text): def _on_log_signal(self, text):
if hasattr(self, 'consoleLog'): if hasattr(self, 'consoleLog'):
self.consoleLog.append(text) self.consoleLog.append(text)
# Delete lines if exceeding 500
doc = self.consoleLog.document()
if doc.blockCount() > 500:
cursor = self.consoleLog.textCursor()
cursor.movePosition(cursor.MoveOperation.Start)
cursor.movePosition(cursor.MoveOperation.Down, cursor.MoveMode.KeepAnchor, doc.blockCount() - 500)
cursor.removeSelectedText()
cursor.deleteChar()
self.consoleLog.verticalScrollBar().setValue( self.consoleLog.verticalScrollBar().setValue(
self.consoleLog.verticalScrollBar().maximum() self.consoleLog.verticalScrollBar().maximum()
) )
+6 -4
View File
@@ -15,9 +15,10 @@ def add_direct_download(url: str, title: str, dl_path: Optional[str] = None, hea
if dl_path is None: if dl_path is None:
dl_path = state.download_path dl_path = state.download_path
if url in state.active_downloads: with state.downloads_lock:
consoleLog(f"Download already active: {title}") if url in state.active_downloads:
return consoleLog(f"Download already active: {title}")
return
filename = ( filename = (
detect_filename_from_headers(url, DirectDownloadHandle.USER_AGENT) detect_filename_from_headers(url, DirectDownloadHandle.USER_AGENT)
@@ -26,7 +27,8 @@ def add_direct_download(url: str, title: str, dl_path: Optional[str] = None, hea
) )
handle = DirectDownloadHandle(url, filename, dl_path, headers, single_threaded) handle = DirectDownloadHandle(url, filename, dl_path, headers, single_threaded)
state.active_downloads[url] = handle with state.downloads_lock:
state.active_downloads[url] = handle
add_download_log(title, url, "", False) add_download_log(title, url, "", False)
handle.start() handle.start()
consoleLog(f"Started direct download: {filename}") consoleLog(f"Started direct download: {filename}")
+4 -4
View File
@@ -1,4 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from collections import deque
from typing import Optional from typing import Optional
import libtorrent as lt import libtorrent as lt
import threading import threading
@@ -31,7 +32,7 @@ class DirectDownloadStatus:
self._upload_rate = 0 self._upload_rate = 0
self._chunk_bytes: dict[int, int] = {} self._chunk_bytes: dict[int, int] = {}
self._speed_window: list[tuple[float, int]] = [] self._speed_window: deque[tuple[float, int]] = deque()
self._speed_window_size = 3.0 self._speed_window_size = 3.0
@property @property
@@ -65,9 +66,8 @@ class DirectDownloadStatus:
self._speed_window.append((now, self._total_wanted_done)) self._speed_window.append((now, self._total_wanted_done))
cutoff = now - self._speed_window_size cutoff = now - self._speed_window_size
self._speed_window = [ while self._speed_window and self._speed_window[0][0] < cutoff:
(t, b) for t, b in self._speed_window if t >= cutoff self._speed_window.popleft()
]
if len(self._speed_window) >= 2: if len(self._speed_window) >= 2:
oldest_time, oldest_bytes = self._speed_window[0] oldest_time, oldest_bytes = self._speed_window[0]
dt = now - oldest_time dt = now - oldest_time
+53 -43
View File
@@ -10,7 +10,7 @@ import time
import os import os
loop_running = False _loop_lock = threading.Lock()
def get_free_space_mb(dirname): def get_free_space_mb(dirname):
if platform.system() == 'Windows': if platform.system() == 'Windows':
@@ -23,7 +23,9 @@ def get_free_space_mb(dirname):
def check_space(): def check_space():
while not state.shutdown_event.is_set(): while not state.shutdown_event.is_set():
for _, magnetdl in list(state.active_downloads.items()): with state.downloads_lock:
items = list(state.active_downloads.items())
for _, magnetdl in items:
try: try:
status = magnetdl.status() status = magnetdl.status()
except RuntimeError: except RuntimeError:
@@ -81,9 +83,11 @@ def add_download(magnet_uri):
init_session() init_session()
free_space = get_free_space_mb(state.download_path) free_space = get_free_space_mb(state.download_path)
if magnet_uri in state.active_downloads: with state.downloads_lock:
already_active = magnet_uri in state.active_downloads
handle = state.active_downloads.get(magnet_uri) if already_active else None
if already_active:
try: try:
handle = state.active_downloads[magnet_uri]
status = handle.status() status = handle.status()
if status.has_metadata: if status.has_metadata:
@@ -93,7 +97,8 @@ def add_download(magnet_uri):
consoleLog(f"File Deleted, redownloading: {status.name}") consoleLog(f"File Deleted, redownloading: {status.name}")
state.dl_session.remove_torrent(handle) state.dl_session.remove_torrent(handle)
del state.active_downloads[magnet_uri] with state.downloads_lock:
del state.active_downloads[magnet_uri]
else: else:
consoleLog("Skipping Downloading, download already running...") consoleLog("Skipping Downloading, download already running...")
return False return False
@@ -102,23 +107,26 @@ def add_download(magnet_uri):
return False return False
except RuntimeError as e: except RuntimeError as e:
consoleLog(f"Error in LibTorrent Handle: {e}") consoleLog(f"Error in LibTorrent Handle: {e}")
del state.active_downloads[magnet_uri] with state.downloads_lock:
del state.active_downloads[magnet_uri]
try: try:
params = lt.parse_magnet_uri(magnet_uri) params = lt.parse_magnet_uri(magnet_uri)
params.save_path = state.download_path params.save_path = state.download_path
handle = state.dl_session.add_torrent(params) handle = state.dl_session.add_torrent(params)
metadata_timeout = 60
metadata_start = time.time()
while not handle.has_metadata(): while not handle.has_metadata():
if time.time() - metadata_start > metadata_timeout:
state.dl_session.remove_torrent(handle)
consoleLog("Timed out waiting for torrent metadata")
return False
time.sleep(1) time.sleep(1)
total_size = handle.get_torrent_info().total_size() total_size = handle.get_torrent_info().total_size()
if free_space > total_size: if free_space <= total_size:
magnetdl = lt.parse_magnet_uri(magnet_uri)
magnetdl.save_path = state.download_path
download = state.dl_session.add_torrent(magnetdl)
else:
state.dl_session.remove_torrent(handle) state.dl_session.remove_torrent(handle)
consoleLog("Not enough free space to download this item.") consoleLog("Not enough free space to download this item.")
return False return False
@@ -126,8 +134,9 @@ def add_download(magnet_uri):
except Exception as e: except Exception as e:
consoleLog(f"Failed to add torrent or fetch info: {e}") consoleLog(f"Failed to add torrent or fetch info: {e}")
return False return False
if download: if handle:
state.active_downloads[magnet_uri] = download with state.downloads_lock:
state.active_downloads[magnet_uri] = handle
consoleLog(f"Added {magnet_uri} to downloads") consoleLog(f"Added {magnet_uri} to downloads")
run_thread(threading.Thread(target=dl_status_loop)) run_thread(threading.Thread(target=dl_status_loop))
@@ -140,9 +149,10 @@ def add_seed(magnet_uri, file_path):
init_session() init_session()
if magnet_uri in state.active_downloads: with state.downloads_lock:
consoleLog("Already seeding this torrent") if magnet_uri in state.active_downloads:
return False consoleLog("Already seeding this torrent")
return False
try: try:
magnetdl = lt.parse_magnet_uri(magnet_uri) magnetdl = lt.parse_magnet_uri(magnet_uri)
@@ -151,43 +161,43 @@ def add_seed(magnet_uri, file_path):
except Exception as e: except Exception as e:
consoleLog(f"Failed to add seed: {e}") consoleLog(f"Failed to add seed: {e}")
return False return False
state.active_downloads[magnet_uri] = handle with state.downloads_lock:
state.active_downloads[magnet_uri] = handle
state.seeded_magnets.add(magnet_uri) state.seeded_magnets.add(magnet_uri)
return True return True
def dl_status_loop(): def dl_status_loop():
global loop_running if not _loop_lock.acquire(blocking=False):
if loop_running == True:
return return
loop_running = True try:
completed_set = set() completed_set = set()
if not state.active_downloads:
consoleLog("No active downloads")
loop_running = False
return
while state.active_downloads and not state.shutdown_event.is_set():
for magnet_uri, magnetdl in list(state.active_downloads.items()):
try:
status = magnetdl.status()
except RuntimeError:
continue
if status.state == lt.torrent_status.seeding and magnet_uri not in completed_set:
consoleLog(f"Download completed: {status.name}")
completed_set.add(magnet_uri)
if not state.active_downloads: if not state.active_downloads:
loop_running = False consoleLog("No active downloads")
break return
time.sleep(1) while state.active_downloads and not state.shutdown_event.is_set():
with state.downloads_lock:
loop_running = False items = list(state.active_downloads.items())
for magnet_uri, magnetdl in items:
try:
status = magnetdl.status()
except RuntimeError:
continue
if status.state == lt.torrent_status.seeding and magnet_uri not in completed_set:
consoleLog(f"Download completed: {status.name}")
completed_set.add(magnet_uri)
if not state.active_downloads:
break
time.sleep(1)
finally:
_loop_lock.release()
def update_settings(): def update_settings():
+17 -8
View File
@@ -9,19 +9,23 @@ import os
def cleanup_session(): def cleanup_session():
if state.dl_session is not None: if state.dl_session is not None:
for magnetdl in state.active_downloads.values(): with state.downloads_lock:
if hasattr(magnetdl, 'pause'): # Check it's a handle for magnetdl in state.active_downloads.values():
magnetdl.pause() if hasattr(magnetdl, 'pause'): # check it's a handle
magnetdl.pause()
del state.dl_session del state.dl_session
state.dl_session = None state.dl_session = None
state.active_downloads.clear() with state.downloads_lock:
state.active_downloads.clear()
def send_notification(shutdown_event): def send_notification(shutdown_event):
notified = set() notified = set()
while not shutdown_event.is_set(): while not shutdown_event.is_set():
try: try:
for magnet_uri, magnetdl in list(state.active_downloads.items()): with state.downloads_lock:
items = list(state.active_downloads.items())
for magnet_uri, magnetdl in items:
if isinstance(magnetdl, dict): if isinstance(magnetdl, dict):
continue continue
@@ -42,7 +46,9 @@ def update_log(shutdown_event):
updated = set() updated = set()
while not shutdown_event.is_set(): while not shutdown_event.is_set():
try: try:
for magnet_uri, magnetdl in list(state.active_downloads.items()): with state.downloads_lock:
items = list(state.active_downloads.items())
for magnet_uri, magnetdl in items:
if isinstance(magnetdl, dict): if isinstance(magnetdl, dict):
continue continue
@@ -63,7 +69,9 @@ def update_log(shutdown_event):
def check_deleted_files(shutdown_event): def check_deleted_files(shutdown_event):
while not shutdown_event.is_set(): while not shutdown_event.is_set():
try: try:
for magnet_uri, magnetdl in list(state.active_downloads.items()): with state.downloads_lock:
items = list(state.active_downloads.items())
for magnet_uri, magnetdl in items:
if isinstance(magnetdl, dict): if isinstance(magnetdl, dict):
continue continue
@@ -75,7 +83,8 @@ def check_deleted_files(shutdown_event):
if not os.path.exists(file_path): if not os.path.exists(file_path):
consoleLog(f"Registered File Deletion: {status.name}") consoleLog(f"Registered File Deletion: {status.name}")
state.dl_session.remove_torrent(magnetdl) state.dl_session.remove_torrent(magnetdl)
del state.active_downloads[magnet_uri] with state.downloads_lock:
del state.active_downloads[magnet_uri]
except Exception as e: except Exception as e:
consoleLog(f"Exception while checking for file deletions: {e}") consoleLog(f"Exception while checking for file deletions: {e}")
time.sleep(5) time.sleep(5)
+3
View File
@@ -38,6 +38,9 @@ class AppState(QObject):
self._image_opacity: int = 100 self._image_opacity: int = 100
self._image_as_wallpaper: bool = False self._image_as_wallpaper: bool = False
self._image_position: str = "bottom-right" # top-left, top-right, bottom-left, bottom-right, center self._image_position: str = "bottom-right" # top-left, top-right, bottom-left, bottom-right, center
self._image_custom_position: bool = False
self._image_x: int = 0
self._image_y: int = 0
# Trackers / Scraping # Trackers / Scraping
self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers
+30 -83
View File
@@ -8,6 +8,27 @@ import os
import re import re
def _downloads_file_path() -> str:
return os.path.join(state.settings_path, "downloads.json")
def _load_downloads() -> list[Download]:
downloads_file = _downloads_file_path()
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)
return [Download(**d) for d in existing_data.get("data", [])]
except (json.JSONDecodeError, TypeError) as e:
consoleLog(f"Error loading downloads.json: {e}")
return []
def _save_downloads(downloads: list[Download]) -> DownloadList:
download_list = DownloadList(data=downloads, count=len(downloads))
with open(_downloads_file_path(), "w") as file:
json.dump(asdict(download_list), file, indent=4)
return download_list
def add_download_log(title, url, magnet_uri, completed) -> DownloadList: def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
# wait for metadata outside the lock to avoid blocking other threads # wait for metadata outside the lock to avoid blocking other threads
magnetdl = state.active_downloads.get(magnet_uri) magnetdl = state.active_downloads.get(magnet_uri)
@@ -28,17 +49,7 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
return _add_download_log_inner(title, url, magnet_uri, completed, path) return _add_download_log_inner(title, url, magnet_uri, completed, path)
def _add_download_log_inner(title, url, magnet_uri, completed, path) -> DownloadList: def _add_download_log_inner(title, url, magnet_uri, completed, path) -> DownloadList:
downloads_file = os.path.join(state.settings_path, "downloads.json") downloads = _load_downloads()
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:
downloads = []
else:
downloads = []
if any((magnet_uri and d.magnet_uri == magnet_uri) or (url and d.url == url) for d in downloads): if any((magnet_uri and d.magnet_uri == magnet_uri) or (url and d.url == url) for d in downloads):
if magnet_uri and magnet_uri in state.active_downloads: if magnet_uri and magnet_uri in state.active_downloads:
@@ -60,30 +71,14 @@ def _add_download_log_inner(title, url, magnet_uri, completed, path) -> Download
)) ))
consoleLog(f"Added {title} to Log File") consoleLog(f"Added {title} to Log File")
return _save_downloads(downloads)
download_list = DownloadList(data=downloads, count=len(downloads))
with open(downloads_file, "w") as file:
json.dump(asdict(download_list), file, indent=4)
return download_list
def remove_download_log(magnet_uri) -> DownloadList: def remove_download_log(magnet_uri) -> DownloadList:
with state.downloads_lock: with state.downloads_lock:
return _remove_download_log_inner(magnet_uri) return _remove_download_log_inner(magnet_uri)
def _remove_download_log_inner(magnet_uri) -> DownloadList: def _remove_download_log_inner(magnet_uri) -> DownloadList:
downloads_file = os.path.join(state.settings_path, "downloads.json") downloads = _load_downloads()
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:
downloads = []
else:
downloads = []
magnet_link = (magnet_uri or "").strip() magnet_link = (magnet_uri or "").strip()
if not magnet_link: if not magnet_link:
@@ -93,30 +88,14 @@ def _remove_download_log_inner(magnet_uri) -> DownloadList:
downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link] downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link]
consoleLog(f"Removed {title} from Log File") consoleLog(f"Removed {title} from Log File")
return _save_downloads(downloads)
download_list = DownloadList(data=downloads, count=len(downloads))
with open(downloads_file, "w") as file:
json.dump(asdict(download_list), file, indent=4)
return download_list
def update_download_completed(magnet_uri, completed) -> DownloadList: def update_download_completed(magnet_uri, completed) -> DownloadList:
with state.downloads_lock: with state.downloads_lock:
return _update_download_completed_inner(magnet_uri, completed) return _update_download_completed_inner(magnet_uri, completed)
def _update_download_completed_inner(magnet_uri, completed) -> DownloadList: def _update_download_completed_inner(magnet_uri, completed) -> DownloadList:
downloads_file = os.path.join(state.settings_path, "downloads.json") downloads = _load_downloads()
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:
consoleLog(f"Error loading downloads.json: {e}")
downloads = []
else:
downloads = []
identifier = (magnet_uri or "").strip() identifier = (magnet_uri or "").strip()
if state.debug: if state.debug:
@@ -145,13 +124,8 @@ def _update_download_completed_inner(magnet_uri, completed) -> DownloadList:
consoleLog("No matching download found to update") consoleLog("No matching download found to update")
return DownloadList(data=downloads, count=len(downloads)) 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)
consoleLog("Updated download log") consoleLog("Updated download log")
return _save_downloads(downloads)
return download_list
def get_download_logs() -> DownloadList: def get_download_logs() -> DownloadList:
@@ -159,18 +133,7 @@ def get_download_logs() -> DownloadList:
return _get_download_logs_inner() return _get_download_logs_inner()
def _get_download_logs_inner() -> DownloadList: def _get_download_logs_inner() -> DownloadList:
downloads_file = os.path.join(state.settings_path, "downloads.json") downloads = _load_downloads()
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:
downloads = []
else:
downloads = []
return DownloadList(data=downloads, count=len(downloads)) return DownloadList(data=downloads, count=len(downloads))
@@ -195,18 +158,7 @@ def update_download_completed_by_hash(info_hash, completed) -> DownloadList:
return _update_download_completed_by_hash_inner(info_hash, completed) return _update_download_completed_by_hash_inner(info_hash, completed)
def _update_download_completed_by_hash_inner(info_hash, completed) -> DownloadList: def _update_download_completed_by_hash_inner(info_hash, completed) -> DownloadList:
downloads_file = os.path.join(state.settings_path, "downloads.json") downloads = _load_downloads()
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:
consoleLog(f"Error loading downloads.json: {e}")
downloads = []
else:
downloads = []
info_hash_upper = (info_hash or "").upper().strip() info_hash_upper = (info_hash or "").upper().strip()
consoleLog(f"Updating download by hash: {info_hash_upper}") consoleLog(f"Updating download by hash: {info_hash_upper}")
@@ -228,13 +180,8 @@ def _update_download_completed_by_hash_inner(info_hash, completed) -> DownloadLi
consoleLog("No matching download found to update") consoleLog("No matching download found to update")
return DownloadList(data=downloads, count=len(downloads)) 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)
consoleLog("Updated download log") consoleLog("Updated download log")
return _save_downloads(downloads)
return download_list
def set_main_window(window): def set_main_window(window):
+6
View File
@@ -37,10 +37,16 @@ def run_download(post, headers: Optional[dict] = None):
if ismagnet: if ismagnet:
link = result link = result
if not link:
consoleLog("Failed to retrieve magnet link")
return
add_magnet(link) add_magnet(link)
add_download_log(post.get("title", "Unknown"), "", link, False) add_download_log(post.get("title", "Unknown"), "", link, False)
else: else:
link, link_headers = result if isinstance(result, tuple) else (result, None) link, link_headers = result if isinstance(result, tuple) else (result, None)
if not link:
consoleLog("Failed to retrieve download link")
return
final_headers = headers or link_headers final_headers = headers or link_headers
add_direct_download(link, post.get("title", "Unknown"), headers=final_headers, single_threaded=final_headers is not None) add_direct_download(link, post.get("title", "Unknown"), headers=final_headers, single_threaded=final_headers is not None)