Update ARCHITECTURE.md, bump version to 1.5.4, add CHANGELOG entry
Some checks failed
CI / Security audit (push) Failing after 23s
Build and Push Docker Image / build (push) Successful in 52s
Docs Check / Markdown lint (push) Successful in 58s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m17s
CI / Tests & coverage (push) Successful in 1m36s
Docs Check / Mermaid diagram parse check (push) Successful in 1m45s

This commit is contained in:
2026-05-19 23:45:37 +01:00
parent be791ed044
commit 49d66c07ee
3 changed files with 60 additions and 2 deletions

View File

@@ -460,6 +460,21 @@ When a browser opens `GET /api/dashboard/stream`:
The browser's native `EventSource` API handles reconnection automatically on network interruption. The browser's native `EventSource` API handles reconnection automatically on network interruption.
**SSE Payload Structure**
```javascript
{
user: string, // Username
isAdmin: boolean, // Admin flag
downloads: DownloadObject[], // Matched download objects (see Section 5.4)
downloadClients: { // Configured download clients for ordering/filtering
id: string, // Instance identifier
name: string, // Instance display name
type: string // Client type ('sabnzbd', 'qbittorrent', 'transmission', 'rtorrent')
}[]
}
```
### 5.4 Download Matching Pipeline ### 5.4 Download Matching Pipeline
For each connected user the server: For each connected user the server:
@@ -495,6 +510,14 @@ Users are matched to downloads via Sonarr/Radarr tags:
1. **Exact match** — tag label (lowercased) === username (lowercased). 1. **Exact match** — tag label (lowercased) === username (lowercased).
2. **Sanitised match** — handles Ombi tag mangling: `sanitizeTagLabel()` converts to lowercase, replaces non-alphanumeric chars with hyphens, collapses runs, trims. 2. **Sanitised match** — handles Ombi tag mangling: `sanitizeTagLabel()` converts to lowercase, replaces non-alphanumeric chars with hyphens, collapses runs, trims.
#### Client ordering and filtering
Matched download objects include `client`, `instanceId`, and `instanceName` fields. The frontend:
1. Receives a `downloadClients` array from the SSE payload with all configured clients in configuration order
2. Displays a multi-select filter allowing users to choose which clients to view
3. Sorts downloads by client order (downloads from the first configured client appear first)
4. Filters downloads to show only those from selected client instances
#### Matched download object fields #### Matched download object fields
| Field | Type | Description | | Field | Type | Description |
@@ -523,6 +546,9 @@ Users are matched to downloads via Sonarr/Radarr tags:
| `arrInstanceKey` | string/null | (Admin) API key for the *arr instance | | `arrInstanceKey` | string/null | (Admin) API key for the *arr instance |
| `arrContentId` | number/null | (Admin) `episodeId` or `movieId` for triggering a new search | | `arrContentId` | number/null | (Admin) `episodeId` or `movieId` for triggering a new search |
| `arrContentType` | `'episode'`/`'movie'`/null | (Admin) Content type for search command | | `arrContentType` | `'episode'`/`'movie'`/null | (Admin) Content type for search command |
| `client` | string | Download client type ('sabnzbd', 'qbittorrent', 'transmission', 'rtorrent') |
| `instanceId` | string | Instance identifier matching the configured client ID |
| `instanceName` | string | Instance display name from configuration |
| `addedOn` | number/null | (qBittorrent) Unix timestamp when torrent was added | | `addedOn` | number/null | (qBittorrent) Unix timestamp when torrent was added |
| `availableForUpgrade` | boolean/undefined | (History) `true` when outcome is `failed` but content is on disk | | `availableForUpgrade` | boolean/undefined | (History) `true` when outcome is `failed` but content is on disk |
@@ -683,7 +709,7 @@ stateDiagram-v2
| `handleLogin()` | Authenticate, fade login → splash → dashboard | | `handleLogin()` | Authenticate, fade login → splash → dashboard |
| `startSSE()` | Open `EventSource` to `/stream`; handle incoming data | | `startSSE()` | Open `EventSource` to `/stream`; handle incoming data |
| `stopSSE()` | Close `EventSource` and cancel reconnect timer | | `stopSSE()` | Close `EventSource` and cancel reconnect timer |
| `renderDownloads()` | Diff-based card rendering (create/update/remove) | | `renderDownloads()` | Diff-based card rendering (create/update/remove); filters by selected download clients; sorts by client order |
| `createDownloadCard()` | Build DOM for a single card; renders tag badges, import-issue badge, blocklist button | | `createDownloadCard()` | Build DOM for a single card; renders tag badges, import-issue badge, blocklist button |
| `updateDownloadCard()` | Update existing card in-place (progress, speed, etc.) | | `updateDownloadCard()` | Update existing card in-place (progress, speed, etc.) |
| `handleBlocklistSearch()` | Confirm dialog → POST `/blocklist-search` → update button state | | `handleBlocklistSearch()` | Confirm dialog → POST `/blocklist-search` → update button state |
@@ -698,6 +724,22 @@ stateDiagram-v2
- **Regular user view** — a single accent-coloured badge showing the tag label that matched the current user's username (via `matchedUserTag`). - **Regular user view** — a single accent-coloured badge showing the tag label that matched the current user's username (via `matchedUserTag`).
- **Admin `showAll` view** — all tags on the download are rendered using `tagBadges[]`: tags with no matching Emby user → amber badge (leftmost); tags matched to a known Emby user → accent badge showing the Emby display name (rightmost). - **Admin `showAll` view** — all tags on the download are rendered using `tagBadges[]`: tags with no matching Emby user → amber badge (leftmost); tags matched to a known Emby user → accent badge showing the Emby display name (rightmost).
#### Download Client Filter
The Active Downloads tab includes a multi-select dropdown filter that allows users to:
- View all download clients with their type displayed as "Client Name (type)"
- Select multiple clients to filter the downloads list
- Use "Select All" / "Deselect All" buttons for bulk operations
- Persist selection across sessions via localStorage
Downloads are sorted by client order (matching the configuration order) and filtered by the selected client IDs.
Related functions:
- `initDownloadClientFilter()` — Sets up dropdown toggle, click-outside handler, Select/Deselect All buttons
- `updateDownloadClientFilter()` — Populates checkbox list with client name + type badges
- `toggleClientSelection()` — Updates selection array and localStorage
- `updateSelectedCountDisplay()` — Updates button text to show "All clients" / "1 selected" / "N selected"
--- ---
## 8. Directory Structure ## 8. Directory Structure

