diff --git a/CHANGELOG.md b/CHANGELOG.md index c59b93c..66263f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - History records are now deduplicated server-side before being sent to the client — only the most relevant record per content item per instance is returned. - Import-issue badge tooltip now uses the themed `var(--surface)` / `var(--text-primary)` / `var(--border)` CSS variables, matching the episode and toggle tooltip style. - Poller now stores `_instanceKey` on Sonarr/Radarr queue records in the cache, enabling the backend to look up API credentials for blocklist operations without an additional configuration lookup. +- **Blocklist & Search button** — now available on all admin downloads (not just those with import issues), and also available to non-admin users when: import issues are present, OR (for qBittorrent torrents only) the download is more than 1 hour old AND has less than 100% availability. +- **Download object** — added `canBlocklist` boolean field to indicate whether the current user can blocklist a given download. +- **qBittorrent torrent data** — added `addedOn` timestamp field to enable age-based blocklist eligibility checks. --- diff --git a/README.md b/README.md index 398402d..840c161 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ sofarr polls all configured services in the background and caches the results. D - `GET /api/dashboard/user-summary` — Per-user download counts (admin) - `GET /api/dashboard/status` — Server / polling / cache status (admin) - `GET /api/dashboard/cover-art` — Proxied cover art image -- `POST /api/dashboard/blocklist-search` — Blocklist a release and trigger a new search (admin) +- `POST /api/dashboard/blocklist-search` — Blocklist a release and trigger a new search (admin or non-admin with eligibility: import issues OR torrent >1h old AND availability<100%) ### History - `GET /api/history/recent` — Recently completed downloads from Sonarr/Radarr history diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index ed728a2..f47c065 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -314,7 +314,7 @@ For each connected user the server: | See download/target paths | ✗ | ✓ | | See Sonarr/Radarr links | ✗ | ✓ | | View status panel | ✗ | ✓ | -| Blocklist & search (import-pending) | ✗ | ✓ | +| Blocklist & search | ✓ (when import issues OR torrent >1h old AND availability<100%) | ✓ (all downloads) | ### Tag Matching @@ -415,6 +415,7 @@ Each matched download produces an object with: | `tagBadges` | `{label, matchedUser}[]` / undefined | (Admin `showAll` only) Each tag classified against full Emby user list | | `importIssues` | string[] / null | Import warning/error messages | | `availableForUpgrade` | boolean / undefined | (History) `true` when outcome is `failed` but the content is already on disk (failed upgrade attempt) | +| `canBlocklist` | boolean | `true` if the current user can blocklist this download (admin: always; non-admin: when import issues OR torrent >1h old AND availability<100%) | | `downloadPath` | string / null | (Admin) Download client path | | `targetPath` | string / null | (Admin) *arr target path | | `arrLink` | string / null | (Admin) Link to *arr web UI | @@ -424,6 +425,7 @@ Each matched download produces an object with: | `arrInstanceKey` | string / null | (Admin, import-pending only) API key for the *arr instance | | `arrContentId` | number / null | (Admin, import-pending only) `episodeId` (Sonarr) or `movieId` (Radarr) for triggering a new search | | `arrContentType` | `'episode'`/`'movie'` / null | (Admin, import-pending only) Content type for the search command | +| `addedOn` | number / null | (qBittorrent only) Unix timestamp when the torrent was added, used for age-based blocklist eligibility | --- @@ -604,7 +606,9 @@ Admin-only per-user download counts (fetches live from APIs, not cached). ### `POST /api/dashboard/blocklist-search` -Admin-only. Removes a Sonarr/Radarr queue item with `blocklist=true` (preventing the same release being grabbed again), then immediately triggers an `EpisodeSearch` or `MoviesSearch` command. +Removes a Sonarr/Radarr queue item with `blocklist=true` (preventing the same release being grabbed again), then immediately triggers an `EpisodeSearch` or `MoviesSearch` command. + +**Access:** Admin users can blocklist any download. Non-admin users can only blocklist downloads that meet specific eligibility criteria: import issues are present, OR (for qBittorrent torrents only) the download is more than 1 hour old AND has less than 100% availability. The frontend only shows the button when the user is eligible. Requires CSRF token (`X-CSRF-Token` header). @@ -633,7 +637,7 @@ Requires CSRF token (`X-CSRF-Token` header). **Response (400):** Missing or invalid fields. -**Response (403):** Non-admin user. +**Response (403):** Non-admin user attempting to blocklist without meeting eligibility criteria (no import issues and not an eligible torrent). **Response (502):** Upstream *arr call failed.