queue_stopped #3

Merged
gru merged 33 commits from queue_stopped into master 2026-05-08 23:45:33 +02:00
6 changed files with 16 additions and 12 deletions
Showing only changes of commit 607d1c59c1 - Show all commits

2
.gitignore vendored
View File

@@ -34,6 +34,8 @@ storage/*
*.sqlite3-shm *.sqlite3-shm
*.sqlite3 *.sqlite3
data/* data/*
!data/tracker_favicons
data/tracker_favicons/*.ico
logs/* logs/*
todo.txt todo.txt

View File

@@ -4,7 +4,6 @@ from pathlib import Path
from ..config import BASE_DIR, USE_OFFLINE_LIBS 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_STATIC_DIR = "libs"
LIBS_DIR = BASE_DIR / "pytorrent" / "static" / LIBS_STATIC_DIR LIBS_DIR = BASE_DIR / "pytorrent" / "static" / LIBS_STATIC_DIR
BOOTSTRAP_VERSION = "5.3.3" BOOTSTRAP_VERSION = "5.3.3"
@@ -84,7 +83,6 @@ def required_offline_paths() -> list[Path]:
def missing_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] 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 = [ required_dirs = [
LIBS_DIR / f"fontawesome/{FONTAWESOME_VERSION}/webfonts", LIBS_DIR / f"fontawesome/{FONTAWESOME_VERSION}/webfonts",
LIBS_DIR / f"flag-icons/{FLAG_ICONS_VERSION}/flags/4x3", 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: def validate_offline_assets() -> None:
# Notatka: aplikacja zatrzymuje start, gdy tryb offline jest aktywny, a pliki nie są zainstalowane.
if not USE_OFFLINE_LIBS: if not USE_OFFLINE_LIBS:
return return
missing = missing_offline_paths() missing = missing_offline_paths()

View File

@@ -28,7 +28,6 @@ FONT_FAMILIES = {
} }
def bootstrap_css_url(theme: str | None) -> str: 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 from .frontend_assets import bootstrap_css_path
return bootstrap_css_path(theme) 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: 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)) 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: 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)) 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: 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)) 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: if footer_items_json is not None:
# Note: Store only JSON objects so footer visibility can be extended without schema churn. # Note: Store only JSON objects so footer visibility can be extended without schema churn.

View File

@@ -169,10 +169,17 @@ def favicon_public_url(domain: str, enabled: bool = True, create: bool = False)
favicon_path(clean, enabled=True) favicon_path(clean, enabled=True)
cached = _cached_favicon(clean) cached = _cached_favicon(clean)
now = _now_epoch() now = _now_epoch()
if not cached or now - float(cached.get("updated_epoch") or 0) >= FAVICON_CACHE_TTL_SECONDS: path = None
return "" if cached and now - float(cached.get("updated_epoch") or 0) < FAVICON_CACHE_TTL_SECONDS:
path = Path(str(cached.get("file_path") or "")) cached_path = Path(str(cached.get("file_path") or ""))
if not path.exists() or not path.is_file(): 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 "" return ""
try: try:
rel = path.resolve().relative_to(FAVICON_DIR.resolve()) rel = path.resolve().relative_to(FAVICON_DIR.resolve())

View File

@@ -239,8 +239,9 @@
function trackerFavicon(tracker){ function trackerFavicon(tracker){
const domain=typeof tracker==='string'?tracker:(tracker?.domain||''); const domain=typeof tracker==='string'?tracker:(tracker?.domain||'');
if(!trackerFaviconsEnabled || !domain) return '<i class="fa-solid fa-bullseye"></i>'; if(!trackerFaviconsEnabled || !domain) return '<i class="fa-solid fa-bullseye"></i>';
// Note: Cached favicons are served from the static/tracker_favicons symlink; the API path is only a one-time cache warmer fallback. const safeName=String(domain).toLowerCase().replace(/[^a-z0-9_.-]+/g,'_').replace(/^[._]+|[._]+$/g,'')||'tracker';
const src=(typeof tracker==='object' && tracker?.favicon_url) ? tracker.favicon_url : `/api/trackers/favicon/${encodeURIComponent(domain)}`; // 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 `<img class="tracker-favicon" src="${esc(src)}" alt="" loading="lazy" onerror="this.classList.add('d-none')"><i class="fa-solid fa-bullseye tracker-fallback-icon"></i>`; return `<img class="tracker-favicon" src="${esc(src)}" alt="" loading="lazy" onerror="this.classList.add('d-none')"><i class="fa-solid fa-bullseye tracker-fallback-icon"></i>`;
} }
function trackerFilterPlaceholder(){ function trackerFilterPlaceholder(){

View File

@@ -1 +0,0 @@
../../data/tracker_favicons

View File

@@ -0,0 +1 @@
../../data/tracker_favicons