feat: endpoint to trigger directory scans from external tools - #1559
Conversation
Add POST /api/dir-scan/webhook/scan endpoint that accepts a file/directory path and triggers a scan on the best matching configured directory using longest-prefix matching. Uses query-param API key auth (same pattern as cross-seed webhook) for easy integration with *arr custom scripts. https://claude.ai/code/session_01VHvyR5hk8D68Wd2nK3nupk
Document the new POST /api/dir-scan/webhook/scan endpoint with usage examples, response codes, and a sample Sonarr/Radarr custom script. https://claude.ai/code/session_01VHvyR5hk8D68Wd2nK3nupk
The webhook endpoint now natively parses Sonarr (series.path), Radarr (movie.folderPath), Lidarr (artist.path), and Readarr (author.path) webhook payloads. No custom scripts needed — just add a Webhook connection in the *arr app pointing at the endpoint. Also rewrites docs to show the straightforward Webhook connection setup instead of a custom script approach. https://claude.ai/code/session_01VHvyR5hk8D68Wd2nK3nupk
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a new POST webhook endpoint /api/dir-scan/webhook/scan to trigger directory scans from Sonarr/Radarr/Lidarr/Readarr or simple {"path": "..."} payloads. The handler extracts and normalizes a path, selects the best matching configured directory by longest-prefix, starts a manual scan, and returns structured responses; includes OpenAPI, route, tests, and docs. Changes
Sequence DiagramsequenceDiagram
participant Client as Webhook Client
participant Server as HTTP Server
participant Auth as Auth Middleware
participant Handler as DirScanHandler
participant Matcher as Path Matcher
participant Scanner as Dir Scanner
Client->>Server: POST /api/dir-scan/webhook/scan (API key & JSON)
Server->>Auth: Validate API key & auth
Auth-->>Server: OK / Reject
Server->>Handler: WebhookTriggerScan(request)
Handler->>Handler: Decode JSON & resolvedPath()
Handler->>Matcher: Find best matching directory (longest-prefix)
Matcher-->>Handler: directoryId or none
alt Directory matched
Handler->>Scanner: Start manual scan(directoryId)
Scanner-->>Handler: runId
Handler-->>Client: 202 Accepted (runId, directoryId, directoryPath)
else No match
Handler-->>Client: 404 Not Found
else Conflict (scan active)
Handler-->>Client: 409 Conflict
else Invalid payload
Handler-->>Client: 400 Bad Request
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
internal/api/handlers/dirscan.go (1)
742-745: Use a typed response payload instead ofmap[string]any.This response has a fixed schema, so a small struct keeps the contract explicit and aligned with the repo's Go guideline.
Suggested shape
type webhookTriggerScanResponse struct { RunID int64 `json:"runId"` DirectoryID int `json:"directoryId"` }- RespondJSON(w, http.StatusAccepted, map[string]any{ - "runId": runID, - "directoryId": bestMatch.ID, - }) + RespondJSON(w, http.StatusAccepted, webhookTriggerScanResponse{ + RunID: runID, + DirectoryID: bestMatch.ID, + })As per coding guidelines, "Avoid
map[string]interface{}— use proper structs in Go."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/handlers/dirscan.go` around lines 742 - 745, Replace the map[string]any payload used in the RespondJSON call with a typed struct: define a webhookTriggerScanResponse struct (with RunID int64 `json:"runId"` and DirectoryID int `json:"directoryId"`) near the handler, construct an instance using runID and bestMatch.ID, and pass that instance to RespondJSON in the handler where RespondJSON(w, http.StatusAccepted, ...) is called (look for the RespondJSON invocation in the dirscan handler).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@documentation/docs/features/cross-seed/dir-scan.md`:
- Around line 271-277: The response table is missing the 400 case; update the
"Response codes" table to add a `400` | Bad Request row explaining that the
handler returns 400 for malformed JSON payloads and for requests that do not
include any supported path field (i.e., invalid/missing path in the payload), so
clients see this alongside the existing `202`, `404`, and `409` entries.
- Around line 242-244: Add language identifiers to the fenced code blocks
containing the POST call and the example URL: change the fence before "POST
/api/dir-scan/webhook/scan?apikey=YOUR_API_KEY" to ```http and change the fence
before "http://your-qui-host:7476/api/dir-scan/webhook/scan?apikey=YOUR_API_KEY"
to ```text (also apply the same fix to the other occurrence of the URL block
noted in the comment); this will satisfy markdownlint MD040.
- Around line 240-246: Update the documentation for the POST
/api/dir-scan/webhook/scan webhook to explicitly state that the matching logic
compares the incoming *arr webhook path against the Dir Scan "Directory Path"
configured in qui (not against any qBittorrent path prefixes), and note that in
split-mount setups the *arr service must expose the library using the same path
as qui for the webhook to match; apply the same clarification to the other
referenced paragraph(s) around lines 267-269.
In `@internal/api/handlers/dirscan.go`:
- Around line 714-716: The current containment check (using filepath.Clean on
dir.Path and strings.HasPrefix(cleanPath, dirPath+string(filepath.Separator)))
fails for filesystem roots because it produces a doubled separator; replace this
prefix logic with a filepath.Rel-based containment test: compute rel, err :=
filepath.Rel(dirPath, cleanPath) and treat the path as contained when err == nil
and rel != ".." and !strings.HasPrefix(rel, ".."+string(filepath.Separator));
keep the existing equality check (cleanPath == dirPath) or simply include that
case by accepting rel == "." as contained; update the condition where cleanPath
and dirPath are compared to use this Rel-based logic and retain use of
filepath.Clean on dir.Path.
---
Nitpick comments:
In `@internal/api/handlers/dirscan.go`:
- Around line 742-745: Replace the map[string]any payload used in the
RespondJSON call with a typed struct: define a webhookTriggerScanResponse struct
(with RunID int64 `json:"runId"` and DirectoryID int `json:"directoryId"`) near
the handler, construct an instance using runID and bestMatch.ID, and pass that
instance to RespondJSON in the handler where RespondJSON(w, http.StatusAccepted,
...) is called (look for the RespondJSON invocation in the dirscan handler).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 78537da8-c67c-4874-a25c-2429ed448dd7
📒 Files selected for processing (3)
documentation/docs/features/cross-seed/dir-scan.mdinternal/api/handlers/dirscan.gointernal/api/server.go
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
internal/api/handlers/dirscan.go (1)
754-757: Return a typed response here instead of an untyped map.This payload shape is fixed, so a struct keeps the JSON contract compiler-checked and aligned with the documented response.
♻️ Suggested change
- RespondJSON(w, http.StatusAccepted, map[string]any{ - "runId": runID, - "directoryId": bestMatch.ID, - }) + RespondJSON(w, http.StatusAccepted, struct { + RunID int64 `json:"runId"` + DirectoryID int `json:"directoryId"` + }{ + RunID: runID, + DirectoryID: bestMatch.ID, + })As per coding guidelines, "Avoid
map[string]interface{}— use proper structs in Go."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/handlers/dirscan.go` around lines 754 - 757, The response currently uses an untyped map in the RespondJSON call; replace it with a concrete response struct (e.g., DirectoryScanResponse or StartScanResponse) that has fields RunID string `json:"runId"` and DirectoryID string `json:"directoryId"`, populate it with the existing runID and bestMatch.ID values, and pass that struct to RespondJSON so the payload shape is compiler-checked (update any nearby type declarations or exports as needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@AGENTS.md`:
- Line 32: The test guidance was weakened by changing the Makefile test command
flags from "-race -count=3 -v" to "-race -count=1 -v"; revert or update the
AGENTS.md entry for the "make test" instruction so it explicitly uses "-race
-count=3 -v" (and any other occurrences of "make test" or the string "-count=1")
to enforce the project's policy of running Go tests with race detection and
three repetitions.
In `@documentation/docs/features/cross-seed/dir-scan.md`:
- Around line 273-280: The response table in dir-scan.md is missing the `500`
case; update the "Response codes" table to add a new row for status code `500`
with a clear meaning such as "Internal server error — scan could not be started
due to an internal failure" so the webhook contract documents the full error
surface; edit the table in documentation/docs/features/cross-seed/dir-scan.md
(the "Response codes" section) and insert the `| `500` | Internal server error —
scan could not be started due to an internal failure |` row.
In `@internal/web/swagger/openapi.yaml`:
- Around line 3342-3347: The OpenAPI response schema for the 202 on this
endpoint currently references DirScanTriggerResponse but the webhook actually
returns both runId and directoryId; update the components schema
(DirScanTriggerResponse) or replace the $ref so the response model includes both
runId and directoryId (with correct types and descriptions) to match the webhook
payload so generated client code will include directoryId alongside runId.
- Around line 3313-3340: Update the request schema in
internal/web/swagger/openapi.yaml to reflect the handler's validation by
replacing the permissive object with a oneOf that enforces at least one path
source: add three/four branches for (1) direct { path } with required: [path],
(2) Sonarr-style { series: { path } } with required: [series] and nested
required: [path], (3) Radarr-style { movie: { folderPath } } with required:
[movie] and nested required: [folderPath], and (4) other arr payloads like {
artist: { path } } and { author: { path } } each with the top-level required
property and nested required: [path]; this models the handler which returns 400
"Could not determine path" if none present. Also update the
DirScanTriggerResponse schema to include directoryId as an integer property and
mark both runId and directoryId as required so the documented response matches
the actual handler output.
---
Nitpick comments:
In `@internal/api/handlers/dirscan.go`:
- Around line 754-757: The response currently uses an untyped map in the
RespondJSON call; replace it with a concrete response struct (e.g.,
DirectoryScanResponse or StartScanResponse) that has fields RunID string
`json:"runId"` and DirectoryID string `json:"directoryId"`, populate it with the
existing runID and bestMatch.ID values, and pass that struct to RespondJSON so
the payload shape is compiler-checked (update any nearby type declarations or
exports as needed).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8073cc62-8485-4938-af31-81ed67260a07
📒 Files selected for processing (5)
AGENTS.mddocumentation/docs/features/cross-seed/dir-scan.mdinternal/api/handlers/dirscan.gointernal/api/handlers/dirscan_webhook_test.gointernal/web/swagger/openapi.yaml
|
|
||
| # Testing | ||
| make test # go test -race -count=3 -v ./... | ||
| make test # go test -race -count=1 -v ./... |
There was a problem hiding this comment.
Keep the repo test guidance at -count=3.
This change weakens the project's stated Go test policy and makes it easier to miss flakes or race-dependent failures that only show up on repeated runs.
As per coding guidelines, "Always use -race and -count=3 flags when running Go tests to catch race conditions."
Also applies to: 88-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` at line 32, The test guidance was weakened by changing the
Makefile test command flags from "-race -count=3 -v" to "-race -count=1 -v";
revert or update the AGENTS.md entry for the "make test" instruction so it
explicitly uses "-race -count=3 -v" (and any other occurrences of "make test" or
the string "-count=1") to enforce the project's policy of running Go tests with
race detection and three repetitions.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/web/swagger/openapi.yaml (1)
7589-7601:⚠️ Potential issue | 🟠 MajorFix the manual trigger response to match its OpenAPI contract.
The manual trigger handler at
TriggerScan(line 371) returns only{"runId": runID}, but both endpoints (/api/dir-scan/directories/{directoryID}/scanand/api/dir-scan/webhook/scan) share theDirScanTriggerResponseschema which now requires bothrunIdanddirectoryId. The webhook handler correctly returns both fields. Either updateTriggerScanto return{runId, directoryId}or give each endpoint its own response schema.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/web/swagger/openapi.yaml` around lines 7589 - 7601, TriggerScan currently returns only {"runId": runID} but the shared OpenAPI schema DirScanTriggerResponse requires both runId and directoryId; update the TriggerScan handler to return an object including both runId and the matched directoryId (the same shape returned by the webhook handler) so the response matches DirScanTriggerResponse for the endpoints /api/dir-scan/directories/{directoryID}/scan and /api/dir-scan/webhook/scan; alternatively, if you prefer separate contracts, create a new response schema for the manual TriggerScan endpoint and update its OpenAPI reference accordingly.
🧹 Nitpick comments (1)
documentation/docs/features/cross-seed/dir-scan.md (1)
273-281: Show the202body here too.The table lists the status codes, but external callers still need the actual success payload (
runIdanddirectoryId) to correlate the scan they just triggered. A tiny JSON example next to202would make this section self-contained.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@documentation/docs/features/cross-seed/dir-scan.md` around lines 273 - 281, Add a small JSON success example for the `202` entry under the "Response codes" table so callers see the response body used to correlate a started scan; update the `202` row to include a tiny example payload showing {"runId": "<uuid>", "directoryId": "<id>"} (or similar names used by the API) next to the description for the `202` status in the "Response codes" section so consumers know the exact fields to expect.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@internal/web/swagger/openapi.yaml`:
- Around line 7589-7601: TriggerScan currently returns only {"runId": runID} but
the shared OpenAPI schema DirScanTriggerResponse requires both runId and
directoryId; update the TriggerScan handler to return an object including both
runId and the matched directoryId (the same shape returned by the webhook
handler) so the response matches DirScanTriggerResponse for the endpoints
/api/dir-scan/directories/{directoryID}/scan and /api/dir-scan/webhook/scan;
alternatively, if you prefer separate contracts, create a new response schema
for the manual TriggerScan endpoint and update its OpenAPI reference
accordingly.
---
Nitpick comments:
In `@documentation/docs/features/cross-seed/dir-scan.md`:
- Around line 273-281: Add a small JSON success example for the `202` entry
under the "Response codes" table so callers see the response body used to
correlate a started scan; update the `202` row to include a tiny example payload
showing {"runId": "<uuid>", "directoryId": "<id>"} (or similar names used by the
API) next to the description for the `202` status in the "Response codes"
section so consumers know the exact fields to expect.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 2bf3d240-4b1f-470c-b510-b8721ce153ce
📒 Files selected for processing (3)
documentation/docs/features/cross-seed/dir-scan.mdinternal/api/handlers/dirscan.gointernal/web/swagger/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/api/handlers/dirscan.go
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/api/handlers/dirscan.go`:
- Around line 371-375: The follow-up GetDirectory call after StartManualScan can
turn a successful scan start into a 500; instead, reuse the directory object you
already loaded during validation or load the directory before calling
StartManualScan so you never drop the successful start due to a later read
error. Concretely, in the dirscan handler replace the post-StartManualScan
GetDirectory usage with the previously validated directory variable (from your
validation step) or move the GetDirectory(dirID) call so it occurs before
calling h.service.StartManualScan; ensure StartManualScan remains responsible
for creating the run and only return a 500 if StartManualScan itself fails,
while returning success to the client when StartManualScan succeeds even if a
subsequent read would fail.
In `@internal/web/swagger/openapi.yaml`:
- Around line 3302-3308: The operation currently documents the apikey as a
normal optional query parameter named "apikey" but should model it as a security
alternative to the global SessionAuth; remove the regular parameter and add a
query apiKey security scheme (type: apiKey, in: query, name: apikey or apiKey)
in the OpenAPI components, then set this operation's security array to allow
either the existing SessionAuth or the new query ApiKey security scheme (e.g.,
security: - SessionAuth: [] - ApiKeyQuery: []); update references to the
parameter in this operation to use the security scheme and run make test-openapi
to validate the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 5d5493a1-3daf-4598-8d3b-899d2f02de23
📒 Files selected for processing (4)
documentation/docs/features/cross-seed/dir-scan.mdinternal/api/handlers/dirscan.gointernal/api/handlers/dirscan_webhook_test.gointernal/web/swagger/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/api/handlers/dirscan_webhook_test.go
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.14.1` → `v1.15.0` | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/2) for more information. --- ### Release Notes <details> <summary>autobrr/qui (ghcr.io/autobrr/qui)</summary> ### [`v1.15.0`](https://github.com/autobrr/qui/releases/tag/v1.15.0) [Compare Source](autobrr/qui@v1.14.1...v1.15.0) #### Changelog ##### Breaking change CORS is disabled by default; enable by setting QUI\_\_CORS\_ALLOWED\_ORIGINS with explicit origins (http(s)://host\[:port]). See <https://getqui.com/docs/advanced/sso-proxy-cors> ##### New Features - [`93786a2`](autobrr/qui@93786a2): feat(automations): add configurable processing priority/sorting ([#​1235](autobrr/qui#1235)) ([@​Oscariremma](https://github.com/Oscariremma)) - [`45eaf1f`](autobrr/qui@45eaf1f): feat(database): add postgres and sqlite migration CLI ([#​1530](autobrr/qui#1530)) ([@​s0up4200](https://github.com/s0up4200)) - [`430f5d1`](autobrr/qui@430f5d1): feat(torrents): mediaInfo dialog ([#​1537](autobrr/qui#1537)) ([@​s0up4200](https://github.com/s0up4200)) - [`8eb8903`](autobrr/qui@8eb8903): feat(web): Add persistence to unified instance filter in sidebar ([#​1560](autobrr/qui#1560)) ([@​drtaru](https://github.com/drtaru)) - [`7aadde7`](autobrr/qui@7aadde7): feat(web): add path autocomplete to set location dialog ([#​1432](autobrr/qui#1432)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`077f32c`](autobrr/qui@077f32c): feat: add mediainfo api endpoint ([#​1545](autobrr/qui#1545)) ([@​Audionut](https://github.com/Audionut)) - [`99cf695`](autobrr/qui@99cf695): feat: endpoint to trigger directory scans from external tools ([#​1559](autobrr/qui#1559)) ([@​s0up4200](https://github.com/s0up4200)) - [`8956f9b`](autobrr/qui@8956f9b): feat: unify bulk tag editor ([#​1571](autobrr/qui#1571)) ([@​s0up4200](https://github.com/s0up4200)) ##### Bug Fixes - [`552d617`](autobrr/qui@552d617): fix(api): align add torrent OpenAPI field ([#​1617](autobrr/qui#1617)) ([@​s0up4200](https://github.com/s0up4200)) - [`424f7a0`](autobrr/qui@424f7a0): fix(api): restrict CORS to explicit allowlist ([#​1551](autobrr/qui#1551)) ([@​s0up4200](https://github.com/s0up4200)) - [`38991d8`](autobrr/qui@38991d8): fix(auth): allow loopback health probes ([#​1621](autobrr/qui#1621)) ([@​s0up4200](https://github.com/s0up4200)) - [`4ae88c9`](autobrr/qui@4ae88c9): fix(automations): align include-cross-seeds category apply ([#​1517](autobrr/qui#1517)) ([@​s0up4200](https://github.com/s0up4200)) - [`6a127a8`](autobrr/qui@6a127a8): fix(automations): scope skipWithin to only deleted action ([#​1538](autobrr/qui#1538)) ([@​jabloink](https://github.com/jabloink)) - [`c776189`](autobrr/qui@c776189): fix(crossseed): avoid completion timeout misses on non-Gazelle torrents ([#​1536](autobrr/qui#1536)) ([@​s0up4200](https://github.com/s0up4200)) - [`b1338a7`](autobrr/qui@b1338a7): fix(crossseed): handle missing webhook collection tags ([#​1610](autobrr/qui#1610)) ([@​s0up4200](https://github.com/s0up4200)) - [`eacbb68`](autobrr/qui@eacbb68): fix(crossseed): normalize hdr aliases ([#​1572](autobrr/qui#1572)) ([@​s0up4200](https://github.com/s0up4200)) - [`537ad46`](autobrr/qui@537ad46): fix(crossseed): queue completion searches and retry rate-limit waits ([#​1523](autobrr/qui#1523)) ([@​s0up4200](https://github.com/s0up4200)) - [`4fc550f`](autobrr/qui@4fc550f): fix(crossseed): use autobrr indexer ids for webhooks ([#​1614](autobrr/qui#1614)) ([@​s0up4200](https://github.com/s0up4200)) - [`08029ad`](autobrr/qui@08029ad): fix(crossseed): valid partial matches being rejected ([#​1291](autobrr/qui#1291)) ([@​rybertm](https://github.com/rybertm)) - [`77eedd9`](autobrr/qui@77eedd9): fix(database): avoid postgres temp-table statement caching ([#​1581](autobrr/qui#1581)) ([@​s0up4200](https://github.com/s0up4200)) - [`25daa17`](autobrr/qui@25daa17): fix(dirscan): honor canceled queued webhook runs ([#​1612](autobrr/qui#1612)) ([@​s0up4200](https://github.com/s0up4200)) - [`56995f1`](autobrr/qui@56995f1): fix(dirscan): queue webhook scans and tighten age filtering ([#​1603](autobrr/qui#1603)) ([@​s0up4200](https://github.com/s0up4200)) - [`444d07b`](autobrr/qui@444d07b): fix(dirscan): select concrete hardlink base dir ([#​1606](autobrr/qui#1606)) ([@​s0up4200](https://github.com/s0up4200)) - [`c35bea0`](autobrr/qui@c35bea0): fix(instances): improve settings dialog scrolling ([#​1569](autobrr/qui#1569)) ([@​nuxencs](https://github.com/nuxencs)) - [`dc501a0`](autobrr/qui@dc501a0): fix(proxy): reauth qbit passthrough requests ([#​1582](autobrr/qui#1582)) ([@​s0up4200](https://github.com/s0up4200)) - [`7950d1d`](autobrr/qui@7950d1d): fix(proxy): search endpoint handling ([#​1524](autobrr/qui#1524)) ([@​Audionut](https://github.com/Audionut)) - [`1076eea`](autobrr/qui@1076eea): fix(qbit): prune empty managed dirs after delete\_with\_files ([#​1604](autobrr/qui#1604)) ([@​s0up4200](https://github.com/s0up4200)) - [`5a3114b`](autobrr/qui@5a3114b): fix(qbittorrent): stop reboot torrent\_completed spam ([#​1515](autobrr/qui#1515)) ([@​s0up4200](https://github.com/s0up4200)) - [`1d02e6c`](autobrr/qui@1d02e6c): fix(settings): contain settings tab scrolling ([#​1567](autobrr/qui#1567)) ([@​nuxencs](https://github.com/nuxencs)) - [`f5d69f3`](autobrr/qui@f5d69f3): fix(settings): smoother gradient ([#​1570](autobrr/qui#1570)) ([@​nuxencs](https://github.com/nuxencs)) - [`1c0c3bc`](autobrr/qui@1c0c3bc): fix(torrents): copy MediaInfo summary without brackets ([#​1540](autobrr/qui#1540)) ([@​s0up4200](https://github.com/s0up4200)) - [`3ec913a`](autobrr/qui@3ec913a): fix(web): auto-append slash on path autocomplete selection ([#​1431](autobrr/qui#1431)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`aa2f3da`](autobrr/qui@aa2f3da): fix(web): check field.state.value type in AddTorrentDialog ([#​1613](autobrr/qui#1613)) ([@​s0up4200](https://github.com/s0up4200)) - [`1abfc5e`](autobrr/qui@1abfc5e): fix(web): handle SSO proxy redirect to /index.html ([#​1600](autobrr/qui#1600)) ([@​s0up4200](https://github.com/s0up4200)) - [`1991f90`](autobrr/qui@1991f90): fix(web): warn before enabling reannounce ([#​1583](autobrr/qui#1583)) ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`4069492`](autobrr/qui@4069492): chore(deps): bump the github group with 3 updates ([#​1535](autobrr/qui#1535)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`a02e9e8`](autobrr/qui@a02e9e8): chore(deps): bump the github group with 7 updates ([#​1558](autobrr/qui#1558)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`8713667`](autobrr/qui@8713667): chore(deps): bump the golang group with 15 updates ([#​1543](autobrr/qui#1543)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`420607e`](autobrr/qui@420607e): chore(go,ci): adopt go fix, bump to 1.26, and speed up PR checks ([#​1480](autobrr/qui#1480)) ([@​s0up4200](https://github.com/s0up4200)) - [`0d0df45`](autobrr/qui@0d0df45): docs: add password reset section to CLI commands ([#​1598](autobrr/qui#1598)) ([@​s0up4200](https://github.com/s0up4200)) - [`9ef56a2`](autobrr/qui@9ef56a2): refactor(makefile): windows support ([#​1546](autobrr/qui#1546)) ([@​Audionut](https://github.com/Audionut)) - [`7899cc8`](autobrr/qui@7899cc8): refactor(reflinking): add windows ReFS filesystem support ([#​1576](autobrr/qui#1576)) ([@​Audionut](https://github.com/Audionut)) - [`51d34ab`](autobrr/qui@51d34ab): refactor(releases): share hdr normalization helpers ([#​1586](autobrr/qui#1586)) ([@​s0up4200](https://github.com/s0up4200)) - [`c7f4e3d`](autobrr/qui@c7f4e3d): refactor(web): tighten unified scope navigation ([#​1618](autobrr/qui#1618)) ([@​s0up4200](https://github.com/s0up4200)) - [`4b05177`](autobrr/qui@4b05177): test(handlers): cover tag baseline field requests ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.14.1...v1.15.0> #### Docker images - `docker pull ghcr.io/autobrr/qui:v1.15.0` - `docker pull ghcr.io/autobrr/qui:latest` #### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4yIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4884 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
… ) (#12) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.14.1` → `v1.19.0` | --- ### Release Notes <details> <summary>autobrr/qui (ghcr.io/autobrr/qui)</summary> ### [`v1.19.0`](https://github.com/autobrr/qui/releases/tag/v1.19.0) [Compare Source](autobrr/qui@v1.18.0...v1.19.0) ##### Changelog ##### New Features - [`e26bd6b`](autobrr/qui@e26bd6b): feat(actions): include web-dist in release artifacts ([#​1801](autobrr/qui#1801)) ([@​turtletowerz](https://github.com/turtletowerz)) - [`2fabce1`](autobrr/qui@2fabce1): feat(automations): add export to instance action ([#​1736](autobrr/qui#1736)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`3fca808`](autobrr/qui@3fca808): feat(instances): api key authentication ([#​1854](autobrr/qui#1854)) ([@​martylukyy](https://github.com/martylukyy)) - [`4ab1a75`](autobrr/qui@4ab1a75): feat(qbittorrent): show process uptime ([#​1849](autobrr/qui#1849)) ([@​s0up4200](https://github.com/s0up4200)) - [`292ef70`](autobrr/qui@292ef70): feat(torrents): hide filter icon when not active or hovering over ([#​1869](autobrr/qui#1869)) ([@​martylukyy](https://github.com/martylukyy)) - [`3c5cf3b`](autobrr/qui@3c5cf3b): feat(torrents): implement auto-fit width for columns on double-click ([#​1867](autobrr/qui#1867)) ([@​martylukyy](https://github.com/martylukyy)) - [`5a9c538`](autobrr/qui@5a9c538): feat(torrents): set comment ([#​1928](autobrr/qui#1928)) ([@​martylukyy](https://github.com/martylukyy)) - [`754f642`](autobrr/qui@754f642): feat(torrents): support new share limit parameters ([#​1872](autobrr/qui#1872)) ([@​martylukyy](https://github.com/martylukyy)) - [`06a5fb6`](autobrr/qui@06a5fb6): feat(torrents): update AddTorrent methods for qbit 5.2.x support ([#​1902](autobrr/qui#1902)) ([@​zze0s](https://github.com/zze0s)) - [`45e3adc`](autobrr/qui@45e3adc): feat: add support for monitored folders ([#​1847](autobrr/qui#1847)) ([@​martylukyy](https://github.com/martylukyy)) ##### Bug Fixes - [`77c7ae8`](autobrr/qui@77c7ae8): fix(automations): discard add torrent response ([#​1904](autobrr/qui#1904)) ([@​s0up4200](https://github.com/s0up4200)) - [`7a368f1`](autobrr/qui@7a368f1): fix(ci): repair docs and release builds ([#​1932](autobrr/qui#1932)) ([@​s0up4200](https://github.com/s0up4200)) - [`8bd114e`](autobrr/qui@8bd114e): fix(crossseed): normalize empty qbit metadata ([#​1887](autobrr/qui#1887)) ([@​s0up4200](https://github.com/s0up4200)) - [`92d66f9`](autobrr/qui@92d66f9): fix(crossseed): reuse shared string normalizer to stop goroutine leak ([#​1909](autobrr/qui#1909)) ([@​s0up4200](https://github.com/s0up4200)) - [`3c5d32a`](autobrr/qui@3c5d32a): fix(crossseed): run external programs after link-mode injection ([#​1874](autobrr/qui#1874)) ([@​s0up4200](https://github.com/s0up4200)) - [`b02d2a9`](autobrr/qui@b02d2a9): fix(qbittorrent): handle forced subcategories ([#​1863](autobrr/qui#1863)) ([@​s0up4200](https://github.com/s0up4200)) - [`83cdb01`](autobrr/qui@83cdb01): fix(qbittorrent): handle new tracker statuses ([#​1879](autobrr/qui#1879)) ([@​s0up4200](https://github.com/s0up4200)) - [`b358ab4`](autobrr/qui@b358ab4): fix(rss): mirror torrentParams to legacy flat fields for qBit < 5.0 compat ([#​1846](autobrr/qui#1846)) ([@​frrad](https://github.com/frrad)) - [`5bcf648`](autobrr/qui@5bcf648): fix(seasonpacker): windows path handling ([#​1910](autobrr/qui#1910)) ([@​Audionut](https://github.com/Audionut)) - [`7de10c4`](autobrr/qui@7de10c4): fix(torrents): clear filters in unified-view empty state ([#​1898](autobrr/qui#1898)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`38f7bd3`](autobrr/qui@38f7bd3): fix(web): Show collapsed folders when searching torrent contents ([#​1850](autobrr/qui#1850)) ([@​ewenjo](https://github.com/ewenjo)) - [`d70d0bb`](autobrr/qui@d70d0bb): fix(web): keep showing torrent column filter icons on touch devices ([#​1881](autobrr/qui#1881)) ([@​martylukyy](https://github.com/martylukyy)) - [`cecbad7`](autobrr/qui@cecbad7): fix(web): select-all bulk actions delete more torrents than visible when filters are stacked ([#​1926](autobrr/qui#1926)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`780c4aa`](autobrr/qui@780c4aa): fix(web): sidebar element collisions ([#​1876](autobrr/qui#1876)) ([@​martylukyy](https://github.com/martylukyy)) - [`b621973`](autobrr/qui@b621973): fix(web): use unique keys for duplicate search results ([#​1886](autobrr/qui#1886)) ([@​GtwoK](https://github.com/GtwoK)) ##### Other Changes - [`ffda071`](autobrr/qui@ffda071): chore(deps): bump [@​babel/plugin-transform-modules-systemjs](https://github.com/babel/plugin-transform-modules-systemjs) from 7.28.5 to 7.29.4 in /documentation ([#​1889](autobrr/qui#1889)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`89ab1c8`](autobrr/qui@89ab1c8): chore(deps): bump [@​babel/plugin-transform-modules-systemjs](https://github.com/babel/plugin-transform-modules-systemjs) from 7.29.0 to 7.29.4 in /web ([#​1888](autobrr/qui#1888)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`c420bb2`](autobrr/qui@c420bb2): chore(deps): bump follow-redirects from 1.15.11 to 1.16.0 in /documentation ([#​1903](autobrr/qui#1903)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`28e4bf9`](autobrr/qui@28e4bf9): chore(deps): bump the github group with 2 updates ([#​1894](autobrr/qui#1894)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`9b241de`](autobrr/qui@9b241de): chore(deps): bump the github group with 3 updates ([#​1861](autobrr/qui#1861)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`4872577`](autobrr/qui@4872577): chore(deps): bump webpack-dev-server from 5.2.2 to 5.2.4 in /documentation ([#​1908](autobrr/qui#1908)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`3e3ec8b`](autobrr/qui@3e3ec8b): chore(deps): bump ws from 8.18.3 to 8.20.1 in /documentation ([#​1907](autobrr/qui#1907)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`25cf511`](autobrr/qui@25cf511): chore(web): migrate to pnpm 11 ([#​1883](autobrr/qui#1883)) ([@​martylukyy](https://github.com/martylukyy)) - [`4b6dce1`](autobrr/qui@4b6dce1): docs: add shared agent instructions symlink ([#​1911](autobrr/qui#1911)) ([@​Audionut](https://github.com/Audionut)) - [`97df42c`](autobrr/qui@97df42c): other(docs): resolve 5 Dependabot security alerts on Docs Site ([#​1906](autobrr/qui#1906)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`5068a4d`](autobrr/qui@5068a4d): refactor(crossseed): gather all results for matching and refine TV based searching ([#​1871](autobrr/qui#1871)) ([@​Audionut](https://github.com/Audionut)) - [`9d3e245`](autobrr/qui@9d3e245): refactor(crossseed): improve anime/AKA handling ([#​1882](autobrr/qui#1882)) ([@​Audionut](https://github.com/Audionut)) - [`6f2b94c`](autobrr/qui@6f2b94c): refactor(torrents): drop unused rename props from TorrentContextMenu ([#​1900](autobrr/qui#1900)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`3ec0605`](autobrr/qui@3ec0605): refactor(web): clean up 180 multiline-ternary warnings repo-wide ([#​1905](autobrr/qui#1905)) ([@​nitrobass24](https://github.com/nitrobass24)) **Full Changelog**: <autobrr/qui@v1.18.0...v1.19.0> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.19.0` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! ### [`v1.18.0`](https://github.com/autobrr/qui/releases/tag/v1.18.0) [Compare Source](autobrr/qui@v1.17.0...v1.18.0) ##### Changelog ##### New Features - [`b708bc8`](autobrr/qui@b708bc8): feat(automations): add UPLOADED\_OVER\_SIZE condition field ([#​1830](autobrr/qui#1830)) ([@​com6056](https://github.com/com6056)) - [`565731e`](autobrr/qui@565731e): feat(automations): cross-instance HARDLINK\_SCOPE\_CROSS ([#​1810](autobrr/qui#1810)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`a284860`](autobrr/qui@a284860): feat(dirscan): add webhook download client filters ([#​1751](autobrr/qui#1751)) ([@​s0up4200](https://github.com/s0up4200)) - [`fd54124`](autobrr/qui@fd54124): feat(web): Add option to copy magnet link from the context menu ([#​1835](autobrr/qui#1835)) ([@​pietrocaselani](https://github.com/pietrocaselani)) ##### Bug Fixes - [`ea33304`](autobrr/qui@ea33304): fix(crossseed): broaden TV/movie torznab categories and skip year on TV ([#​1822](autobrr/qui#1822)) ([@​imSp4rky](https://github.com/imSp4rky)) - [`3c3f944`](autobrr/qui@3c3f944): fix(license): unblock stale polar license migration ([#​1799](autobrr/qui#1799)) ([@​s0up4200](https://github.com/s0up4200)) - [`941cf42`](autobrr/qui@941cf42): fix(qbit): stop serving stale maindata to the status bar ([#​1785](autobrr/qui#1785)) ([@​Zariel](https://github.com/Zariel)) - [`ca7590a`](autobrr/qui@ca7590a): fix(rss): populate legacy assignedCategory for qBittorrent < 5.0 comp… ([#​1836](autobrr/qui#1836)) ([@​frrad](https://github.com/frrad)) - [`278441a`](autobrr/qui@278441a): fix(settings): explain disabled API keys ([#​1826](autobrr/qui#1826)) ([@​s0up4200](https://github.com/s0up4200)) - [`0f4eb52`](autobrr/qui@0f4eb52): fix(web): remove deprecated tsconfig baseUrl ([#​1840](autobrr/qui#1840)) ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`c6dbf9a`](autobrr/qui@c6dbf9a): chore(deps): bump postcss from 8.5.6 to 8.5.10 in /documentation ([#​1837](autobrr/qui#1837)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`053c171`](autobrr/qui@053c171): chore(deps): bump the github group across 1 directory with 3 updates ([#​1834](autobrr/qui#1834)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`4cbedac`](autobrr/qui@4cbedac): chore(deps): bump the golang group with 15 updates ([#​1832](autobrr/qui#1832)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`dd661e8`](autobrr/qui@dd661e8): chore(deps): bump the npm group in /web with 20 updates ([#​1833](autobrr/qui#1833)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`82029d6`](autobrr/qui@82029d6): chore(deps): update go-qbittorrent for new qbt 5.2.x webAPI version ([#​1841](autobrr/qui#1841)) ([@​martylukyy](https://github.com/martylukyy)) - [`6b5b82e`](autobrr/qui@6b5b82e): refactor(time): use seconds and cleanup ([#​1823](autobrr/qui#1823)) ([@​Audionut](https://github.com/Audionut)) - [`f5cb0c1`](autobrr/qui@f5cb0c1): refactor: O(n²) cross-seed lookups in automations preview and execution paths ([#​1829](autobrr/qui#1829)) ([@​nitrobass24](https://github.com/nitrobass24)) **Full Changelog**: <autobrr/qui@v1.17.0...v1.18.0> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.18.0` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! ### [`v1.17.0`](https://github.com/autobrr/qui/releases/tag/v1.17.0) [Compare Source](autobrr/qui@v1.16.1...v1.17.0) ##### Changelog ##### Bug Fixes - [`52c5e73`](autobrr/qui@52c5e73): fix(automations): clarify cross-seed condition labels ([#​1763](autobrr/qui#1763)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`70e0032`](autobrr/qui@70e0032): fix(crossseed): skip link-mode category path warnings ([#​1753](autobrr/qui#1753)) ([@​s0up4200](https://github.com/s0up4200)) - [`85df3e7`](autobrr/qui@85df3e7): fix(orphanscan): align content path root detection ([#​1771](autobrr/qui#1771)) ([@​s0up4200](https://github.com/s0up4200)) - [`117d617`](autobrr/qui@117d617): fix(orphanscan): ignore qBittorrent incomplete files ([#​1761](autobrr/qui#1761)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`388415e`](autobrr/qui@388415e): fix(web): fix add torrent/magnet urls ([#​1762](autobrr/qui#1762)) ([@​nitrobass24](https://github.com/nitrobass24)) ##### Other Changes - [`845599a`](autobrr/qui@845599a): chore(ci): speed up CI pipeline (9.3 min → 4.3 min for PRs) ([#​1750](autobrr/qui#1750)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`2b09cf3`](autobrr/qui@2b09cf3): chore: trim embedded web assets and Docker build context ([#​1723](autobrr/qui#1723)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`5262f7c`](autobrr/qui@5262f7c): docs: add migration PR guidance ([#​1748](autobrr/qui#1748)) ([@​s0up4200](https://github.com/s0up4200)) - [`65c7046`](autobrr/qui@65c7046): refactor(search): table & cards cleanup ([#​1768](autobrr/qui#1768)) ([@​nuxencs](https://github.com/nuxencs)) **Full Changelog**: <autobrr/qui@v1.16.1...v1.17.0> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.17.0` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! ### [`v1.16.1`](https://github.com/autobrr/qui/releases/tag/v1.16.1) [Compare Source](autobrr/qui@v1.16.0...v1.16.1) ##### Changelog ##### Bug Fixes - [`e177ad9`](autobrr/qui@e177ad9): fix(auth): harden OIDC PKCE flow ([#​1746](autobrr/qui#1746)) ([@​s0up4200](https://github.com/s0up4200)) - [`393e2b7`](autobrr/qui@393e2b7): fix(dirscan): tighten flexible matching and improve diagnostics ([#​1742](autobrr/qui#1742)) ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.16.0...v1.16.1> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.16.1` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! ### [`v1.16.0`](https://github.com/autobrr/qui/releases/tag/v1.16.0) [Compare Source](autobrr/qui@v1.15.0...v1.16.0) ##### Changelog ##### Highlights - Automations got a major upgrade: rules can now match across instances, use system time, control AutoTMM, and opt out of notifications per workflow. - Cross-seed is more reliable during state changes. Completion searches now wait for torrents to finish checking or moving, disabled instances are skipped cleanly, and hardlink/reflink save-path handling is more accurate. - Dir Scan works better with real media libraries, with improved partial season-pack handling in link-tree mode, support for downloading missing files when needed, and better progress retention across restarts. - Managing torrents in the unified view is smoother, with more accurate tracker health, quicker instance-level actions, and more stable category and tag editing dialogs. - OIDC and backups both got practical quality-of-life improvements: OIDC now supports PKCE, backup settings can be applied across instances, and backup export handling is safer for tricky torrent layouts. ##### New Features - [`2b92c7b`](autobrr/qui@2b92c7b): feat(auth): add PKCE support to OIDC implementation ([#​1737](autobrr/qui#1737)) ([@​oynqr](https://github.com/oynqr)) - [`ba3d5d9`](autobrr/qui@ba3d5d9): feat(automations): add AutoTMM condition and action ([#​1698](autobrr/qui#1698)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`702e808`](autobrr/qui@702e808): feat(automations): add system time to query builder ([#​1677](autobrr/qui#1677)) ([@​wastaken7](https://github.com/wastaken7)) - [`e6493b3`](autobrr/qui@e6493b3): feat(automations): allow disable of notifications ([#​1652](autobrr/qui#1652)) ([@​heathlarsen](https://github.com/heathlarsen)) - [`565ac2d`](autobrr/qui@565ac2d): feat(automations): cross instance condition ([#​1648](autobrr/qui#1648)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`7e12a02`](autobrr/qui@7e12a02): feat(update): verify self-updates with signed release checksums ([#​1665](autobrr/qui#1665)) ([@​s0up4200](https://github.com/s0up4200)) - [`3778c7b`](autobrr/qui@3778c7b): feat(web): Add action buttons to unified instance. ([#​1637](autobrr/qui#1637)) ([@​drtaru](https://github.com/drtaru)) - [`bf9eaba`](autobrr/qui@bf9eaba): feat(web): Clarify dashboard quick links ([#​1636](autobrr/qui#1636)) ([@​drtaru](https://github.com/drtaru)) - [`b930530`](autobrr/qui@b930530): feat(web): add "Save changes to all instances" button to backup settings ([#​1651](autobrr/qui#1651)) ([@​drtaru](https://github.com/drtaru)) - [`5975c34`](autobrr/qui@5975c34): feat(web): add Discord perk section to license manager ([#​1656](autobrr/qui#1656)) ([@​s0up4200](https://github.com/s0up4200)) - [`d8ad0d6`](autobrr/qui@d8ad0d6): feat(web): unify tab styling and animations ([#​1632](autobrr/qui#1632)) ([@​nuxencs](https://github.com/nuxencs)) ##### Bug Fixes - [`44596b9`](autobrr/qui@44596b9): fix(automations): add AutoTMM to condition validation ([#​1726](autobrr/qui#1726)) ([@​s0up4200](https://github.com/s0up4200)) - [`8f757b2`](autobrr/qui@8f757b2): fix(automations): hardlink signature grouping ([#​1670](autobrr/qui#1670)) ([@​aulterego](https://github.com/aulterego)) - [`d242f0c`](autobrr/qui@d242f0c): fix(automations): include AutoManagement in delete standalone check ([#​1731](autobrr/qui#1731)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`744bdb8`](autobrr/qui@744bdb8): fix(backups): adaptive export throttle ([#​1630](autobrr/qui#1630)) ([@​s0up4200](https://github.com/s0up4200)) - [`d1dbb81`](autobrr/qui@d1dbb81): fix(backups): gate bulk save on resolved instance capabilities ([#​1682](autobrr/qui#1682)) ([@​s0up4200](https://github.com/s0up4200)) - [`2eea961`](autobrr/qui@2eea961): fix(backups): skip live export for hybrid torrents ([#​1669](autobrr/qui#1669)) ([@​s0up4200](https://github.com/s0up4200)) - [`2cac32d`](autobrr/qui@2cac32d): fix(crossseed): skip disabled instances ([#​1635](autobrr/qui#1635)) ([@​s0up4200](https://github.com/s0up4200)) - [`ebbba8e`](autobrr/qui@ebbba8e): fix(crossseed): tone down async cache reuse log ([#​1686](autobrr/qui#1686)) ([@​s0up4200](https://github.com/s0up4200)) - [`fd382b7`](autobrr/qui@fd382b7): fix(dirscan): link plan size tolerance + partial season pack injection in link tree mode ([#​1695](autobrr/qui#1695)) ([@​s0up4200](https://github.com/s0up4200)) - [`5131092`](autobrr/qui@5131092): fix(dirscan): retain recent runs and clarify restart behavior ([#​1564](autobrr/qui#1564)) ([@​s0up4200](https://github.com/s0up4200)) - [`3fbcc7a`](autobrr/qui@3fbcc7a): fix(openapi): document dirscan downloadMissingFiles ([#​1727](autobrr/qui#1727)) ([@​s0up4200](https://github.com/s0up4200)) - [`d16fee2`](autobrr/qui@d16fee2): fix(orphanscan): use content\_path to prevent false positives when Auto TMM changes save\_path ([#​1712](autobrr/qui#1712)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`e74bf02`](autobrr/qui@e74bf02): fix(qbittorrent): avoid tracker health URL false positives ([#​1738](autobrr/qui#1738)) ([@​s0up4200](https://github.com/s0up4200)) - [`632fc54`](autobrr/qui@632fc54): fix(torrents): honor tracker health in unified view ([#​1668](autobrr/qui#1668)) ([@​s0up4200](https://github.com/s0up4200)) - [`76fddc4`](autobrr/qui@76fddc4): fix(torrents): stabilize tag and category dialogs ([#​1638](autobrr/qui#1638)) ([@​s0up4200](https://github.com/s0up4200)) - [`c758b6d`](autobrr/qui@c758b6d): fix(torrents): validate creator output path ([#​1739](autobrr/qui#1739)) ([@​s0up4200](https://github.com/s0up4200)) - [`6c23f0e`](autobrr/qui@6c23f0e): fix(web): cross-seed warning in unified view ([#​1692](autobrr/qui#1692)) ([@​s0up4200](https://github.com/s0up4200)) - [`57822c0`](autobrr/qui@57822c0): fix(web): improve duplicate torrent state and check another field.state.value type in AddTorrentDialog ([#​1679](autobrr/qui#1679)) ([@​keatonhasse](https://github.com/keatonhasse)) - [`246c8f6`](autobrr/qui@246c8f6): fix(web): migrate vite chunk splitting config ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`263b0bd`](autobrr/qui@263b0bd): build(deps): add cooldown to dependabot config ([#​1691](autobrr/qui#1691)) ([@​s0up4200](https://github.com/s0up4200)) - [`a394157`](autobrr/qui@a394157): chore(deps): bump github.com/go-jose/go-jose/v4 from 4.1.3 to 4.1.4 ([#​1713](autobrr/qui#1713)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`10612a7`](autobrr/qui@10612a7): chore(deps): bump golang.org/x/image from 0.36.0 to 0.38.0 ([#​1685](autobrr/qui#1685)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`16019dd`](autobrr/qui@16019dd): chore(deps): bump pnpm/action-setup from 4 to 5 in the github group ([#​1634](autobrr/qui#1634)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`fbb25fc`](autobrr/qui@fbb25fc): chore(deps): bump the golang group with 11 updates ([#​1693](autobrr/qui#1693)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`cbb9594`](autobrr/qui@cbb9594): chore(deps): bump the golang group with 3 updates ([#​1701](autobrr/qui#1701)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`805ab74`](autobrr/qui@805ab74): chore(deps): bump the npm group in /web with 25 updates ([#​1694](autobrr/qui#1694)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`e3f839c`](autobrr/qui@e3f839c): chore(deps): bump the npm group in /web with 5 updates ([#​1702](autobrr/qui#1702)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`b076ad4`](autobrr/qui@b076ad4): docs(dirscan): clarify re-identification after torrent removal ([#​1720](autobrr/qui#1720)) ([@​s0up4200](https://github.com/s0up4200)) - [`340f343`](autobrr/qui@340f343): docs: add license management page with deactivation guide ([#​1706](autobrr/qui#1706)) ([@​s0up4200](https://github.com/s0up4200)) - [`5d148be`](autobrr/qui@5d148be): docs: fix link in issue triage template ([@​s0up4200](https://github.com/s0up4200)) - [`0b64237`](autobrr/qui@0b64237): docs: update release follow-up docs ([#​1741](autobrr/qui#1741)) ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.15.0...v1.16.0> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.16.0` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! ### [`v1.15.0`](https://github.com/autobrr/qui/releases/tag/v1.15.0) [Compare Source](autobrr/qui@v1.14.1...v1.15.0) ##### Changelog ##### Breaking change CORS is disabled by default; enable by setting QUI\_\_CORS\_ALLOWED\_ORIGINS with explicit origins (http(s)://host\[:port]). See <https://getqui.com/docs/advanced/sso-proxy-cors> ##### New Features - [`93786a2`](autobrr/qui@93786a2): feat(automations): add configurable processing priority/sorting ([#​1235](autobrr/qui#1235)) ([@​Oscariremma](https://github.com/Oscariremma)) - [`45eaf1f`](autobrr/qui@45eaf1f): feat(database): add postgres and sqlite migration CLI ([#​1530](autobrr/qui#1530)) ([@​s0up4200](https://github.com/s0up4200)) - [`430f5d1`](autobrr/qui@430f5d1): feat(torrents): mediaInfo dialog ([#​1537](autobrr/qui#1537)) ([@​s0up4200](https://github.com/s0up4200)) - [`8eb8903`](autobrr/qui@8eb8903): feat(web): Add persistence to unified instance filter in sidebar ([#​1560](autobrr/qui#1560)) ([@​drtaru](https://github.com/drtaru)) - [`7aadde7`](autobrr/qui@7aadde7): feat(web): add path autocomplete to set location dialog ([#​1432](autobrr/qui#1432)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`077f32c`](autobrr/qui@077f32c): feat: add mediainfo api endpoint ([#​1545](autobrr/qui#1545)) ([@​Audionut](https://github.com/Audionut)) - [`99cf695`](autobrr/qui@99cf695): feat: endpoint to trigger directory scans from external tools ([#​1559](autobrr/qui#1559)) ([@​s0up4200](https://github.com/s0up4200)) - [`8956f9b`](autobrr/qui@8956f9b): feat: unify bulk tag editor ([#​1571](autobrr/qui#1571)) ([@​s0up4200](https://github.com/s0up4200)) ##### Bug Fixes - [`552d617`](autobrr/qui@552d617): fix(api): align add torrent OpenAPI field ([#​1617](autobrr/qui#1617)) ([@​s0up4200](https://github.com/s0up4200)) - [`424f7a0`](autobrr/qui@424f7a0): fix(api): restrict CORS to explicit allowlist ([#​1551](autobrr/qui#1551)) ([@​s0up4200](https://github.com/s0up4200)) - [`38991d8`](autobrr/qui@38991d8): fix(auth): allow loopback health probes ([#​1621](autobrr/qui#1621)) ([@​s0up4200](https://github.com/s0up4200)) - [`4ae88c9`](autobrr/qui@4ae88c9): fix(automations): align include-cross-seeds category apply ([#​1517](autobrr/qui#1517)) ([@​s0up4200](https://github.com/s0up4200)) - [`6a127a8`](autobrr/qui@6a127a8): fix(automations): scope skipWithin to only deleted action ([#​1538](autobrr/qui#1538)) ([@​jabloink](https://github.com/jabloink)) - [`c776189`](autobrr/qui@c776189): fix(crossseed): avoid completion timeout misses on non-Gazelle torrents ([#​1536](autobrr/qui#1536)) ([@​s0up4200](https://github.com/s0up4200)) - [`b1338a7`](autobrr/qui@b1338a7): fix(crossseed): handle missing webhook collection tags ([#​1610](autobrr/qui#1610)) ([@​s0up4200](https://github.com/s0up4200)) - [`eacbb68`](autobrr/qui@eacbb68): fix(crossseed): normalize hdr aliases ([#​1572](autobrr/qui#1572)) ([@​s0up4200](https://github.com/s0up4200)) - [`537ad46`](autobrr/qui@537ad46): fix(crossseed): queue completion searches and retry rate-limit waits ([#​1523](autobrr/qui#1523)) ([@​s0up4200](https://github.com/s0up4200)) - [`4fc550f`](autobrr/qui@4fc550f): fix(crossseed): use autobrr indexer ids for webhooks ([#​1614](autobrr/qui#1614)) ([@​s0up4200](https://github.com/s0up4200)) - [`08029ad`](autobrr/qui@08029ad): fix(crossseed): valid partial matches being rejected ([#​1291](autobrr/qui#1291)) ([@​rybertm](https://github.com/rybertm)) - [`77eedd9`](autobrr/qui@77eedd9): fix(database): avoid postgres temp-table statement caching ([#​1581](autobrr/qui#1581)) ([@​s0up4200](https://github.com/s0up4200)) - [`25daa17`](autobrr/qui@25daa17): fix(dirscan): honor canceled queued webhook runs ([#​1612](autobrr/qui#1612)) ([@​s0up4200](https://github.com/s0up4200)) - [`56995f1`](autobrr/qui@56995f1): fix(dirscan): queue webhook scans and tighten age filtering ([#​1603](autobrr/qui#1603)) ([@​s0up4200](https://github.com/s0up4200)) - [`444d07b`](autobrr/qui@444d07b): fix(dirscan): select concrete hardlink base dir ([#​1606](autobrr/qui#1606)) ([@​s0up4200](https://github.com/s0up4200)) - [`c35bea0`](autobrr/qui@c35bea0): fix(instances): improve settings dialog scrolling ([#​1569](autobrr/qui#1569)) ([@​nuxencs](https://github.com/nuxencs)) - [`dc501a0`](autobrr/qui@dc501a0): fix(proxy): reauth qbit passthrough requests ([#​1582](autobrr/qui#1582)) ([@​s0up4200](https://github.com/s0up4200)) - [`7950d1d`](autobrr/qui@7950d1d): fix(proxy): search endpoint handling ([#​1524](autobrr/qui#1524)) ([@​Audionut](https://github.com/Audionut)) - [`1076eea`](autobrr/qui@1076eea): fix(qbit): prune empty managed dirs after delete\_with\_files ([#​1604](autobrr/qui#1604)) ([@​s0up4200](https://github.com/s0up4200)) - [`5a3114b`](autobrr/qui@5a3114b): fix(qbittorrent): stop reboot torrent\_completed spam ([#​1515](autobrr/qui#1515)) ([@​s0up4200](https://github.com/s0up4200)) - [`1d02e6c`](autobrr/qui@1d02e6c): fix(settings): contain settings tab scrolling ([#​1567](autobrr/qui#1567)) ([@​nuxencs](https://github.com/nuxencs)) - [`f5d69f3`](autobrr/qui@f5d69f3): fix(settings): smoother gradient ([#​1570](autobrr/qui#1570)) ([@​nuxencs](https://github.com/nuxencs)) - [`1c0c3bc`](autobrr/qui@1c0c3bc): fix(torrents): copy MediaInfo summary without brackets ([#​1540](autobrr/qui#1540)) ([@​s0up4200](https://github.com/s0up4200)) - [`3ec913a`](autobrr/qui@3ec913a): fix(web): auto-append slash on path autocomplete selection ([#​1431](autobrr/qui#1431)) ([@​nitrobass24](https://github.com/nitrobass24)) - [`aa2f3da`](autobrr/qui@aa2f3da): fix(web): check field.state.value type in AddTorrentDialog ([#​1613](autobrr/qui#1613)) ([@​s0up4200](https://github.com/s0up4200)) - [`1abfc5e`](autobrr/qui@1abfc5e): fix(web): handle SSO proxy redirect to /index.html ([#​1600](autobrr/qui#1600)) ([@​s0up4200](https://github.com/s0up4200)) - [`1991f90`](autobrr/qui@1991f90): fix(web): warn before enabling reannounce ([#​1583](autobrr/qui#1583)) ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`4069492`](autobrr/qui@4069492): chore(deps): bump the github group with 3 updates ([#​1535](autobrr/qui#1535)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`a02e9e8`](autobrr/qui@a02e9e8): chore(deps): bump the github group with 7 updates ([#​1558](autobrr/qui#1558)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`8713667`](autobrr/qui@8713667): chore(deps): bump the golang group with 15 updates ([#​1543](autobrr/qui#1543)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`420607e`](autobrr/qui@420607e): chore(go,ci): adopt go fix, bump to 1.26, and speed up PR checks ([#​1480](autobrr/qui#1480)) ([@​s0up4200](https://github.com/s0up4200)) - [`0d0df45`](autobrr/qui@0d0df45): docs: add password reset section to CLI commands ([#​1598](autobrr/qui#1598)) ([@​s0up4200](https://github.com/s0up4200)) - [`9ef56a2`](autobrr/qui@9ef56a2): refactor(makefile): windows support ([#​1546](autobrr/qui#1546)) ([@​Audionut](https://github.com/Audionut)) - [`7899cc8`](autobrr/qui@7899cc8): refactor(reflinking): add windows ReFS filesystem support ([#​1576](autobrr/qui#1576)) ([@​Audionut](https://github.com/Audionut)) - [`51d34ab`](autobrr/qui@51d34ab): refactor(releases): share hdr normalization helpers ([#​1586](autobrr/qui#1586)) ([@​s0up4200](https://github.com/s0up4200)) - [`c7f4e3d`](autobrr/qui@c7f4e3d): refactor(web): tighten unified scope navigation ([#​1618](autobrr/qui#1618)) ([@​s0up4200](https://github.com/s0up4200)) - [`4b05177`](autobrr/qui@4b05177): test(handlers): cover tag baseline field requests ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.14.1...v1.15.0> ##### Docker images - `docker pull ghcr.io/autobrr/qui:v1.15.0` - `docker pull ghcr.io/autobrr/qui:latest` ##### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/London) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/12
Summary
This PR adds a webhook endpoint that allows external tools like Sonarr/Radarr custom scripts to trigger directory scans by providing a file path. The endpoint intelligently matches the provided path against configured scan directories using longest-prefix matching.
Key Changes
POST /api/dir-scan/webhook/scanaccepts a JSON payload with apathfield202 Acceptedwhen scan starts successfully404 Not Foundwhen no matching directory is found409 Conflictwhen a scan is already in progress400 Bad Requestfor invalid input?apikey=pattern as the cross-seed webhook for external tool integrationImplementation Details
filepath.Clean()to handle various path formats consistentlyrunIdanddirectoryIdfor external tools to track the scanSummary by CodeRabbit
New Features
Documentation
Tests
Chores