@startuml seq-dashboard !theme plain title sofarr — Dashboard Request Sequence actor User as user participant "Browser\n(app.js)" as browser participant "Express\n/api/dashboard" as dashboard participant "MemoryCache" as cache participant "Poller" as poller participant "External\nServices" as ext == Periodic Refresh (or Initial Load) == user -> browser : (auto-refresh fires) activate browser browser -> dashboard : GET /api/dashboard/user-downloads\n?refreshRate=5000&showAll=false activate dashboard dashboard -> dashboard : Parse emby_user cookie\nExtract username, isAdmin dashboard -> dashboard : Track client refresh rate\nin activeClients Map alt Polling disabled AND cache empty dashboard -> poller : pollAllServices() activate poller poller -> ext : Parallel API calls\n(SAB, Sonarr, Radarr, qBit) ext --> poller : Raw data poller -> cache : set poll:* keys\n(TTL = 30s) deactivate poller end dashboard -> cache : get('poll:sab-queue') cache --> dashboard : { slots, status, speed } dashboard -> cache : get('poll:sab-history') cache --> dashboard : { slots } dashboard -> cache : get('poll:sonarr-tags') cache --> dashboard : [{ instance, data }] dashboard -> cache : get('poll:sonarr-queue') cache --> dashboard : { records } (with embedded series) dashboard -> cache : get('poll:sonarr-history') cache --> dashboard : { records } dashboard -> cache : get('poll:radarr-queue') cache --> dashboard : { records } (with embedded movie) dashboard -> cache : get('poll:radarr-history') cache --> dashboard : { records } dashboard -> cache : get('poll:radarr-tags') cache --> dashboard : [{id, label}] dashboard -> cache : get('poll:qbittorrent') cache --> dashboard : [torrent, ...] dashboard -> dashboard : Build seriesMap from\nSonarr queue records dashboard -> dashboard : Build moviesMap from\nRadarr queue records dashboard -> dashboard : Build tag maps\n(id → label) group SABnzbd Queue Matching loop each queue slot dashboard -> dashboard : Match title vs Sonarr queue dashboard -> dashboard : Match title vs Radarr queue dashboard -> dashboard : Resolve series/movie\n→ extract user tag\n→ filter by username end end group SABnzbd History Matching loop each history slot dashboard -> dashboard : Match title vs Sonarr history dashboard -> dashboard : Match title vs Radarr history dashboard -> dashboard : Resolve via seriesMap/moviesMap\n→ extract user tag\n→ filter end end group qBittorrent Matching loop each torrent dashboard -> dashboard : 1. Match vs Sonarr queue dashboard -> dashboard : 2. Match vs Radarr queue dashboard -> dashboard : 3. Match vs Sonarr history dashboard -> dashboard : 4. Match vs Radarr history dashboard -> dashboard : mapTorrentToDownload()\n→ enrich with series/movie info end end dashboard --> browser : { user, isAdmin,\ndownloads: [...] } deactivate dashboard browser -> browser : renderDownloads()\n(diff-based update) deactivate browser @enduml