Skip to main content

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