queue_stopped #3
@@ -6,6 +6,7 @@ import sys
|
||||
|
||||
from .db import connect, init_db, utcnow
|
||||
from .services.auth import password_hash
|
||||
from .services import tracker_cache
|
||||
|
||||
|
||||
def reset_password(username: str, password: str) -> bool:
|
||||
@@ -30,6 +31,20 @@ def reset_password(username: str, password: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def fetch_tracker_favicon(domain: str, refresh: bool = True) -> str:
|
||||
"""Note: Download or refresh one tracker favicon from CLI without starting the web server."""
|
||||
clean = tracker_cache.tracker_domain(domain)
|
||||
if not clean:
|
||||
raise ValueError("Tracker domain is required")
|
||||
init_db()
|
||||
path, mime = tracker_cache.favicon_path(clean, enabled=True, force=refresh)
|
||||
if not path:
|
||||
row = tracker_cache.favicon_cache_row(clean)
|
||||
detail = (row or {}).get("error") if row else "favicon not found"
|
||||
raise RuntimeError(str(detail or "favicon not found"))
|
||||
return f"{path} ({mime or 'unknown'})"
|
||||
|
||||
def _password_from_args(args: argparse.Namespace) -> str:
|
||||
"""Note: Allow the password to be passed as an argument or entered securely in interactive mode."""
|
||||
if args.password is not None:
|
||||
@@ -51,6 +66,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
reset.add_argument("password", nargs="?", help="New password; omit to type it interactively")
|
||||
reset.set_defaults(func=_cmd_reset_password)
|
||||
|
||||
icon = sub.add_parser("tracker-favicon", help="Download or refresh a tracker favicon cache file")
|
||||
icon.add_argument("domain", help="Tracker domain, e.g. t.pte.nu")
|
||||
icon.add_argument("--no-refresh", action="store_true", help="Use fresh cache when available")
|
||||
icon.set_defaults(func=_cmd_tracker_favicon)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@@ -64,6 +84,12 @@ def _cmd_reset_password(args: argparse.Namespace) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
def _cmd_tracker_favicon(args: argparse.Namespace) -> int:
|
||||
"""Note: Run favicon discovery from CLI and print the saved file path."""
|
||||
print(fetch_tracker_favicon(args.domain, refresh=not args.no_refresh))
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
"""Note: Main CLI entrypoint with error handling and without starting the web app."""
|
||||
parser = build_parser()
|
||||
|
||||
@@ -508,15 +508,33 @@ def trackers_summary():
|
||||
|
||||
|
||||
@bp.get("/trackers/favicon/<path:domain>")
|
||||
@bp.get("/tracker-favicon/<path:domain>")
|
||||
def tracker_favicon(domain: str):
|
||||
prefs = preferences.get_preferences()
|
||||
enabled = bool(prefs and prefs.get("tracker_favicons_enabled"))
|
||||
force = str(request.args.get("refresh") or "").lower() in {"1", "true", "yes", "force"}
|
||||
# Note: Manual refresh must work from CLI even when tracker favicons are disabled in Preferences.
|
||||
enabled = force or bool(prefs and prefs.get("tracker_favicons_enabled"))
|
||||
static_url = tracker_cache.favicon_public_url(domain, enabled=enabled, create=True, force=force)
|
||||
if static_url:
|
||||
# Note: The API only discovers/cache-warms the icon; the browser receives the file from /static/tracker_favicons/.
|
||||
return redirect(static_url, code=302)
|
||||
abort(404)
|
||||
cached = tracker_cache.favicon_cache_row(domain)
|
||||
return jsonify({
|
||||
"ok": False,
|
||||
"error": "favicon not found",
|
||||
"domain": tracker_cache.tracker_domain(domain),
|
||||
"enabled": bool(enabled),
|
||||
"cached_error": (cached or {}).get("error") if cached else None,
|
||||
}), 404
|
||||
|
||||
|
||||
@bp.get("/trackers/favicon")
|
||||
def tracker_favicon_query():
|
||||
# Note: Query-string alias makes cache warming easier from shell scripts where path routing/proxies may differ.
|
||||
domain = str(request.args.get("domain") or "").strip()
|
||||
if not domain:
|
||||
return jsonify({"ok": False, "error": "domain is required"}), 400
|
||||
return tracker_favicon(domain)
|
||||
|
||||
@bp.get("/torrent-stats")
|
||||
def torrent_stats_get():
|
||||
|
||||
@@ -300,6 +300,11 @@ def _cached_favicon(domain: str):
|
||||
return conn.execute("SELECT * FROM tracker_favicon_cache WHERE domain=?", (clean,)).fetchone()
|
||||
|
||||
|
||||
def favicon_cache_row(domain: str):
|
||||
"""Note: Expose the favicon cache row for diagnostics without duplicating SQL in routes or CLI."""
|
||||
return _cached_favicon(domain)
|
||||
|
||||
|
||||
def favicon_path(domain: str, enabled: bool = True, force: bool = False) -> tuple[Path | None, str | None]:
|
||||
clean = tracker_domain(domain)
|
||||
if not enabled or not clean:
|
||||
|
||||
Reference in New Issue
Block a user