Skip to main content

v1.32.0

Breaking changes​

  • Backup history uses a new storage format. If you plan to go back to an earlier qui after you upgrade, export the backup runs that you need first, because restore fails for every run after a downgrade. Exported archives import on any version.
  • Automation rule JSON is decoded strictly. A create, update, import, or dry run now returns 400 for an unknown key, a key with the wrong case, or a value of the wrong type. An import with intervalSeconds below 60 also fails, so remove these keys from your scripts and saved JSON files.
  • Rule conditions are checked against a field table. qui now rejects an unknown field or operator and a value that the field does not list, such as seeding for a state. To save a changed condition, fix the line that the error names.
  • Group IDs are checked on save. When you edit a rule, the save fails if grouping.defaultGroupId, an action groupId, or a score rule names a group that does not exist, or a built-in ID with different capitals. Change the ID to an existing group in Edit as JSON.

Highlights​

  • Automation rules that say what is wrong. qui lists each mistake in a rule with its JSON path, so a typo no longer saves a rule that never matches.
  • Byte and speed units in your language. Units, decimal separators, and bit rates follow the interface language, for example 1,5 Gio in French.
  • Lighter backups. Each backup stores only the torrents that changed, so backups write about 53x less WAL on Postgres.
  • Update guidance for every setup. Install update shows on every setup, and the dialog explains why automatic installation is unavailable and gives manual steps.
  • Hardlink and cross-seed fixes. Delete rules on HARDLINK_SCOPE_CROSS now delete the matching torrents, and season-less batches cross-seed against tracker names with a year.
  • Fixes and polish. Row actions stay in view in wide tables, a license portal link at the activation limit, correct size labels on the backups page, fewer cached search warnings in the log, and a release notes page in the docs.

Changelog​

