mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-03 17:39:42 +02:00
Merge pull request #57 from KeksPirates/fix/improve-memory-and-quality
Enhance thread-safety, logging, and image handling performance
This commit is contained in:
@@ -50,6 +50,9 @@ class SteamripScraper:
|
||||
for i in range(len(names)):
|
||||
ret.append({"title" : names[i], "url" : links[i]})
|
||||
|
||||
self.cache["data"] = ret
|
||||
self.cache["last_fetched"] = current_time
|
||||
|
||||
return ret
|
||||
|
||||
def scrape_steamrip_game_downloads(self, gamelink):
|
||||
@@ -72,8 +75,6 @@ class SteamripScraper:
|
||||
if len(link) != 1:
|
||||
ret.append(link)
|
||||
|
||||
self.cache["data"] = ret
|
||||
|
||||
return ret
|
||||
|
||||
def get_download_link(self, post: Dict):
|
||||
|
||||
@@ -43,7 +43,7 @@ class UztrackerScraper:
|
||||
|
||||
except requests.RequestException as e:
|
||||
consoleLog(f"Failed to fetch {search_url}: {e}")
|
||||
return None
|
||||
return []
|
||||
|
||||
def get_download_link(self, post: Dict):
|
||||
return get_magnet_link(post["url"])
|
||||
|
||||
@@ -34,10 +34,12 @@ class ContextMenu:
|
||||
if not hasattr(self, '_context_menu_row'):
|
||||
return
|
||||
row = self._context_menu_row
|
||||
if row < 0 or row >= len(state.active_downloads):
|
||||
return
|
||||
magnet_link = list(state.active_downloads.keys())[row]
|
||||
magnetdl = state.active_downloads[magnet_link]
|
||||
with state.downloads_lock:
|
||||
if row < 0 or row >= len(state.active_downloads):
|
||||
return
|
||||
keys = list(state.active_downloads.keys())
|
||||
magnet_link = keys[row]
|
||||
magnetdl = state.active_downloads[magnet_link]
|
||||
download_path = magnetdl.save_path()
|
||||
if download_path and os.path.exists(download_path):
|
||||
if platform.system() == "Windows":
|
||||
@@ -51,9 +53,11 @@ class ContextMenu:
|
||||
if not hasattr(self, '_context_menu_row'):
|
||||
return
|
||||
row = self._context_menu_row
|
||||
if row < 0 or row >= len(state.active_downloads):
|
||||
return
|
||||
magnet_link = list(state.active_downloads.keys())[row]
|
||||
with state.downloads_lock:
|
||||
if row < 0 or row >= len(state.active_downloads):
|
||||
return
|
||||
keys = list(state.active_downloads.keys())
|
||||
magnet_link = keys[row]
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
clipboard.setText(magnet_link)
|
||||
|
||||
@@ -64,7 +68,8 @@ class ContextMenu:
|
||||
with state.downloads_lock:
|
||||
if row < 0 or row >= len(state.active_downloads):
|
||||
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]
|
||||
|
||||
# Cache the name BEFORE removing from session
|
||||
|
||||
@@ -34,7 +34,8 @@ class DownloadModel(QAbstractTableModel):
|
||||
return None
|
||||
|
||||
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]
|
||||
status = magnetdl.status()
|
||||
except (IndexError, KeyError, RuntimeError):
|
||||
@@ -101,7 +102,8 @@ class DownloadModel(QAbstractTableModel):
|
||||
if row >= len(state.active_downloads) or row < 0:
|
||||
return
|
||||
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]
|
||||
status = magnetdl.status()
|
||||
except (IndexError, KeyError, RuntimeError):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 utils.data.state import state
|
||||
import darkdetect
|
||||
@@ -17,8 +17,15 @@ class Image(QObject):
|
||||
self.overlay_label.setGraphicsEffect(self.opacity_effect)
|
||||
|
||||
self._current_image_path = None
|
||||
self._cached_qimage = None
|
||||
self._wallpaper_active = False
|
||||
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)
|
||||
state.image_changed.connect(self.update_image_overlay)
|
||||
|
||||
@@ -28,9 +35,14 @@ class Image(QObject):
|
||||
def eventFilter(self, obj, event):
|
||||
if obj == self.application and event.type() == QEvent.Type.Resize:
|
||||
if self._current_image_path:
|
||||
self._load_and_display(self._current_image_path)
|
||||
self._apply_layout_fast()
|
||||
self._resize_timer.start()
|
||||
return False
|
||||
|
||||
def _on_resize_finished(self):
|
||||
if self._current_image_path:
|
||||
self._apply_layout()
|
||||
|
||||
def _set_wallpaper_transparency(self, enabled):
|
||||
if self._wallpaper_active == enabled:
|
||||
return
|
||||
@@ -117,12 +129,62 @@ class Image(QObject):
|
||||
self._set_wallpaper_transparency(False)
|
||||
return
|
||||
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
|
||||
image = QImage(image_path)
|
||||
if image.isNull():
|
||||
image = self._cached_qimage
|
||||
|
||||
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()
|
||||
return
|
||||
|
||||
parent = self.application
|
||||
image = self._cached_qimage
|
||||
|
||||
if getattr(state, "image_as_wallpaper", False):
|
||||
scaled_image = image.scaled(
|
||||
parent.size(),
|
||||
@@ -184,4 +246,6 @@ class Image(QObject):
|
||||
self.overlay_label.show()
|
||||
|
||||
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)
|
||||
@@ -315,6 +315,15 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
def _on_log_signal(self, text):
|
||||
if hasattr(self, 'consoleLog'):
|
||||
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().maximum()
|
||||
)
|
||||
|
||||
@@ -15,9 +15,10 @@ def add_direct_download(url: str, title: str, dl_path: Optional[str] = None, hea
|
||||
if dl_path is None:
|
||||
dl_path = state.download_path
|
||||
|
||||
if url in state.active_downloads:
|
||||
consoleLog(f"Download already active: {title}")
|
||||
return
|
||||
with state.downloads_lock:
|
||||
if url in state.active_downloads:
|
||||
consoleLog(f"Download already active: {title}")
|
||||
return
|
||||
|
||||
filename = (
|
||||
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)
|
||||
state.active_downloads[url] = handle
|
||||
with state.downloads_lock:
|
||||
state.active_downloads[url] = handle
|
||||
add_download_log(title, url, "", False)
|
||||
handle.start()
|
||||
consoleLog(f"Started direct download: {filename}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from collections import deque
|
||||
from typing import Optional
|
||||
import libtorrent as lt
|
||||
import threading
|
||||
@@ -31,7 +32,7 @@ class DirectDownloadStatus:
|
||||
self._upload_rate = 0
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
@@ -65,9 +66,8 @@ class DirectDownloadStatus:
|
||||
self._speed_window.append((now, self._total_wanted_done))
|
||||
|
||||
cutoff = now - self._speed_window_size
|
||||
self._speed_window = [
|
||||
(t, b) for t, b in self._speed_window if t >= cutoff
|
||||
]
|
||||
while self._speed_window and self._speed_window[0][0] < cutoff:
|
||||
self._speed_window.popleft()
|
||||
if len(self._speed_window) >= 2:
|
||||
oldest_time, oldest_bytes = self._speed_window[0]
|
||||
dt = now - oldest_time
|
||||
|
||||
@@ -10,7 +10,7 @@ import time
|
||||
import os
|
||||
|
||||
|
||||
loop_running = False
|
||||
_loop_lock = threading.Lock()
|
||||
|
||||
def get_free_space_mb(dirname):
|
||||
if platform.system() == 'Windows':
|
||||
@@ -23,7 +23,9 @@ def get_free_space_mb(dirname):
|
||||
|
||||
def check_space():
|
||||
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:
|
||||
status = magnetdl.status()
|
||||
except RuntimeError:
|
||||
@@ -81,9 +83,11 @@ def add_download(magnet_uri):
|
||||
init_session()
|
||||
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:
|
||||
handle = state.active_downloads[magnet_uri]
|
||||
status = handle.status()
|
||||
|
||||
if status.has_metadata:
|
||||
@@ -93,7 +97,8 @@ def add_download(magnet_uri):
|
||||
|
||||
consoleLog(f"File Deleted, redownloading: {status.name}")
|
||||
state.dl_session.remove_torrent(handle)
|
||||
del state.active_downloads[magnet_uri]
|
||||
with state.downloads_lock:
|
||||
del state.active_downloads[magnet_uri]
|
||||
else:
|
||||
consoleLog("Skipping Downloading, download already running...")
|
||||
return False
|
||||
@@ -102,23 +107,26 @@ def add_download(magnet_uri):
|
||||
return False
|
||||
except RuntimeError as 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:
|
||||
params = lt.parse_magnet_uri(magnet_uri)
|
||||
params.save_path = state.download_path
|
||||
|
||||
handle = state.dl_session.add_torrent(params)
|
||||
metadata_timeout = 60
|
||||
metadata_start = time.time()
|
||||
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)
|
||||
|
||||
total_size = handle.get_torrent_info().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:
|
||||
if free_space <= total_size:
|
||||
state.dl_session.remove_torrent(handle)
|
||||
consoleLog("Not enough free space to download this item.")
|
||||
return False
|
||||
@@ -126,8 +134,9 @@ def add_download(magnet_uri):
|
||||
except Exception as e:
|
||||
consoleLog(f"Failed to add torrent or fetch info: {e}")
|
||||
return False
|
||||
if download:
|
||||
state.active_downloads[magnet_uri] = download
|
||||
if handle:
|
||||
with state.downloads_lock:
|
||||
state.active_downloads[magnet_uri] = handle
|
||||
consoleLog(f"Added {magnet_uri} to downloads")
|
||||
|
||||
run_thread(threading.Thread(target=dl_status_loop))
|
||||
@@ -140,9 +149,10 @@ def add_seed(magnet_uri, file_path):
|
||||
|
||||
init_session()
|
||||
|
||||
if magnet_uri in state.active_downloads:
|
||||
consoleLog("Already seeding this torrent")
|
||||
return False
|
||||
with state.downloads_lock:
|
||||
if magnet_uri in state.active_downloads:
|
||||
consoleLog("Already seeding this torrent")
|
||||
return False
|
||||
|
||||
try:
|
||||
magnetdl = lt.parse_magnet_uri(magnet_uri)
|
||||
@@ -151,43 +161,43 @@ def add_seed(magnet_uri, file_path):
|
||||
except Exception as e:
|
||||
consoleLog(f"Failed to add seed: {e}")
|
||||
return False
|
||||
state.active_downloads[magnet_uri] = handle
|
||||
with state.downloads_lock:
|
||||
state.active_downloads[magnet_uri] = handle
|
||||
state.seeded_magnets.add(magnet_uri)
|
||||
return True
|
||||
|
||||
|
||||
def dl_status_loop():
|
||||
global loop_running
|
||||
if loop_running == True:
|
||||
if not _loop_lock.acquire(blocking=False):
|
||||
return
|
||||
|
||||
loop_running = True
|
||||
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)
|
||||
try:
|
||||
completed_set = set()
|
||||
|
||||
if not state.active_downloads:
|
||||
loop_running = False
|
||||
break
|
||||
consoleLog("No active downloads")
|
||||
return
|
||||
|
||||
time.sleep(1)
|
||||
while state.active_downloads and not state.shutdown_event.is_set():
|
||||
with state.downloads_lock:
|
||||
items = list(state.active_downloads.items())
|
||||
for magnet_uri, magnetdl in items:
|
||||
try:
|
||||
status = magnetdl.status()
|
||||
except RuntimeError:
|
||||
continue
|
||||
|
||||
loop_running = False
|
||||
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():
|
||||
|
||||
|
||||
@@ -9,19 +9,23 @@ import os
|
||||
|
||||
def cleanup_session():
|
||||
if state.dl_session is not None:
|
||||
for magnetdl in state.active_downloads.values():
|
||||
if hasattr(magnetdl, 'pause'): # Check it's a handle
|
||||
magnetdl.pause()
|
||||
with state.downloads_lock:
|
||||
for magnetdl in state.active_downloads.values():
|
||||
if hasattr(magnetdl, 'pause'): # check it's a handle
|
||||
magnetdl.pause()
|
||||
|
||||
del state.dl_session
|
||||
state.dl_session = None
|
||||
state.active_downloads.clear()
|
||||
with state.downloads_lock:
|
||||
state.active_downloads.clear()
|
||||
|
||||
def send_notification(shutdown_event):
|
||||
notified = set()
|
||||
while not shutdown_event.is_set():
|
||||
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):
|
||||
continue
|
||||
|
||||
@@ -42,7 +46,9 @@ def update_log(shutdown_event):
|
||||
updated = set()
|
||||
while not shutdown_event.is_set():
|
||||
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):
|
||||
continue
|
||||
|
||||
@@ -63,7 +69,9 @@ def update_log(shutdown_event):
|
||||
def check_deleted_files(shutdown_event):
|
||||
while not shutdown_event.is_set():
|
||||
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):
|
||||
continue
|
||||
|
||||
@@ -75,7 +83,8 @@ def check_deleted_files(shutdown_event):
|
||||
if not os.path.exists(file_path):
|
||||
consoleLog(f"Registered File Deletion: {status.name}")
|
||||
state.dl_session.remove_torrent(magnetdl)
|
||||
del state.active_downloads[magnet_uri]
|
||||
with state.downloads_lock:
|
||||
del state.active_downloads[magnet_uri]
|
||||
except Exception as e:
|
||||
consoleLog(f"Exception while checking for file deletions: {e}")
|
||||
time.sleep(5)
|
||||
@@ -38,6 +38,9 @@ class AppState(QObject):
|
||||
self._image_opacity: int = 100
|
||||
self._image_as_wallpaper: bool = False
|
||||
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
|
||||
self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers
|
||||
|
||||
+30
-83
@@ -8,6 +8,27 @@ import os
|
||||
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:
|
||||
# wait for metadata outside the lock to avoid blocking other threads
|
||||
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)
|
||||
|
||||
def _add_download_log_inner(title, url, magnet_uri, completed, path) -> 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:
|
||||
downloads = []
|
||||
else:
|
||||
downloads = []
|
||||
downloads = _load_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:
|
||||
@@ -60,30 +71,14 @@ def _add_download_log_inner(title, url, magnet_uri, completed, path) -> Download
|
||||
))
|
||||
|
||||
consoleLog(f"Added {title} to Log File")
|
||||
|
||||
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
|
||||
return _save_downloads(downloads)
|
||||
|
||||
def remove_download_log(magnet_uri) -> DownloadList:
|
||||
with state.downloads_lock:
|
||||
return _remove_download_log_inner(magnet_uri)
|
||||
|
||||
def _remove_download_log_inner(magnet_uri) -> 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:
|
||||
downloads = []
|
||||
else:
|
||||
downloads = []
|
||||
|
||||
downloads = _load_downloads()
|
||||
|
||||
magnet_link = (magnet_uri or "").strip()
|
||||
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]
|
||||
|
||||
consoleLog(f"Removed {title} from Log File")
|
||||
|
||||
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
|
||||
return _save_downloads(downloads)
|
||||
|
||||
def update_download_completed(magnet_uri, completed) -> DownloadList:
|
||||
with state.downloads_lock:
|
||||
return _update_download_completed_inner(magnet_uri, completed)
|
||||
|
||||
def _update_download_completed_inner(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:
|
||||
consoleLog(f"Error loading downloads.json: {e}")
|
||||
downloads = []
|
||||
else:
|
||||
downloads = []
|
||||
downloads = _load_downloads()
|
||||
|
||||
identifier = (magnet_uri or "").strip()
|
||||
if state.debug:
|
||||
@@ -145,13 +124,8 @@ def _update_download_completed_inner(magnet_uri, completed) -> DownloadList:
|
||||
consoleLog("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)
|
||||
|
||||
consoleLog("Updated download log")
|
||||
|
||||
return download_list
|
||||
return _save_downloads(downloads)
|
||||
|
||||
|
||||
def get_download_logs() -> DownloadList:
|
||||
@@ -159,18 +133,7 @@ def get_download_logs() -> DownloadList:
|
||||
return _get_download_logs_inner()
|
||||
|
||||
def _get_download_logs_inner() -> 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:
|
||||
downloads = []
|
||||
else:
|
||||
downloads = []
|
||||
|
||||
downloads = _load_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)
|
||||
|
||||
def _update_download_completed_by_hash_inner(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:
|
||||
consoleLog(f"Error loading downloads.json: {e}")
|
||||
downloads = []
|
||||
else:
|
||||
downloads = []
|
||||
downloads = _load_downloads()
|
||||
|
||||
info_hash_upper = (info_hash or "").upper().strip()
|
||||
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")
|
||||
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")
|
||||
|
||||
return download_list
|
||||
return _save_downloads(downloads)
|
||||
|
||||
|
||||
def set_main_window(window):
|
||||
|
||||
@@ -37,10 +37,16 @@ def run_download(post, headers: Optional[dict] = None):
|
||||
|
||||
if ismagnet:
|
||||
link = result
|
||||
if not link:
|
||||
consoleLog("Failed to retrieve magnet link")
|
||||
return
|
||||
add_magnet(link)
|
||||
add_download_log(post.get("title", "Unknown"), "", link, False)
|
||||
else:
|
||||
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
|
||||
add_direct_download(link, post.get("title", "Unknown"), headers=final_headers, single_threaded=final_headers is not None)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user