mobile ux

This commit is contained in:
Mateusz Gruszczyński
2026-05-08 18:29:56 +02:00
parent 76412284bb
commit d5d41bcec2
7 changed files with 178 additions and 15 deletions

View File

@@ -168,6 +168,7 @@ def save_preferences(data: dict, user_id: int | None = None):
port_check_enabled = data.get("port_check_enabled")
footer_items_json = data.get("footer_items_json")
title_speed_enabled = data.get("title_speed_enabled")
tracker_favicons_enabled = data.get("tracker_favicons_enabled")
with connect() as conn:
now = utcnow()
if allowed_theme:
@@ -187,6 +188,9 @@ def save_preferences(data: dict, user_id: int | None = 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))
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.
value = footer_items_json if isinstance(footer_items_json, str) else json.dumps(footer_items_json)

View File

@@ -922,6 +922,49 @@ def _call_first(c: ScgiRtorrentClient, candidates: list[tuple[str, tuple]]) -> d
raise RuntimeError("; ".join(errors))
def _tracker_domain(url: str) -> str:
raw = str(url or '').strip()
if not raw:
return ''
parsed = urlparse(raw if '://' in raw else f'http://{raw}')
host = (parsed.hostname or '').lower().strip('.')
if host.startswith('www.'):
host = host[4:]
return host
def tracker_summary(profile: dict, torrent_hashes: list[str] | None = None, limit: int = 1000) -> dict:
"""Return tracker domains grouped by torrent for the sidebar filter."""
# Note: Tracker summary is read-only and isolated from the normal torrent snapshot, so slow tracker RPC calls cannot break the main list.
hashes = [str(h or '').strip() for h in (torrent_hashes or []) if str(h or '').strip()]
if not hashes:
hashes = [t.get('hash') for t in list_torrents(profile) if t.get('hash')]
hashes = hashes[:max(1, int(limit or 1000))]
by_hash: dict[str, list[dict]] = {}
counts: dict[str, dict] = {}
errors = []
for h in hashes:
try:
items = []
seen = set()
for tr in torrent_trackers(profile, h):
url = str(tr.get('url') or '')
domain = _tracker_domain(url)
if not domain or domain in seen:
continue
seen.add(domain)
item = {'domain': domain, 'url': url}
items.append(item)
row = counts.setdefault(domain, {'domain': domain, 'url': url, 'count': 0})
row['count'] += 1
by_hash[h] = items
except Exception as exc:
errors.append({'hash': h, 'error': str(exc)})
by_hash[h] = []
trackers = sorted(counts.values(), key=lambda x: (-int(x.get('count') or 0), str(x.get('domain') or '')))
return {'hashes': by_hash, 'trackers': trackers, 'errors': errors, 'scanned': len(hashes)}
def _safe_tracker_call(c: ScgiRtorrentClient, method: str, target: str, default=None):
try:
return c.call(method, target)