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