favicons
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user