@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) alt showAll=true dashboard -> cache : get('emby:users') alt cache miss dashboard -> ext : GET /Users (Emby) ext --> dashboard : [{ Name, ... }] dashboard -> cache : set('emby:users', map, 60s) end dashboard -> dashboard : Build embyUserMap\n(lowerName → displayName) end group SABnzbd Queue Matching loop each queue slot dashboard -> dashboard : Match title vs Sonarr queue dashboard -> dashboard : Match title vs Radarr queue dashboard -> dashboard : extractAllTags() + extractUserTag(username)\nInclude if: showAll+anyTag OR matchedUserTag\nAttach allTags, matchedUserTag\nIf showAll: tagBadges = buildTagBadges(embyUserMap) end end group SABnzbd History Matching loop each history slot dashboard -> dashboard : Match title vs Sonarr/Radarr history dashboard -> dashboard : Same tag extraction + inclusion logic 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: allTags, matchedUserTag, tagBadges end end dashboard --> browser : { user, isAdmin,\ndownloads: [...] } deactivate dashboard browser -> browser : renderDownloads() (diff-based) note right createDownloadCard() renders tag badges: - Normal: accent badge for matchedUserTag - showAll: amber badges (unmatched tags) accent badges (matched → show Emby displayName) end note deactivate browser @enduml