fix(requests): keep download server details out of requesters' requests - #1651
Conversation
The profile-scoped v2 request operations (createRequest, listMyRequests, getRequest, cancelRequest) gave a requester every target's download server id, kind and name, the server's own id and raw status, the routing rule and the target error, plus the request's integration kind, external fields and submission error. These are admin details. They show how the admin named their servers and routing rules, and the errors can carry a plugin's raw text, such as a server URL or a release title. mediaRequestOf now takes the viewer and fills those fields for an admin only. A requester keeps the request's state and outcome_reason and each target's quality, status and download progress. An admin still sees everything, on the profile-scoped operations and on the admin request operations. /api/v1 is frozen and unchanged. The fields were already optional, so the contract diff reports no change; their descriptions now say admins only. Refs #1646 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
* feat(requests): show download progress while a request downloads Request-router plugins can now report how far a target's downloads are (TargetStatus.progress, declared with request_router.reports_download_progress). Store it on the target in new download_* columns, and show it on the Requests page, the title page's request bar and the admin queue: a bar once the size is known and "Downloading · 43% · about 12 min left", or the phase (waiting, paused, stalled, importing, import blocked). The reconcile pass records a download's first progress. A new one-minute refresh_request_downloads task then refreshes only downloading targets that have progress, from plugins that declare it. It shares a target-write lock with reconcile, which waits for it rather than skipping, and runs within a 90-second budget. Progress writes leave the target's updated_at, the request status and its history alone; progress clears on completion or failure, when the plugin stops reporting it, or 15 minutes after its server stops answering. A target's raw external status is kept in step with its phase. The v2 API adds RequestDownload on request targets, requests and the title detail's request state, and download_progress_supported on the requests status capability. Clients poll every 30 seconds while something they show downloads. TMDB title details are cached for two minutes so title pages polling a download share one fetch. The plugin SDK is pinned to the silo-plugin-sdk PR commit until v0.19.0 is tagged. Refs #1643 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(requests): hold both reconcile locks on one session and size requests from every live target The reconcile pass took its own advisory lock and the request target write lock on two pooled connections, so with database.max_connections at 2 its first query waited forever for a third. pglock gains Lock.AcquireAlso, which takes a second key on the session already holding a lock, and Release frees every key the session holds; the reconcile pass takes both locks through it. pglock.Acquire, which only this pass used, is gone. A request's combined download progress skipped a live target that had not reported progress yet, so a 1080p and 4K request could show 90% from the 1080p copy alone. Such a target now leaves the combined size unknown, as the docs already said. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * perf(requests): index the targets the download refresh pass looks for The download refresh pass, and its idle check on every API node, select downloading targets that have progress once a minute. Without an index on that predicate each run scanned all of media_request_targets, which grows with request history. A partial index on (request_id, download_checked_at) where status = 'downloading' and download_phase is set covers only those few rows, built concurrently. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(requests): keep download server details out of requesters' requests (#1651) The profile-scoped v2 request operations (createRequest, listMyRequests, getRequest, cancelRequest) gave a requester every target's download server id, kind and name, the server's own id and raw status, the routing rule and the target error, plus the request's integration kind, external fields and submission error. These are admin details. They show how the admin named their servers and routing rules, and the errors can carry a plugin's raw text, such as a server URL or a release title. mediaRequestOf now takes the viewer and fills those fields for an admin only. A requester keeps the request's state and outcome_reason and each target's quality, status and download progress. An admin still sees everything, on the profile-scoped operations and on the admin request operations. /api/v1 is frozen and unchanged. The fields were already optional, so the contract diff reports no change; their descriptions now say admins only. Refs #1646 Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Problem
Closes #1646
Related issue: #1643
Validation tasks: reaches #1202 C1, #1204 C1 and C2, Silo-Server/silo-android#338 C2, Silo-Server/silo-android#339 C2, Silo-Server/silo-apple#328 C2 and Silo-Server/silo-apple#329 C2 (none run yet). The requester's request rows show less; nothing else changes.
Regular users' v2 request responses carry admin details. Every request target includes the download server it went to, the routing rule that sent it there, the server's own ids, and its raw status (such as
completed/importBlocked). The request itself includes submission errors, which can name servers and routing rules or carry a plugin's raw error. The Android apps print some of this in the My Requests row, for exampleRadarr • 1080p • downloading • completed/importBlocked. A requester can't act on any of it, and on a shared server it shows how the admin set up their download servers.Approach
mediaRequestOfnow takes the viewer, and it fills the download server details only for an admin:integration_id,integration_kind,instance_name,external_id,external_status,route_name,last_errorintegration_kind,external_id,external_status,last_errorA requester keeps each target's quality, status and download progress, plus the request's state and
outcome_reason. This applies to every v2 endpoint that returns a request: create, list mine, get, cancel, and the admin list and actions. Admins see everything, as before.The request-level
last_errorgoes with the rest. It is written only by failed submissions, and it can quote routing-rule and server names, raw plugin or transport errors, and instructions meant for the admin ("re-save it in admin"). A requester whose request failed still sees that it failed, and why it was declined or cancelled when it was. On the web, that means the Failed badge with no error line under it.All of these fields were already optional, so the contract check reports no change, and their descriptions now say admins only.
/api/v1is frozen and still returns them, asdocs/architecture/media-requests.mdnow states.This stacks on #1649, which adds
downloadto the same mapping.Validation
go build ./...,go vet, gofmt,make lint-changed(0 issues) andmake test-gopass.make verify-apiv2-openapi,verify-apiv2-contract(no changes),verify-apiv2-fixtures,verify-apiv2-web-typesand the committed-artifact test pass.1080p • downloading. The Apple apps read only quality, status, error and download. The web requester pages use only the error andoutcome_reason. None of them needs a change.Risks
instance_nameto requesters will see it disappear.make apiv2-fixtures-sync) after this merges.Checklist
AI Disclosure
🤖 Generated with Claude Code
Note
Hide download-server details from requester responses in v2 media request API
Makes
mediaRequestOfin requests.go viewer-aware. Requesters no longer receive server kind, server identity, external identifiers and statuses, routing names, and server-related errors at the request or target level; admins still receive all fields. All handlers (create, get, list, cancel, admin list, admin actions) now pass the acting viewer into serialization.TestRequestDownloadServerDetailsAreForAdminsandTestAdminRequestsCarryDownloadServerDetails.contracts/api/v2/fixtures/get_system_info_ok.jsoncontract digest changes; requester-visible target fields (quality, status, download progress) are unchanged.Macroscope summarized 8b109c8.