diff --git a/.gitignore b/.gitignore
index 0f0aeca..68b35d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,8 @@ storage/*
*.sqlite3-shm
*.sqlite3
data/*
+!data/tracker_favicons
+data/tracker_favicons/*.ico
logs/*
todo.txt
diff --git a/pytorrent/services/frontend_assets.py b/pytorrent/services/frontend_assets.py
index 97ce593..4a3fc51 100644
--- a/pytorrent/services/frontend_assets.py
+++ b/pytorrent/services/frontend_assets.py
@@ -4,7 +4,6 @@ from pathlib import Path
from ..config import BASE_DIR, USE_OFFLINE_LIBS
-# Notatka: jeden manifest utrzymuje spójne adresy CDN i ścieżki lokalne dla trybu offline.
LIBS_STATIC_DIR = "libs"
LIBS_DIR = BASE_DIR / "pytorrent" / "static" / LIBS_STATIC_DIR
BOOTSTRAP_VERSION = "5.3.3"
@@ -84,7 +83,6 @@ def required_offline_paths() -> list[Path]:
def missing_offline_paths() -> list[Path]:
missing = [path for path in required_offline_paths() if not path.is_file() or path.stat().st_size <= 0]
- # Notatka: sprawdzane są też zasoby referencjonowane przez CSS, np. fonty ikon i pliki flag.
required_dirs = [
LIBS_DIR / f"fontawesome/{FONTAWESOME_VERSION}/webfonts",
LIBS_DIR / f"flag-icons/{FLAG_ICONS_VERSION}/flags/4x3",
@@ -97,7 +95,6 @@ def missing_offline_paths() -> list[Path]:
def validate_offline_assets() -> None:
- # Notatka: aplikacja zatrzymuje start, gdy tryb offline jest aktywny, a pliki nie są zainstalowane.
if not USE_OFFLINE_LIBS:
return
missing = missing_offline_paths()
diff --git a/pytorrent/services/preferences.py b/pytorrent/services/preferences.py
index 05224f5..1ccd109 100644
--- a/pytorrent/services/preferences.py
+++ b/pytorrent/services/preferences.py
@@ -28,7 +28,6 @@ FONT_FAMILIES = {
}
def bootstrap_css_url(theme: str | None) -> str:
- # Notatka: zachowana funkcja zwraca aktualny adres motywu, ale źródło wybiera konfiguracja offline.
from .frontend_assets import bootstrap_css_path
return bootstrap_css_path(theme)
@@ -186,10 +185,8 @@ def save_preferences(data: dict, user_id: int | None = None):
if port_check_enabled is not None:
conn.execute("UPDATE user_preferences SET port_check_enabled=?, updated_at=? WHERE user_id=?", (1 if port_check_enabled else 0, now, user_id))
if title_speed_enabled is not None:
- # Notatka: preferencja steruje wyświetlaniem bieżącego DL/UL w tytule karty przeglądarki.
conn.execute("UPDATE user_preferences SET title_speed_enabled=?, updated_at=? WHERE user_id=?", (1 if title_speed_enabled else 0, now, user_id))
if tracker_favicons_enabled is not None:
- # Note: Enables optional tracker favicon display without changing tracker filtering itself.
conn.execute("UPDATE user_preferences SET tracker_favicons_enabled=?, updated_at=? WHERE user_id=?", (1 if tracker_favicons_enabled else 0, now, user_id))
if footer_items_json is not None:
# Note: Store only JSON objects so footer visibility can be extended without schema churn.
diff --git a/pytorrent/services/tracker_cache.py b/pytorrent/services/tracker_cache.py
index c919461..7ca6160 100644
--- a/pytorrent/services/tracker_cache.py
+++ b/pytorrent/services/tracker_cache.py
@@ -169,10 +169,17 @@ def favicon_public_url(domain: str, enabled: bool = True, create: bool = False)
favicon_path(clean, enabled=True)
cached = _cached_favicon(clean)
now = _now_epoch()
- if not cached or now - float(cached.get("updated_epoch") or 0) >= FAVICON_CACHE_TTL_SECONDS:
- return ""
- path = Path(str(cached.get("file_path") or ""))
- if not path.exists() or not path.is_file():
+ path = None
+ if cached and now - float(cached.get("updated_epoch") or 0) < FAVICON_CACHE_TTL_SECONDS:
+ cached_path = Path(str(cached.get("file_path") or ""))
+ if cached_path.exists() and cached_path.is_file():
+ path = cached_path
+ if path is None:
+ # Note: Existing symlinked .ico files are still linked directly even when the DB favicon row is missing or stale.
+ direct_path = FAVICON_DIR / f"{_safe_filename(clean)}.ico"
+ if direct_path.exists() and direct_path.is_file():
+ path = direct_path
+ if path is None:
return ""
try:
rel = path.resolve().relative_to(FAVICON_DIR.resolve())
diff --git a/pytorrent/static/app.js b/pytorrent/static/app.js
index 66733bf..541cdc0 100644
--- a/pytorrent/static/app.js
+++ b/pytorrent/static/app.js
@@ -239,8 +239,9 @@
function trackerFavicon(tracker){
const domain=typeof tracker==='string'?tracker:(tracker?.domain||'');
if(!trackerFaviconsEnabled || !domain) return '';
- // Note: Cached favicons are served from the static/tracker_favicons symlink; the API path is only a one-time cache warmer fallback.
- const src=(typeof tracker==='object' && tracker?.favicon_url) ? tracker.favicon_url : `/api/trackers/favicon/${encodeURIComponent(domain)}`;
+ const safeName=String(domain).toLowerCase().replace(/[^a-z0-9_.-]+/g,'_').replace(/^[._]+|[._]+$/g,'')||'tracker';
+ // Note: Tracker favicon links are direct static URLs matching the tracker_favicons symlink.
+ const src=(typeof tracker==='object' && tracker?.favicon_url) ? tracker.favicon_url : `/static/tracker_favicons/${encodeURIComponent(safeName)}.ico`;
return `
`;
}
function trackerFilterPlaceholder(){
diff --git a/pytorrent/static/tracker_favicons b/pytorrent/static/tracker_favicons
deleted file mode 100755
index d4b544b..0000000
--- a/pytorrent/static/tracker_favicons
+++ /dev/null
@@ -1 +0,0 @@
-../../data/tracker_favicons
\ No newline at end of file
diff --git a/pytorrent/static/tracker_favicons b/pytorrent/static/tracker_favicons
new file mode 120000
index 0000000..d4b544b
--- /dev/null
+++ b/pytorrent/static/tracker_favicons
@@ -0,0 +1 @@
+../../data/tracker_favicons
\ No newline at end of file