This commit is contained in:
Mateusz Gruszczyński
2026-05-08 19:59:38 +02:00
parent 3eee5be37a
commit 96e17d4b63
3 changed files with 50 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import json
import mimetypes
import re
import time
import threading
import urllib.error
import urllib.parse
import urllib.request
@@ -18,6 +19,8 @@ FAVICON_CACHE_TTL_SECONDS = 7 * 24 * 60 * 60
TRACKER_SCAN_LIMIT = 80
FAVICON_DIR = BASE_DIR / "data" / "tracker_favicons"
PUBLIC_FAVICON_BASE = "/static/tracker_favicons"
_TRACKER_SCAN_LOCKS: dict[int, threading.Lock] = {}
_TRACKER_SCAN_LOCKS_GUARD = threading.Lock()
class _IconParser(HTMLParser):
@@ -159,6 +162,40 @@ def summary(profile: dict, hashes: list[str], loader, scan_limit: int = TRACKER_
def _scan_lock(profile_id: int) -> threading.Lock:
with _TRACKER_SCAN_LOCKS_GUARD:
if profile_id not in _TRACKER_SCAN_LOCKS:
_TRACKER_SCAN_LOCKS[profile_id] = threading.Lock()
return _TRACKER_SCAN_LOCKS[profile_id]
def warm_summary_cache(profile: dict, hashes: list[str], loader, batch_size: int = TRACKER_SCAN_LIMIT) -> bool:
"""Start a non-blocking tracker cache warmup for large libraries."""
# Note: Tracker cache warming runs in one background thread per profile, so F5 returns cached data immediately instead of waiting for rTorrent scans.
profile_id = int(profile.get("id") or 0)
clean_hashes = [str(h or "").strip() for h in hashes if str(h or "").strip()]
if not profile_id or not clean_hashes:
return False
lock = _scan_lock(profile_id)
if lock.locked():
return False
def _worker():
if not lock.acquire(blocking=False):
return
try:
while True:
result = summary(profile, clean_hashes, loader, scan_limit=max(1, int(batch_size or TRACKER_SCAN_LIMIT)), include_favicons=False)
if int(result.get("pending") or 0) <= 0 or int(result.get("scanned_now") or 0) <= 0:
break
time.sleep(0.05)
finally:
lock.release()
threading.Thread(target=_worker, name=f"tracker-cache-warm-{profile_id}", daemon=True).start()
return True
def favicon_public_url(domain: str, enabled: bool = True, create: bool = False, force: bool = False) -> str:
"""Return the static URL for a cached tracker favicon, optionally creating or refreshing it first."""
# Note: Favicon files stay in data/tracker_favicons, but the browser loads them via the static/tracker_favicons symlink.