New Features​

  • feat(automations): check condition fields, operators and values against a field table (#3112) (@s0up4200)
  • feat(automations): decode rule JSON strictly and list every error (#3110) (@s0up4200)
  • feat(automations): generate the JSON reference and check group IDs on save (#3122) (@s0up4200)
  • feat(docs): add a release notes page from each GitHub release (#3099) (@s0up4200)
  • feat(docs): add a release notes page from each GitHub release (#3099) (@s0up4200)
  • feat(docs): show a Discord card for docs and release notes links (#3121) (@s0up4200)
  • feat(release): write release Highlights with Claude before publishing (#3130) (@s0up4200)
  • feat(triage): carry log links from discussions to issues (#3146) (@s0up4200)
  • feat(web): localize byte and speed units (#2835) (@ColinHebert)
  • feat(web): show update guidance for unsupported setups (#3085) (@s0up4200)

Bug Fixes​

  • fix(automations): rebuild the hardlink index once per TTL and check delete scope by kind (#3124) (@s0up4200)
  • fix(crossseed): match season-less batches against tracker names with a year (#3162) (@s0up4200)
  • fix(crossseed): run the partial pool only on local filesystem access (#3079) (@com6056)
  • fix(crossseed): use the backend path dialect for link trees, season packs and local matches (#3140) (@com6056)
  • fix(jackett): log the cached search fallback at trace (#3080) (@s0up4200)
  • fix(sshtest): count a handshake before the client's dial returns (#3092) (@s0up4200)
  • fix(web): keep table row actions in view when the table scrolls sideways (#3096) (@s0up4200)
  • fix(web): send users who hit the license activation limit to the license portal (#3089) (@s0up4200)

Other Changes​

  • chore(ci): add a mojibake check and a review rule for Windows claims (#3084) (@s0up4200)
  • chore(docs): build faster and serve page Markdown from the site (#3102) (@s0up4200)
  • ci(release): run the Release workflow on every PR and skip its jobs for docs-only changes (#3131) (@s0up4200)
  • ci(triage): hand a discussion to a maintainer when logs decide (#3149) (@s0up4200)
  • ci: add a Breaking change section to the PR template and give the Highlights job the claude environment (#3164) (@s0up4200)
  • ci: keep the Claude token in the claude environment (#3161) (@s0up4200)
  • docs(automations): test the JSON examples in CI and fix three sections (#3118) (@s0up4200)
  • docs(proxy): explain how to exclude the proxy path from basic auth (#3163) (@s0up4200)
  • docs(proxy): explain how to exclude the proxy path from basic auth (#3163) (@s0up4200)
  • docs(triage): prove a cause live before a comment names it (#3160) (@s0up4200)
  • docs: describe the tracker URL edit and the episode cleanup after a season pack (#3137) (@s0up4200)
  • perf(backups): store each torrent's backup state once across runs (#2694) (@ColinHebert)
  • refactor(web): generate the query builder field constants from the Go field table (#3128) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.31.1...v1.32.0

v1.31.1

Highlights​

  • Update from the web UI. On v1.31.0, click Install update and qui updates and restarts itself. The old version stays as a .bak file for a rollback. Docker users pull the image as always.
  • Steady torrent list on phones. It no longer bounces at the end of the list.
  • Labels for every torrent state. For example (F) Fetching Metadata instead of forcedMetaDL.
  • Windows, updating from 1.30? v1.30.0 does not add the new tray app by itself. Copy qui-tray.exe from the zip into the folder with qui.exe once unless you did already. After that, each update replaces both files.

Changelog​

Bug Fixes​

  • fix(mobile): stop the torrent list from jumping when the footer nav hides (#3067) (@s0up4200)
  • fix(web): take t() fallbacks from the locale files instead of hardcoded English (#2837) (@ColinHebert)

Other Changes​

  • chore(github): add CODEOWNERS (#3069) (@s0up4200)
  • ci(triage): triage only bug discussions automatically (#3075) (@s0up4200)
  • docs: explain the missing qui-tray.exe after an update from 1.30 and add a generic webhook example (#3065) (@s0up4200)
  • docs: prefer a proven library for spec-defined logic (#3068) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.31.0...v1.31.1

v1.30.0

Breaking​

Enter your Dodo key again if you still had a Polar license. This release removes the legacy Polar license support.

Highlights​

  • Hardlink mode no longer risks your source files. When a hardlinked file failed its recheck, auto-resume could still start the torrent. qBittorrent then wrote the missing pieces into the hardlink and changed the file that your original torrent seeds. qui now leaves such torrents paused and names the file in the log and in a new "Resume" row of the season pack history.
  • Stricter login and sessions. qui limits password login to five failures per minute, and a password change signs out every session. The new sessionCookieSecure option marks the session cookie Secure for HTTPS setups. Write requests that use the session cookie now need an X-Requested-With header. The web UI sends it, but a script that reuses a browser cookie must use an API key instead. The new optional allowedHosts setting blocks DNS rebinding and is off by default.
  • Less database churn, most of all on Postgres. File cache refreshes now write only the rows that changed. One homelab measured 16% of its cluster WAL on unchanged rows, now zero, and SQLite runs the same refresh 3-4x faster. RSS polls no longer rewrite unchanged feed items. Concurrent file fetches no longer fail with "deadlock detected". Backup archive filenames stay the same across runs for torrents with several trackers.
  • New automation condition: Season pack status. Each torrent is a pack, an episode that a pack covers, or an episode with no pack. An any-instance variant looks across all your instances. Use it to tag season packs or to clean up episodes that a pack already covers. Matching is strict, so a 720p pack never counts as cover for a 1080p episode. Dry runs now report the same torrents as the impact preview instead of stopping at the delete cooldown gates.
  • Gazelle cross-seed finds older uploads. RED and OPS hash lookups also try the legacy PTH and APL source flags. Uploads from before the renames now match by infohash instead of a filename search. A failed lookup (tracker down, rate limited) no longer starts the torrent's cooldown.
  • Catalan is language number eleven. The Catalan translator for the *arr apps contributed it. Czech now renders counts 2-4 instead of English, and several plural strings are fixed across locales.
  • Fixes and polish. getqui.com now embeds a live demo of the real frontend with 10,000 synthetic torrents and no login. The Create Torrent source picker lists files and works with Windows paths. Downloads from the Content tab show the real size and progress and can resume. "Export all" under a parent category includes its subcategories. Select-all in cross-seed mode follows the visible rows. The tab icon no longer stays blank in Firefox background tabs. --config-dir with no config.toml writes a default instead of a crash.

Thanks to ColinHebert, nitrobass24, thejdubb02, schwarz, and Kisnov for contributing to this release.

Changelog​

New Features​

  • feat(automations): season pack status condition fields (#2770) (@s0up4200)
  • feat(docs): live demo on the getqui.com landing page (#2767) (@s0up4200)
  • feat(i18n): add Catalan (ca) translation (#2682) (@Kisnov)
  • feat(models): add SSH schema and HasFilesystemAccess helper (#1917) (@nitrobass24)
  • feat(security): add optional allowed hosts restriction (#2642) (@s0up4200)
  • feat(sshpool): SSH connection layer, host-key pinning, and credential endpoints (#2728) (@nitrobass24)

Bug Fixes​

  • fix(api): preserve content length for torrent file downloads (#2766) (@schwarz)
  • fix(auth): harden the API and session boundary (#2548) (@s0up4200)
  • fix(automations): dry run reports the same torrents as the preview (#2775) (@s0up4200)
  • fix(backups): keep archive filenames stable across runs (#2689) (@ColinHebert)
  • fix(config): write default config.toml when --config-dir has none (#2693) (@ColinHebert)
  • fix(crossseed): keep a hardlink resume out of a linked file that failed its recheck (#2688) (@s0up4200)
  • fix(crossseed): stop rewriting unchanged feed items on every poll (#2692) (@ColinHebert)
  • fix(crossseed): try legacy source flags in Gazelle hash lookups and keep failed lookups eligible (#2765) (@s0up4200)
  • fix(docs): open the demo in a new tab on phones (#2776) (@s0up4200)
  • fix(filesmanager): upsert cache rows in a fixed order to avoid Postgres deadlocks (#2690) (@ColinHebert)
  • fix(http): send the real qui version in the Sonarr, Radarr, and Gazelle User-Agent (#2706) (@s0up4200)
  • fix(i18n): render Czech instead of English for counts 2-4 (#2681) (@ColinHebert)
  • fix(i18n): render the plural form for the ignored-paths count (#2680) (@ColinHebert)
  • fix(i18n): restore meaning dropped by three abbreviated translations (#2701) (@ColinHebert)
  • fix(orphanscan): say items, not files, where a count includes directories (#2675) (@ColinHebert)
  • fix(torrents): list files in the Create Torrent source path picker (#2744) (@s0up4200)
  • fix(web): serve a static favicon so Firefox background tabs are not blank (#2671) (@thejdubb02)

Other Changes​

  • chore(deps): bump anthropics/claude-code-action from 1.0.216 to 1.0.223 in the github group (#2758) (@dependabot[bot])
  • chore(deps): bump go-qbittorrent to v1.19.0 (#2764) (@s0up4200)
  • chore(i18n): remove unreferenced locale blocks and check for new ones (#2684) (@ColinHebert)
  • ci(triage): reduce backlog sweep to five discussions (#2737) (@s0up4200)
  • docs(seo): descriptive titles on every page, sitemap lastmod, and cross-links (#2721) (@s0up4200)
  • docs(seo): match Netlify trailing slashes, add robots.txt, and give pages descriptive titles (#2708) (@s0up4200)
  • docs(seo): match Netlify trailing slashes, add robots.txt, and give pages descriptive titles (#2715) (@s0up4200)
  • docs: add where to run qui, the cross-seed scope, filter exclusion, and the Windows task limit (#2697) (@s0up4200)
  • perf(filesmanager): skip unchanged rows before the files cache upsert (#2691) (@ColinHebert)
  • refactor(crossseed): one gatherer for the Torznab search passes (#2755) (@s0up4200)
  • refactor(crossseed): one link-mode body behind hardlink and reflink (#2753) (@s0up4200)
  • refactor(crossseed): the matching rules leave Service (#2756) (@s0up4200)
  • refactor(license): remove the Polar license provider (#2707) (@s0up4200)
  • refactor(torrents): one selection resolver for copy and bulk actions (#2777) (@s0up4200)
  • refactor(web): one filter serializer and one select-all scope for the torrent table (#2779) (@s0up4200)
  • refactor: consumer-side interfaces over SyncManager in orphanscan, automations, and the torrents handler (#2752) (@s0up4200)
  • test(i18n): run every scripts/*.test.mjs in check:i18n (#2683) (@ColinHebert)

Full Changelog: https://github.com/autobrr/qui/compare/v1.29.0...v1.30.0

v1.29.0

Important​

  • Credential encryption. The key protecting stored passwords and API keys is now derived from the sessionSecret with HKDF. qui re-encrypts every stored credential on first start, so take a database backup before upgrading: an older qui cannot read the new format.

Highlights​

  • Far fewer requests to qBittorrent on big instances. Startup no longer fires one tracker request per torrent, and an instance whose first capability probe failed no longer stays on that per-torrent path for every refresh after (needs qBittorrent 5.1+), the hardlink index reuses cached file lists instead of re-downloading every torrent's files on each rebuild, reannounce filters candidates before fetching trackers and caps at four jobs per instance, and dead tracker favicon hosts back off up to 24 hours instead of hammering DNS. The live stream also keeps your rows on screen through a short error instead of blanking and reconnecting.
  • Orphan scan reaches further, deletes safer. New per-instance switches under Scan Scope: Scan Default Save Path, Including Category Paths, and Delete Abandoned Directories. Stray files in the default save path are finally visible, and empty directories left behind by moves show up in the preview. All three are off by default. A scan that could not reach every path is marked Partial scan and skips automatic cleanup.
  • Cross-seed: anime, games, manual season packs. Anime episodes match across numbering schemes (absolute - 81 versus S04E15) via the episode map from a linked Sonarr. Releases split into more than 100 RAR volumes (.s01 was read as season 1) now apply. The manual match dialog can assemble a season pack from several episodes you already seed. Matches with 100% overlap no longer sit paused for an hour, the Library Scan card counts honestly with a Cross-seeds added line, and an indexer answering a download with a login page no longer poisons the torrent cache.
  • BDInfo reports from the Content tab. Every Blu-ray Disc gets a BDInfo action that scans in the background and hands you the Quick Summary and Forum blocks for upload forms (needs Local Filesystem Access).
  • Torrent table and Content tab. A new toolbar button stretches the Name column to fill the table so the desktop view stops scrolling sideways (on by default, toggle it off to keep your own Name width), the Toggle columns menu has drag handles for reordering, and the Content tab sorts by Name, Progress, Size, or Priority.
  • Automations. New Has Skipped Files condition, the category action can target Uncategorized, a slow or failed preview no longer blocks enabling a workflow (Save without preview), and overnight ranges finally work: System Hour between 20 and 6 wraps past midnight instead of matching nothing. Hardlink and missing-file conditions never match when qui cannot read the files.
  • Fixes and polish. Unified view actions on a torrent that exists on several instances no longer fan out to all of them (the cross-seed run API also renames torrentsAdded to crossSeedsAdded), the sidebar Clear filters button gets a dropdown for sidebar, column, or filters and search, magnet links opened through the browser honour the instance's save path and TMM settings, log viewer and orphan preview timestamps follow your Date & Time preferences, and manual backups no longer error after visiting Automations.

Want a new look? https://github.com/autobrr/qui-community-themes has 24 community themes (Tokyo Night, Dracula, Gruvbox, Rosé Pine, and more). Drop the .css into your themes dir and hit Refresh under Settings > Themes. Sideloading needs a licence key, PRs with your own theme are welcome!

Changelog​

New Features​

  • feat(arr): read and cache the Sonarr episode map (#2537) (@s0up4200)
  • feat(automations): add HAS_SKIPPED_FILES condition (#2518) (@s0up4200)
  • feat(crossseed): assemble season packs from manually selected episodes (#2641) (@s0up4200)
  • feat(crossseed): search and match anime episodes through the episode map (#2541) (@s0up4200)
  • feat(orphanscan): preview every directory a run removes (#2666) (@ColinHebert)
  • feat(orphanscan): scan beyond torrent save paths and clean up empty directories (#2588) (@ColinHebert)
  • feat(security): HKDF-derive the credential key and version ciphertext (#2652) (@com6056)
  • feat(torrents): BDInfo Disc reports in the Content tab (#2570) (@s0up4200)
  • feat(torrents): name column stretch mode and menu column reordering (#2674) (@gl0ryus)
  • feat(torrents): sort the Content tab by name, size, progress, or priority (#2547) (@s0up4200)

Bug Fixes​

  • fix(automations): allow workflows to clear categories (#2660) (@s0up4200)
  • fix(automations): fold Unicode in CONTAINS_IN name matching (#2648) (@thejdubb02)
  • fix(automations): keep unknown filesystem conditions from matching (#2643) (@s0up4200)
  • fix(automations): let a slow or failed preview not block enabling a workflow (#2566) (@s0up4200)
  • fix(automations): wrap BETWEEN ranges on clock fields (#2668) (@s0up4200)
  • fix(backups): scope run cache updates to backup queries (#2517) (@s0up4200)
  • fix(ci): limit Postgres job to integration tests (#2659) (@s0up4200)
  • fix(crossseed): fetch file lists only for contested duplicate groups (#2573) (@s0up4200)
  • fix(crossseed): reconcile run counters and count cross-seeds added separately (#2577) (@s0up4200)
  • fix(crossseed): resume fast verification rechecks that finish between polls (#2558) (@thejdubb02)
  • fix(crossseed): search and match absolute-numbered anime episodes (#2533) (@s0up4200)
  • fix(crossseed): stop rewriting the search run results blob on every candidate (#2574) (@s0up4200)
  • fix(database): register the sqlite connection hook at init (#2589) (@ColinHebert)
  • fix(dirscan): prune tracked files that no longer exist on disk (#2587) (@ColinHebert)
  • fix(gojackett): stop retrying once the caller's context ends (#2650) (@nitrobass24)
  • fix(i18n): render the correct plural form for six count strings (#2677) (@ColinHebert)
  • fix(jackett): reject non-torrent download responses (#2640) (@s0up4200)
  • fix(qbittorrent): key the peer fetch on the lowered hash (#2612) (@s0up4200)
  • fix(qbittorrent): serialize peer fetch and merge per torrent (#2611) (@s0up4200)
  • fix(releases): keep S23E19.Episode.1174 names as one episode (#2543) (@s0up4200)
  • fix(search): correct the glob examples in the search help (#2670) (@s0up4200)
  • fix(sse): retain page snapshots and avoid unnecessary reconnects (#2656) (@s0up4200)
  • fix(stream): prevent desync after missed baselines (#2506) (@Zariel)
  • fix(torrents): make filter clearing scopes explicit (#2595) (@s0up4200)
  • fix(torrents): require targets for unified bulk actions (#2530) (@s0up4200)
  • fix(torrents): seed add-torrent options from preferences on a cold mount (#2653) (@s0up4200)
  • fix(torrents): send instance targets for recheck and reannounce in the unified view (#2528) (@s0up4200)
  • fix(torrents): stop re-applying the search client-side in cross-seed filter mode (#2532) (@s0up4200)
  • fix(trackericons): back off dead tracker hosts and stop the DNS flood (#2549) (@s0up4200)
  • fix(web): dismiss path autocomplete on outside pointer interaction (#2526) (@s0up4200)
  • fix(web): format timestamps with date/time preferences everywhere (#2519) (@s0up4200)

Other Changes​

  • chore(deps): bump anthropics/claude-code-action from 1.0.200 to 1.0.210 in the github group (#2562) (@dependabot[bot])
  • chore(deps): bump the github group with 3 updates (#2679) (@dependabot[bot])
  • chore(i18n): remove unreferenced locale blocks (#2676) (@ColinHebert)
  • chore(repo): remove unused workflows, artwork, and ReDoc (#2635) (@s0up4200)
  • ci(test): cache the embedded Postgres binary (#2673) (@s0up4200)
  • docs(agents): add code comment rules (#2647) (@s0up4200)
  • docs(agents): one PR per feature, slice commits instead of stacked PRs (#2569) (@s0up4200)
  • docs(api): update the torrent peers response schema (#2614) (@s0up4200)
  • docs(backups): state what a snapshot does not contain (#2591) (@s0up4200)
  • docs(context): add search candidate, cross-seed added, and due candidate (@s0up4200)
  • docs: add Disc report glossary terms and ADR 0003 (#2568) (@s0up4200)
  • docs: require before/after performance results in PRs (#2655) (@s0up4200)
  • perf(automations): rebuild the hardlink index from cached file lists (#2590) (@s0up4200)
  • perf(filesmanager): skip no-op writes in torrent files cache upsert (#2586) (@ColinHebert)
  • perf(proxy): remove duplicate peer fetch (#2604) (@s0up4200)
  • perf(qbittorrent): enable bulk tracker fetching after client initialization (#2584) (@s0up4200)
  • perf(qbittorrent): expire unused peer sync managers (#2605) (@s0up4200)
  • perf(qbittorrent): share one app info refresh across concurrent callers (#2593) (@s0up4200)
  • perf(reannounce): narrow tracker fetches and bound jobs (#2596) (@s0up4200)
  • perf(torrents): answer single-hash requests from the cache index (#2594) (@s0up4200)
  • refactor(api): drop the dead full_update field from the peers response (#2606) (@s0up4200)
  • refactor(backend): remove unused APIs and helpers (#2633) (@s0up4200)
  • refactor(cache): migrate ttlcache to autobrr/go-cache (#2672) (@s0up4200)
  • refactor(crossseed): remove inactive auto-resume policy flag (#2636) (@s0up4200)
  • refactor(filesmanager): remove unused cache paths and chunk helpers (#2630) (@s0up4200)
  • refactor(i18n): consolidate locale coverage checkers (#2627) (@s0up4200)
  • refactor(jackett): remove unused APIs and backup plumbing (#2629) (@s0up4200)
  • refactor(notifications): reuse production Notifiarr payload builders (#2639) (@s0up4200)
  • refactor(web): remove unused APIs and redundant helpers (#2634) (@s0up4200)
  • test(backend): remove copied and inactive test code (#2628) (@s0up4200)
  • test(i18n): replace source checks with behavior tests (#2638) (@s0up4200)
  • test(postgres): run backend suite with embedded Postgres (#2616) (@s0up4200)
  • test(qbittorrent): pin list hydration to never call torrents/trackers (#2592) (@s0up4200)
  • test(torznab): cover HTTP 500 and unreachable outcomes of the indexer test (#2422) (@nitrobass24)

Full Changelog: https://github.com/autobrr/qui/compare/v1.28.0...v1.29.0

v1.28.0

Changelog​

New Features​

  • feat(crossseed): add manual match against a chosen target torrent (#2499) (@s0up4200)
  • feat(crossseed): coordinate pooled partial completion (#2356) (@Audionut)

Bug Fixes​

  • fix(automations): match tracker conditions against every tracker of a torrent (#2487) (@s0up4200)
  • fix(ci): move the triage sweep cron off the top of the hour (@s0up4200)
  • fix(ci): raise the triage turn cap to 80 (#2474) (@s0up4200)
  • fix(ci): re-trigger triage on needs-info comments from any human (#2497) (@s0up4200)
  • fix(ci): triage follow-ups and the ADR for the Claude subscription (#2475) (@s0up4200)
  • fix(crossseed): align webhook notifications with apply outcomes (#2463) (@nuxencs)
  • fix(crossseed): correct Dir Scan mode guidance (#2476) (@s0up4200)
  • fix(crossseed): use ARR alternate titles for announce matching (#2494) (@s0up4200)
  • fix(database): drop unused timestamp indexes on torrent_files_cache/sync (#2455) (@ColinHebert)
  • fix(database): truncate cross_seed_feed_items.last_seen_at to day precision (#2456) (@ColinHebert)
  • fix(docs): restore list bullets by removing Tailwind (#2471) (@s0up4200)
  • fix(indexers): ignore missing requested indexers (#2458) (@s0up4200)
  • fix(indexers): keep activity lists in a stable order (#2485) (@s0up4200)
  • fix(indexers): skip caps sync for disabled indexers (#2461) (@s0up4200)
  • fix(jackett): let tests override the torznab request interval (#2470) (@s0up4200)
  • fix(jackett): schedule opportunistic cleanup of torznab indexer latency (#2457) (@ColinHebert)
  • fix(logging): lower routine search events to debug (#2460) (@s0up4200)
  • fix(mobile): keep torrent actions sheet and limit dialogs inside the viewport (#2510) (@s0up4200)
  • fix(notifications): route Notifiarr test events (#2453) (@nuxencs)
  • fix(orphanscan): drop scan roots covered by ignore paths (#2486) (@s0up4200)
  • fix(reannounce): skip the scan when the scope cannot match (#2491) (@s0up4200)
  • fix(search): require an explicit search type (#2452) (@s0up4200)
  • fix(search): rerun recent searches with their restored indexer selection (#2451) (@s0up4200)
  • fix(torrents): separate mobile and desktop view modes (#2459) (@s0up4200)
  • fix(torrents): support qBittorrent I2P peers (#2466) (@s0up4200)
  • fix(torrents): wrap I2P peer addresses on mobile (@s0up4200)
  • fix(torznab): honor upstream rate limits (#2462) (@s0up4200)
  • fix(web): avoid canvas readback for dynamic favicon (#2503) (@heathlarsen)

Other Changes​

  • chore(deps): bump the github group with 2 updates (#2468) (@dependabot[bot])
  • chore(deps): bump the golang group with 12 updates (#2515) (@dependabot[bot])
  • chore(deps): bump the npm group in /web with 26 updates (#2516) (@dependabot[bot])
  • docs(agents): require PR documentation review (#2467) (@s0up4200)
  • docs(automations): add example for finding duplicate releases (#2493) (@s0up4200)
  • docs(triage): key the promotion rule on the enhancement label (@s0up4200)
  • docs(triage): promote bug reports only, feature requests stay a human call (@s0up4200)
  • docs(triage): standing reason for enhancements, bug-only gate on the promote recipe (@s0up4200)
  • docs: revamp user documentation (#2450) (@s0up4200)
  • other(ci): triage discussions end to end without a human (#2472) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.27.0...v1.28.0

v1.27.0

Changelog​

New Features​

  • feat(cli): migrate Deluge, rTorrent and Transmission to qBittorrent with stats (#616) (@zze0s)
  • feat(crossseed): fall back to MKV metadata IDs when ARR supplies none (#2398) (@s0up4200)
  • feat(crossseed): search by MKV tag IDs first when no arr supplies them (#2404) (@s0up4200)
  • feat(crossseed): show why a manual search accepted or rejected candidates (#2427) (@s0up4200)
  • feat(docker): add PUID/PGID support (#858) (@s0up4200)
  • feat(docs): redesign landing page (#2347) (@s0up4200)
  • feat(fsops): add Backend interface, local implementation, and pool resolver (#1914) (@nitrobass24)
  • feat(i18n): add Traditional Chinese (zh-TW) translation (#2367) (@hkaden)
  • feat(mobile): add torrent export to actions sheet (#2421) (@s0up4200)
  • feat(preferences): add slow torrent settings to the queue form (#2428) (@s0up4200)
  • feat(settings): add client_settings key-value store and API (#2407) (@s0up4200)
  • feat(settings): sync UI state to the database (#2412) (@s0up4200)
  • feat(settings): sync localStorage user settings to the database (#2409) (@s0up4200)
  • feat(settings): sync table preferences and sidebar filters to the database (#2410) (@s0up4200)
  • feat(themes): store theme selection in the database (#2384) (@s0up4200)
  • feat(torrents): archive large exports (#2378) (@s0up4200)

Bug Fixes​

  • fix(automations): ignore absent skipped files in hardlink scans (#2370) (@s0up4200)
  • fix(automations): verify files before a cross-seed delete expands (#2351) (@s0up4200)
  • fix(build): add Windows executable extension (#2414) (@Audionut)
  • fix(crossseed): disable download path on recheck adds so complete cross-seeds don't stall at 0% (#2432) (@statelycurmudgeon)
  • fix(crossseed): don't reuse cached indexer sets across content types (#2420) (@s0up4200)
  • fix(crossseed): log repeated size-mismatch rejections at trace (#2371) (@s0up4200)
  • fix(crossseed): omit categories from ID-driven searches (#2395) (@s0up4200)
  • fix(crossseed): recover exact-size matches with compatible names (#2345) (@s0up4200)
  • fix(crossseed): retry per indexer when nothing usable comes back (#2400) (@s0up4200)
  • fix(crossseed): silence content fallback log (#2401) (@s0up4200)
  • fix(crossseed): trust disc layout for content detection (#2359) (@s0up4200)
  • fix(database): resolve the sqlite→postgres import against the connection's schema (#2383) (@nitrobass24)
  • fix(docker): honor configured non-root user (#2447) (@s0up4200)
  • fix(docker): make the standalone Dockerfile build again (#2416) (@s0up4200)
  • fix(lint): resolve the correctness findings in the golangci-lint backlog (#2386) (@s0up4200)
  • fix(mobile): make Back close torrent details instead of leaving the page (#2426) (@s0up4200)
  • fix(orphanscan): protect metadata-less paths (#2445) (@s0up4200)
  • fix(orphanscan): skip torrents without metadata (#2441) (@s0up4200)
  • fix(qbittorrent): treat request timeouts as slow instances, not dead ones (#2408) (@s0up4200)
  • fix(reflinktree): track root before support probe (#2341) (@s0up4200)
  • fix(rss): improve large feed performance (#2438) (@s0up4200)
  • fix(settings): preserve latest cross-tab write (#2444) (@s0up4200)
  • fix(ui): show sideloaded themes in the mobile theme picker (#2373) (@s0up4200)
  • fix(ui): show unknown for unavailable disk space (#2348) (@s0up4200)
  • fix(web): resolve dynamic imports relative to chunk for base_url deployments (#2442) (@MaKTaiL)

Other Changes​

  • chore(build): bump Go to 1.27 (#2369) (@s0up4200)
  • chore(deps): bump anthropics/claude-code-action from 1.0.189 to 1.0.193 in the github group (#2394) (@dependabot[bot])
  • chore(deps): bump go-mediainfo to v0.8.0 (#2399) (@s0up4200)
  • chore(docs): require field testing and refuse Gemini-authored PRs (#2391) (@s0up4200)
  • chore(github): trim pull request template (#2390) (@s0up4200)
  • chore(jackett): raise torznab torrent cache TTL to 24h (#2372) (@s0up4200)
  • chore(lint): clear the mechanical findings in the golangci-lint backlog (#2387) (@s0up4200)
  • chore(lint): take the backlog to zero and gate on the whole repo (#2393) (@s0up4200)
  • chore: bump golangci-lint to v2.13.0 and apply go fix for Go 1.27 (#2381) (@nitrobass24)
  • chore: merge main into develop (@s0up4200)
  • docs(agents): scope local test runs and keep caches off tmpfs (#2430) (@s0up4200)
  • docs(agents): stack cross-layer PRs and trim diffs before commit (#2419) (@s0up4200)
  • docs(config): explain UI preference persistence (#2443) (@s0up4200)
  • docs(crossseed): explain that Prowlarr indexer filters also apply (#2429) (@s0up4200)
  • docs(metrics): document exporter and add Grafana dashboard (#2431) (@s0up4200)
  • docs: correct tracker customizations location and document the feature (#2364) (@s0up4200)
  • docs: explain when to pick postgres over sqlite (#2424) (@s0up4200)
  • docs: link the community themes repository (#2377) (@s0up4200)
  • docs: move macOS Container install to the docs site (#2357) (@s0up4200)
  • perf(sse): compress stream sessions with gzip (#2358) (@s0up4200)
  • perf(torrents): trim magnet_uri from the torrent list payload (#2360) (@s0up4200)
  • perf(web): migrate torrent tables to TanStack Table v9 (#2379) (@s0up4200)
  • refactor(services): use fsops.Backend for all filesystem operations (#1915) (@nitrobass24)
  • refactor(themes): serve built-in themes from the backend (#2385) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.26.0...v1.27.0

v1.26.0

Changelog​

New Features​

  • feat(automations): add CROSS_SEED_TAGS condition across same-instance cross-seeds (#2302) (@nitrobass24)
  • feat(automations): add inside-qBittorrent hardlink scope condition (#2194) (@s0up4200)
  • feat(backups): add a configurable backup directory (#2304) (@s0up4200)
  • feat(crossseed): let file extensions correct the search category (#2305) (@s0up4200)
  • feat(crossseed): map qBittorrent categories to search categories (#2306) (@s0up4200)
  • feat(dashboard): add session download/upload columns to server statistics and tracker breakdown (#2149) (@BeliasCC)
  • feat(dirscan): add skip individual episodes to scan directories (#2248) (@s0up4200)
  • feat(dirscan): retry a search that found nothing usable (#2247) (@s0up4200)
  • feat(mobile): auto-hide bottom bars, resurface view mode, improve selection visibility (#2265) (@s0up4200)
  • feat(orphanscan): show the paths a scan run covered (#2297) (@s0up4200)
  • feat(themes): add spreadsheet disguise themes (#2315) (@s0up4200)
  • feat(themes): streamline theme selection (#2334) (@s0up4200)
  • feat(torrents): add total session downloaded/uploaded in the footer (#2148) (@BeliasCC)
  • feat(web): add shared FieldHelp component for field tooltips (#2284) (@s0up4200)

Bug Fixes​

  • fix(arr): keep live positive ID-cache rows over concurrent negative writes (#2301) (@nitrobass24)
  • fix(backups): apply restore category and tags on add only (#2295) (@s0up4200)
  • fix(backups): restore category-managed save paths (#2258) (@ultronknowhere)
  • fix(crossseed): classify a disc rip as a movie (#2337) (@s0up4200)
  • fix(crossseed): parse the file name instead of its whole path (#2335) (@s0up4200)
  • fix(crossseed): read a slash in a release title as a separator (#2249) (@s0up4200)
  • fix(crossseed): recover exact matches lost to failed ARR lookups and rename-only recheck skips (#2298) (@nitrobass24)
  • fix(crossseed): replace anacrolix/torrent with autobrr/go-torrent (#2264) (@arjunrn)
  • fix(crossseed): roll back only the files a failed link attempt created (#2287) (@s0up4200)
  • fix(dashboard): tighten the dashboard on mobile (#2333) (@s0up4200)
  • fix(dirscan): drop the year from the indexer search query (#2246) (@s0up4200)
  • fix(jackett): warn when a search runs without indexer capabilities (#2296) (@s0up4200)
  • fix(mobile): keep the last card clear of the selection bar and guard footer scroll registration (#2279) (@s0up4200)
  • fix(orphanscan): compare paths case-insensitively (#2316) (@s0up4200)
  • fix(qbittorrent): read the tracker mapping before publishing it (#2332) (@s0up4200)
  • fix(releases): read a multi-episode range as a pack (#2253) (@s0up4200)
  • fix(torrents): keep stored view mode when a restricted consumer mounts (#2271) (@s0up4200)
  • fix(web): add force start to mobile actions (#2268) (@VsnGamer)
  • fix(web): emit CSS asset urls relative to the stylesheet (#2276) (@s0up4200)
  • fix(web): offer both toggle directions when selection is partly loaded (#2278) (@s0up4200)
  • fix(web): translate hardcoded strings in CrossSeedPage (#2275) (@OlziYT)
  • fix: land on the app when entering through /index.html under a base URL (#2277) (@s0up4200)

Other Changes​

  • chore(crossseed): log byte sizes in season pack size mismatch demotions (#2270) (@s0up4200)
  • chore(deps): bump docker/login-action from 4.5.2 to 4.6.0 in the github group (#2261) (@dependabot[bot])
  • chore(deps): bump the github group with 2 updates (#2330) (@dependabot[bot])
  • chore(deps): bump the npm group across 1 directory with 15 updates (#2227) (@dependabot[bot])
  • chore(deps): bump the npm group in /web with 7 updates (#2226) (@dependabot[bot])
  • chore(docs): fix go.mod and package.json links in contributing guide (@s0up4200)
  • chore(docs): forbid tracker links and copied torrent names in published text (#2273) (@s0up4200)
  • chore(docs): remove collaborators-only PR restriction (@s0up4200)
  • chore(github): add pull request template (#2281) (@s0up4200)
  • docs(readme): add qBittorrent interface alternatives (#2317) (@s0up4200)
  • perf(automations): update the hardlink index instead of rebuilding it (#2241) (@s0up4200)
  • perf(crossseed): cache the AKA title parse (#2254) (@s0up4200)
  • perf(instances): report all-time totals from transfer-info (#2240) (@s0up4200)
  • perf(qbittorrent): read maindata fields without cloning the torrent map (#2329) (@s0up4200)
  • perf(torrents): keep one object identity per unchanged row so table rows can memoize (#2263) (@s0up4200)
  • perf(torrents): make list, search and sort faster on large libraries (#2319) (@s0up4200)
  • refactor(automations): move field help into label tooltips (#2293) (@s0up4200)
  • refactor(crossseed): move field help into label tooltips (#2294) (@s0up4200)
  • refactor(crossseed): stop tests from reaching the live gazelle trackers (#2311) (@s0up4200)
  • refactor(instances): move the hand-written field help into label tooltips (#2292) (@s0up4200)
  • refactor(mobile): share the selection bar condition and drop its dead transition (#2280) (@s0up4200)
  • refactor(web): delete field help that repeats its own label or tooltip (#2288) (@s0up4200)
  • refactor(web): move notification and RSS field help into label tooltips (#2291) (@s0up4200)
  • refactor(web): move preference field help into label tooltips (#2290) (@s0up4200)
  • test(crossseed): stop the suite from calling the real trackers (#2309) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.25.0...v1.26.0

v1.25.0

Changelog​

New Features​

  • feat(crossseed): add skip individual episodes option to library search runs (#2216) (@s0up4200)
  • feat(crossseed): assemble season packs from seeded episodes in automation flows (#2211) (@s0up4200)
  • feat(crossseed): curb season pack churn in automation flows (#2213) (@s0up4200)
  • feat(crossseed): match torrents whose files pair by exact size (#2219) (@s0up4200)
  • feat(crossseed): page RSS feeds until a page is fully known (#2207) (@s0up4200)
  • feat(crossseed): replace the auto-resume percentage rule with a byte limit (#2228) (@s0up4200)
  • feat(crossseed): rescue exact-size title mismatches (#2230) (@s0up4200)
  • feat(crossseed): search for season packs of seeded episodes in library runs (#2212) (@s0up4200)
  • feat(crossseed): track seeded search history per indexer (#2218) (@s0up4200)
  • feat(filters): saved filter views (#2193) (@s0up4200)
  • feat(indexers): remember Prowlarr and Jackett connections in the discover dialog (#2231) (@s0up4200)
  • feat(torrents): navigate the torrent list with arrow keys (#2196) (@s0up4200)

Bug Fixes​

  • fix(crossseed): apply search aliases only to the source torrent (#2221) (@s0up4200)
  • fix(crossseed): clarify the max age setting copy (#2223) (@s0up4200)
  • fix(crossseed): compare the search size band against the full torrent size (#2206) (@s0up4200)
  • fix(crossseed): demote unlinkable season pack episodes to missing (#2215) (@s0up4200)
  • fix(crossseed): match titles whose punctuation scene naming drops (#2191) (@s0up4200)
  • fix(crossseed): recover season structure from files when a search result is applied (#2232) (@s0up4200)
  • fix(crossseed): recover season structure from metainfo files when a torrent is applied (#2235) (@s0up4200)
  • fix(crossseed): retry zero-result searches with alternate titles (#2204) (@s0up4200)
  • fix(crossseed): skip Gazelle lookups for content that RED/OPS cannot host (#2195) (@s0up4200)
  • fix(crossseed): treat one-sided HDR and collection tags as compatible (#2205) (@s0up4200)
  • fix(dirscan): reopen no_match files when new indexers appear (#2199) (@s0up4200)
  • fix(indexers): prevent incorrect autodiscovery updates (#2236) (@s0up4200)
  • fix(logging): make DEBUG the supported diagnostic level (#2239) (@s0up4200)
  • fix(torznab): keep rate-limited caps searches uncovered (#2229) (@s0up4200)
  • fix(torznab): keep structured season/ep params when indexer caps support them (#2214) (@s0up4200)
  • fix(torznab): skip indexers whose caps fetch is rate limited (#2217) (@s0up4200)
  • fix(web): don't hijack Cmd/Ctrl+Shift+A as select-all in torrent table (#2200) (@s0up4200)
  • fix(web): scope overlay blur to the support dialog (#2197) (@s0up4200)

Other Changes​

  • chore(deps): bump docker/login-action from 4.5.1 to 4.5.2 in the github group (#2225) (@dependabot[bot])
  • chore(deps): bump postcss from 8.5.18 to 8.5.23 in /documentation (#2224) (@dependabot[bot])
  • chore(deps): bump the github group with 2 updates (#2203) (@dependabot[bot])
  • chore(deps): bump the golang group with 16 updates (#2209) (@dependabot[bot])
  • chore(deps): bump the npm group in /web with 43 updates (#2210) (@dependabot[bot])
  • ci(release): fold signing smoke test into develop snapshot build (#2201) (@s0up4200)
  • docs(web): correct the vitest setup-file and auto-cleanup rules (#2238) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.24.0...v1.25.0

v1.24.0

Changelog​

New Features​

  • feat(backups): reclaim orphaned torrent cache blobs at startup (#2190) (@nitrobass24)
  • feat(crossseed): detect ReFS block-cloned delete matches (#2183) (@Audionut)
  • feat(crossseed): detect hardlinked cross-seeds in delete dialog (#2180) (@s0up4200)
  • feat(crossseed): match season packs across alternate titles and anime absolute numbering (#2105) (@s0up4200)
  • feat(qbittorrent): detect "torrent has been rejected" as unregistered (#2133) (@s0up4200)
  • feat(web): add Brazilian Portuguese (pt-BR) localization and i18n coverage tooling (#2136) (@MaKTaiL)
  • feat(web): add supporter subscription entry point (#2185) (@s0up4200)

Bug Fixes​

  • fix(backups): overlap export stalls and cap the adaptive delay (#2188) (@nitrobass24)
  • fix(backups): write cache blobs atomically (#2189) (@nitrobass24)
  • fix(crossseed): complete RSS-deduplicated searches and surface search errors (#2162) (@jussaw)
  • fix(crossseed): keep partial results when a single tracker fails (#2156) (@s0up4200)
  • fix(crossseed): retain candidates using exact size evidence (#2160) (@Audionut)
  • fix(hardlink): use 128-bit Windows file IDs (#2182) (@Audionut)
  • fix(reannounce): update torrent age calculation and add 'started' outcome to reannounce automation (#2135) (@MaKTaiL)
  • fix(web): update radix-ui packages to fix dialog dismissal regression (#2143) (@nitrobass24)
  • fix: preserve empty torrent path components (#2152) (@Mevas)
  • fix: reject qBittorrent never-completed sentinels in completion detection, automations, and UI (#2171) (@s0up4200)
  • fix: sanitize non-UTF-8 bytes from torrent-derived strings (#2158) (@Zmegolaz)

Other Changes​

  • chore(deps): bump fast-uri from 3.1.3 to 3.1.4 in /web (#2174) (@dependabot[bot])
  • chore(deps): bump go-qbittorrent to v1.17.0 (#2178) (@s0up4200)
  • chore(deps): bump postcss from 8.5.16 to 8.5.18 in /documentation (#2176) (@dependabot[bot])
  • chore(deps): bump the github group with 2 updates (#2153) (@dependabot[bot])
  • chore(deps): bump the github group with 4 updates (#2173) (@dependabot[bot])
  • chore(deps): bump the github group with 7 updates (#2128) (@dependabot[bot])
  • chore(docs): fix missing tip close (#2155) (@Audionut)
  • chore(web): bump postcss to 8.5.23 (#2181) (@s0up4200)
  • docs: add instructions for macOS Container (#2161) (@danielcg)
  • docs: update Telegram Shoutrrr URL format (#2145) (@denisoster)

Full Changelog: https://github.com/autobrr/qui/compare/v1.23.0...v1.24.0

v1.23.0

Changelog​

New Features​

Bug Fixes​

  • fix(crossseed): stop season-pack double-nesting and honor hardlink dir preset (#2088) (@s0up4200)
  • fix(dashboard): preserve fallback status during SSE reconnect (#2085) (@Audionut)
  • fix(dirscan): align injected torrent paths to on-disk layout (#2113) (@s0up4200)
  • fix(qbittorrent): avoid sync cache reader convoys (#2106) (@s0up4200)
  • fix(qbittorrent): keep loading when a qBittorrent instance is unresponsive (#2097) (@s0up4200)
  • fix(torrents): keep rename dialog scrolled to start of file name (#2111) (@s0up4200)
  • fix(torrents): keep renamed folders expanded in file tree (#2110) (@s0up4200)
  • fix(torrents): refresh scrolled-in pages after mutations (#2116) (@s0up4200)

Other Changes​

  • chore(deps): bump postcss from 8.5.15 to 8.5.16 in /documentation in the npm group (#2098) (@dependabot[bot])
  • chore(deps): bump the github group with 3 updates (#2091) (@dependabot[bot])
  • chore(deps): bump the github group with 5 updates (#2109) (@dependabot[bot])
  • chore(deps): bump the golang group with 14 updates (#2090) (@dependabot[bot])
  • chore(deps): bump the npm group in /documentation with 9 updates (#2092) (@dependabot[bot])
  • chore(deps): bump the npm group in /web with 39 updates (#2093) (@dependabot[bot])
  • chore(deps): bump the npm group in /web with 4 updates (#2099) (@dependabot[bot])
  • chore(deps): clear all open Dependabot alerts (dev/build/docs tooling) (#2089) (@s0up4200)
  • ci(discussions): unblock triage/dedup runs and harden the write path (#2083) (@s0up4200)

Full Changelog: https://github.com/autobrr/qui/compare/v1.22.0...v1.23.0