View File

@@ -6,6 +6,22 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
--- ---
## [1.5.4] - 2026-05-19
### Added
- **Multi-select download client filter** — Active Downloads tab now includes a dropdown filter that allows users to select multiple download clients. Shows client name and type (e.g., "Main (qbitt)"). Includes Select All / Deselect All buttons. Selection persists across sessions via localStorage.
- **Download client ordering** — Downloads are now sorted by client order (matching the configuration order). Downloads from the first configured client appear first.
- **Client metadata in downloads** — Download objects now include `client`, `instanceId`, and `instanceName` fields for client identification and filtering.
- **SSE payload extension** — SSE stream now includes `downloadClients` array with all configured clients for UI ordering/filtering.
- **Automatic migration** — Existing single-select filter selection automatically migrates to new multi-select format on first load.
### Fixed
- **SABnzbd size and speed display** — Fixed SABnzbd downloads showing undefined size and speed values. Now correctly uses `slot.mb` for size calculation and `slot.kbpersec` for per-slot speed from cached data.
---
## [1.5.3] - 2026-05-19 ## [1.5.3] - 2026-05-19
### Fixed ### Fixed

View File

@@ -1,6 +1,6 @@
{ {
"name": "sofarr", "name": "sofarr",
"version": "1.5.3", "version": "1.5.4",
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish", "description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
"main": "server/index.js", "main": "server/index.js",
"scripts": { "scripts": {