- Sonarr/Radarr history: pageSize 20 -> 10
- SABnzbd history: limit 20 -> 10
- Drop includeEpisode from Sonarr queue and history (never rendered)
- These reduce DB join overhead and response payload size
Debug logging at line 389/393 still referenced radarrMovies.data and
sonarrSeries.data which were removed in the previous commit. Updated
to use moviesMap/seriesMap built from embedded queue/history objects.
The poller was fetching the entire series and movie libraries on every
poll cycle (~9s each). Queue and history records already embed the full
series/movie object via includeSeries/includeMovie params.
Changes:
- Remove 'Sonarr Series' and 'Radarr Movies' timed fetches from poller
- Tag queue/history records with _instanceUrl in the poller instead
- Build seriesMap/moviesMap from embedded objects in dashboard
- Remove poll:sonarr-series and poll:radarr-movies cache keys
- Fix missing axios and config imports in dashboard
- Net result: ~18s saved per poll cycle, ~2 fewer API calls
- Server tracks each client's refresh rate via query param on /user-downloads
- Active clients expire after 30s of no requests
- Status panel 'Data Refresh' card shows:
- Background poll interval (or Disabled)
- Effective mode: Background if all clients >= poll rate,
Foreground (with rate) if any client is faster, Idle if no clients
- Active client list with per-user refresh rate and last-seen age
- Foreground mode shown with orange badge for visibility
- Client refresh rate sent on every dashboard request
- Each service fetch is individually timed (SABnzbd, Sonarr, Radarr, qBit)
- Status panel shows timing bar chart with ms per task and total
- Shows 'Last Poll' age that updates live
- Shows client refresh rate (1s/5s/10s/Off)
- Status panel auto-refreshes in sync with dashboard refresh cycle
- Changing refresh rate restarts the status panel refresh too
- TTL counters update live on each refresh
- New /api/dashboard/status endpoint (admin-only, 403 for non-admins)
- Returns server info (uptime, Node version, memory usage)
- Returns polling mode and interval
- Returns cache stats: entry count, total size, per-key breakdown
with item count, size in KB, and TTL remaining
- Status button in admin controls header
- Collapsible status panel with grid layout
- Responsive: single column on mobile
- Set POLL_INTERVAL=0, off, false, or disabled to disable background polling
- When disabled, data is fetched on-demand when a user opens the dashboard
- On-demand results cached for 30s so other users benefit from fresh data
- A user with a faster refresh rate keeps the cache warm for everyone
- When polling is enabled, behaviour is unchanged (default 5s)
- New poller.js polls all services on a configurable interval
- POLL_INTERVAL env var (default 5000ms / 5 seconds)
- All data stored in cache with TTL of 3x poll interval
- Dashboard endpoint now reads from cache only (no network calls)
- API responses are near-instant regardless of service count
- First poll runs immediately on server start
- SABnzbd, Sonarr, and Radarr history now fetch 20 items instead of 100
- Only recent completions are needed for the dashboard
- Reduces response payload and serialization time
- Reuse client instances so auth cookies survive across requests
- Eliminates redundant login round-trips on every dashboard refresh
- Clients still re-authenticate automatically if session expires (403)
- Add MemoryCache utility with get/set/invalidate/clear
- Cache Sonarr series, Sonarr tags, Radarr movies, Radarr tags
- 60-second TTL - first request fetches, subsequent requests served from cache
- Queue, history, and torrent data remain uncached (changes frequently)
- On cache hit, these 4 heavy API calls resolve instantly
- Detect trackedDownloadState=importPending or status=warning/error
- Extract statusMessages and errorMessage from queue records
- Display red 'Import Pending' badge on download card header
- Hover reveals tooltip with the specific issue messages
- Visible to all users (not admin-only)
- Replicate Ombi's SanitizeTagLabel logic (lowercase, replace non-alnum with hyphen, collapse, trim)
- Try exact tag match first (handles users with normal usernames)
- Fall back to sanitized comparison (handles email usernames like robcunn@live.co.uk → robcunn-live-co-uk)
- Applied to all tag matching locations
- Series title links to Sonarr series page (/series/{titleSlug})
- Movie title links to Radarr movie page (/movie/{titleSlug})
- Links open in new tab, only shown for admin users
- Instance URL preserved through data aggregation for multi-instance support
- Prefer content_path over save_path for qBittorrent torrents
- content_path is the full path to the single file or top-level
folder for multi-file torrents
- save_path is just the base download directory
- Admin users see download path (SABnzbd storage / qBittorrent save_path)
- Admin users see target path (Sonarr series folder / Radarr movie folder)
- Paths displayed in monospace font at bottom of card details
- Non-admin users unaffected (paths not sent in API response)
- Admin users (Emby IsAdministrator) see a 'Show all users' toggle
- When toggled, all tagged downloads are shown regardless of user
- Each download card shows the tagged user's name as a badge
- Non-admin users see only their own downloads (unchanged behavior)
- Backend accepts ?showAll=true query param (admin-only)
- Fix seriesMap key (use Sonarr internal id, not tvdbId)
- Fix Sonarr tag resolution (use tag map like Radarr)
- Use sourceTitle for history record matching
- Fall back to embedded movie/series objects when API timeouts
- Add includeMovie/includeSeries params to queue/history API calls
- Add coverArt field to all download responses (TMDB poster URLs)
- Add cover art display to frontend download cards
- Fix user-summary route to use instance config and tag maps