Changelog
Breaking changes
- Automation API responses no longer include
trackerDomains. Scripts that read or write it must usetrackerPatterninstead. Saved rules and old exports still work. - The API-only cross-seed settings
categoryandstartPausedare gone. If you set them through the API, use the Custom category mode in the UI, or sendcategoryandstartPausedin the webhook/applybody.
Highlights
- Update and restart from the web UI. A new Install update button checks the release signature and keeps a backup of the old binary. A Restart button is in Settings > Application. Neither works in Docker, where you pull a new image instead.
- Better on Windows. A per-user MSI installer, and
qui-tray.exe, which runs qui without a console window. Its tray icon menu has Open qui, Restart, Open config folder, Start with Windows and Quit. - A new Cross-Seed page. It has one section per source (RSS, Webhook, Completion, Library, Directories), and each section saves on its own.
- Cross-seed matching fixes. Season packs now match with AKA, slash and Roman-numeral titles. Hardlinks try the next base directory after a cross-device error. Jackett and Prowlarr searches use your indexer timeout.
- A theme just for mobile. Give the mobile layout its own theme, or keep one browser's theme local.
- Fixes and polish. Edit automation rules as JSON, a Content Type drop-down,
{{.Tracker}}uses display names, orphan scan disc fixes, safer delete checks, and more translated UI.
Thanks to ColinHebert, com6056, nitrobass24, YouKyi, theluckystrike, and neelmehta247 for contributing to this release.
New Features
- b59153f: feat(automations): add path template help to Move and Export save paths (#2762) (@ColinHebert)
- 9fbb687: feat(automations): edit a rule as JSON with a highlighted editor (#2797) (@s0up4200)
- e3c08a5: feat(automations): lift the free-space path source gates for remote instances (#2951) (@s0up4200)
- 12ab17d: feat(automations): pick Content Type from a drop-down (#2757) (@ColinHebert)
- f913e1f: feat(crossseed): check a new OPS or RED API key when the settings are saved (#2810) (@s0up4200)
- 760a366: feat(crossseed): stop querying a Gazelle tracker that rejects the key or IP (#2808) (@s0up4200)
- 60e1789: feat(fsops): SFTP remote backend, read-only operations (#2739) (@nitrobass24)
- 8a17b86: feat(fsops): SFTP remote backend, write operations (#2952) (@nitrobass24)
- 6492cba: feat(i18n): check translation keys in labelKey and sibling data properties (#2964) (@s0up4200)
- e99ae5e: feat(orphanscan): scan remote instances, refuse their deletes (#2968) (@com6056)
- 75cdc2f: feat(system): Unix Restart through an in-place exec (#2873) (@s0up4200)
- 78c1338: feat(system): Windows Restart through a supervisor process (#2878) (@s0up4200)
- 26b4bcf: feat(themes): add a separate theme for the mobile layout and a browser-local theme (#3021) (@s0up4200)
- 3bdb723: feat(update): Self-update endpoint with a verified backup (#2876) (@s0up4200)
- 558206b: feat(update): measure Restart and Self-update availability (#2862) (@s0up4200)
- b69f874: feat(web): Install update button and update overlay (#2885) (@s0up4200)
- ac09630: feat(web): Restart button and restart overlay (#2881) (@s0up4200)
- 2218266: feat(web): cross-seed page with one section per source (#2787) (@s0up4200)
- 638cbea: feat(web): point users of other clients to the migration tool (#2818) (@s0up4200)
- d5c9e0b: feat(web): remember the live log viewer's level filter (#2931) (@s0up4200)
- cc53620: feat(windows): add the Tray and a qui-tray.exe build (#2887) (@s0up4200)
- a4c36ca: feat(windows): ship a per-user MSI next to the zip (#2894) (@s0up4200)
Bug Fixes
- e1642e1: fix(api): end the log and RSS streams when the shutdown starts (#2861) (@s0up4200)
- 945c3af: fix(automations): hide SSH wording until the SSH setup screen ships (#2967) (@s0up4200)
- a05a5c7: fix(automations): use tracker display names for .Tracker in move and export paths (#2892) (@s0up4200)
- 58c1ff7: fix(ci): install wixl for the MSI build (#2910) (@s0up4200)
- 9c7c887: fix(crossseed): add an exact match without a recheck after a link-mode fallback (#3029) (@s0up4200)
- ab64083: fix(crossseed): apply the blocklist to Dir Scan and season-pack apply (#2833) (@s0up4200)
- 0378452: fix(crossseed): count title-rescued results as usable in the retry step (#3051) (@s0up4200)
- d6aa6b7: fix(crossseed): fail closed on Lstat permission errors in the local match check (#2850) (@s0up4200)
- 244fff7: fix(crossseed): fail closed on unresolvable torrent file names (#2423) (@nitrobass24)
- e279f81: fix(crossseed): read a season numeral title without the numeral (#3022) (@s0up4200)
- 2519388: fix(crossseed): read slash titles in the season pack check and Local matches (#3055) (@s0up4200)
- 8a28650: fix(crossseed): read the season numeral without a file list (#3052) (@s0up4200)
- 4e21480: fix(crossseed): recheck pooled hardlink cross-seeds that start in missingFiles (#2841) (@s0up4200)
- 782a426: fix(crossseed): report a secret as set only when it decrypts (#2806) (@s0up4200)
- 71d4d54: fix(crossseed): season pack hint reads AKA, numeral and held alternate titles (#3054) (@s0up4200)
- 84b0795: fix(crossseed): try the next hardlink base dir after a cross-device error (#2983) (@s0up4200)
- 5691d21: fix(dirscan): check linked files before a partial hardlink add resumes (#2909) (@s0up4200)
- e9fddf0: fix(fsops): act on the current row and connection state in the remote backend (#2884) (@com6056)
- 4de1994: fix(i18n): guard the plural forms i18next actually resolves (#2769) (@ColinHebert)
- c1050a3: fix(jackett): honor configured indexer search timeouts (#2995) (@neelmehta247)
- 8b52f1b: fix(metadata): drop misleading TVDB startup log (#2826) (@s0up4200)
- 3e7221d: fix(orphanscan): decide disc units in lexical walk order (#2972) (@com6056)
- 50ee175: fix(orphanscan): fold nested disc units into the outermost disc root (#3004) (@s0up4200)
- 79cebf0: fix(orphanscan): keep a seeded disc unit from hiding its siblings (#3042) (@theluckystrike)
- de99118: fix(orphanscan): resolve subcategory paths flat on qBittorrent 4.6 (#2763) (@ColinHebert)
- 931697b: fix(orphanscan): use the backend path dialect for scan, delete and ignore paths (#3050) (@com6056)
- 66a2cbd: fix(preferences): lift the share ratio cap in seeding limits (#2846) (@s0up4200)
- 5255938: fix(pwa): pick up a new build when the app comes back on screen (#3027) (@s0up4200)
- b1218f0: fix(qbittorrent): prune completion and added state for removed torrents (#2950) (@s0up4200)
- e533d49: fix(qbittorrent): sort the Unified view like the single-instance view (#2993) (@s0up4200)
- d040b21: fix(release): allow the Windows zip to hold two binaries (#2890) (@s0up4200)
- c189e60: fix(release): list breaking changes first in the changelog (#2980) (@s0up4200)
- 7eb4fa1: fix(sshpool): constrain host-key algorithms on first contact (#2789) (@com6056)
- 0970c0b: fix(trackers): refresh tracker health from qBittorrent on every pass (#2820) (@s0up4200)
- b2ccaf6: fix(web): add missing table columns in default order and show Instance after Name (#2838) (@ColinHebert)
- af0b57e: fix(web): copy the cross-seed search report on plain HTTP (#2827) (@s0up4200)
- d47d306: fix(web): fit the orphan scan card and preview on narrow screens (#2922) (@com6056)
- 5621c3b: fix(web): keep the user's edits when a preference save fails (#2923) (@s0up4200)
- 9be0566: fix(web): name cross-seed in the instance RSS tooltip (#2895) (@s0up4200)
- 105e006: fix(web): report a preference save only after qBittorrent answers (#2908) (@s0up4200)
- 1db8c84: fix(web): show the orphan preview again when it is reopened (#2898) (@com6056)
- 5356cec: fix(web): size the icon columns to the icon plus row padding (#2785) (@s0up4200)
- e4d78ea: fix(web): translate API error fallbacks and title-bar speeds (#2783) (@ColinHebert)
- d413fed: fix(web): translate option labels from exported constants and check .ts files for them (#2781) (@ColinHebert)
- 607b510: fix(web): use i18next plurals instead of English suffix variables (#2784) (@ColinHebert)
- c1a9ba0: fix: limit concurrent external programs and fix four goroutine defects (#2852) (@s0up4200)
Other Changes
- 432b5a6: build(ci): add workflow to build and push images for external PRs (#2804) (@s0up4200)
- d96201b: chore(ci): tune CodeRabbit and Codex reviews (#3007) (@s0up4200)
- 94027de: chore(deps): bump compression from 1.8.1 to 1.8.2 in /documentation (#3040) (@dependabot[bot])
- 110b128: chore(deps): bump image-size from 2.0.2 to 2.0.4 in /documentation (#2828) (@dependabot[bot])
- a8105cd: chore(deps): bump joi from 17.13.4 to 17.13.8 in /documentation (#2911) (@dependabot[bot])
- 737a76e: chore(deps): bump proxy-addr from 2.0.7 to 2.0.8 in /documentation (#3041) (@dependabot[bot])
- 1dc6ea8: chore(deps): bump serialize-javascript from 7.1.1 to 7.1.2 in /web (#2912) (@dependabot[bot])
- 7d544ac: chore(deps): bump shell-quote from 1.10.0 to 1.12.0 in /documentation (#3039) (@dependabot[bot])
- 855d6bd: chore(deps): bump the github group with 3 updates (#2842) (@dependabot[bot])
- 7c5c2d2: chore(deps): bump the golang group with 18 updates (#2867) (@dependabot[bot])
- bf9a39e: chore(deps): bump the golang group with 3 updates (#2944) (@dependabot[bot])
- e87ce70: chore(deps): bump the npm group in /documentation with 3 updates (#2945) (@dependabot[bot])
- 09d5266: chore(deps): bump the npm group in /web with 13 updates (#2946) (@dependabot[bot])
- 1376850: chore(deps): bump the npm group in /web with 30 updates (#2868) (@dependabot[bot])
- 6044cfd: chore(deps): bump undici from 7.29.0 to 7.30.0 in /documentation (#2883) (@dependabot[bot])
- 4aeb33c: chore(github): add Behaviour change section to PR template (#2845) (@s0up4200)
- 7952b30: chore(i18n): delete 105 dead locale keys and the unused-key allowlist (#2939) (@ColinHebert)
- 5c2d74c: chore(web): delete the English field labels from CONDITION_FIELDS (#2957) (@ColinHebert)
- 6736a8d: chore(web): lint web/scripts (#2829) (@ColinHebert)
- 6aeafdd: ci(lint): fail when a PR changes an existing migration (#3032) (@s0up4200)
- 62baed8: ci(triage): pin claude-code-action to a commit with Opus 5.5 support (#2816) (@s0up4200)
- dd564f8: ci(triage): run discussion triage on Opus 5.5 (#2815) (@s0up4200)
- 1b74896: ci(windows): add compile gate for packages and tests (#3057) (@s0up4200)
- 81cba34: ci: require ! in the title of a breaking PR (#2981) (@s0up4200)
- c79083f: docs(agents): add the needs-grilling triage role (#3038) (@s0up4200)
- 4a0230e: docs(agents): drop the one-PR-per-feature rule (#2903) (@s0up4200)
- 74cee8e: docs(agents): fix stale docs/ rule, gh issue read command, and web CI gates (#2960) (@s0up4200)
- 863552a: docs(agents): route backend paths through backend.Paths() (#3017) (@s0up4200)
- b493401: docs(agents): trim the steering files and fix stale claims (#3031) (@s0up4200)
- 2a4aa1a: docs(automations): note that hardlink scope does not detect reflinks (#3013) (@s0up4200)
- 63a3e0e: docs(automations): note that hardlink scope does not detect reflinks (#3014) (@s0up4200)
- ef2a516: docs(metrics): expand Grafana observability dashboard (#2803) (@YouKyi)
- 3d73e12: docs(seo): descriptive titles on nine short doc pages (#2780) (@s0up4200)
- 544495c: docs: add agent pointers (#2969) (@s0up4200)
- 2ec048b: docs: add guide to move qui to a new server (#2893) (@s0up4200)
- 6a89316: docs: rename CONTEXT.md to GLOSSARY.md (#3010) (@s0up4200)
- dd261fd: fix(CI) testdb: add a sequence counter to Postgres test schema names (#2798) (@nitrobass24)
- 4508136: perf(fsops): walk sibling directories concurrently over sftp (#2961) (@nitrobass24)
- 0e83305: perf(tests): share the migrated SQLite template across test processes (#2864) (@s0up4200)
- 3e14b69: refactor(automations): build one evaluation context for live run, preview, and dry run (#2926) (@s0up4200)
- 7cadc23: refactor(automations): drop trackerDomains, keep trackerPattern (#2963) (@s0up4200)
- 1c2280f: refactor(automations): validate rules in one place and walk action conditions once (#2924) (@s0up4200)
- 30ad072: refactor(crossseed): classify content type with pkg/releases (#2843) (@s0up4200)
- 27f3ded: refactor(crossseed): delete the hidden category and startPaused automation settings (#2938) (@s0up4200)
- 0db3918: refactor(crossseed): one Trigger defaults function for every cross-seed settings read (#2937) (@s0up4200)
- b3ed822: refactor(externalprograms): drop the go-shellquote dependency (#2872) (@s0up4200)
- f3ad120: refactor(fsops): answer filesystem capabilities in one place (#2915) (@s0up4200)
- 1f4d5e6: refactor(fsops): one shared WalkOptions filter for the local and remote walks (#2986) (@s0up4200)
- a78f6fe: refactor(fsops): path dialect on the Backend interface (#2932) (@nitrobass24)
- 2f1731b: refactor(i18n): one locale coverage checker for every locale (#2834) (@ColinHebert)
- c2ea3a3: refactor(proxy): drop the autobrr module dependency (#2871) (@s0up4200)
- ed2ebe1: refactor(web): offer filesystem features from the instance capabilities (#2929) (@s0up4200)
- be83f03: test(crossseed): release-pair corpus across every match path (#3049) (@s0up4200)
- 6918c59: test(fsops): cover the WalkOptions filter edges (#3016) (@com6056)
- 876a4d2: test(orphanscan): build the package tests on Windows (#2978) (@com6056)
- c87a065: test(qbittorrent): pin the single-instance sort order with a golden test (#2992) (@s0up4200)
Full Changelog: v1.30.0...v1.31.0
Docker images
docker pull ghcr.io/autobrr/qui:v1.31.0docker pull ghcr.io/autobrr/qui:latest
What to do next?
- Join our Discord server
Thank you for using qui!