refactor(crossseed): hardcode ignore patterns for file matching - #915
Conversation
Replace the configurable "Allowed extra files" setting with hardcoded lists of file extensions and path keywords to ignore during cross-seed matching. This aligns with cross-seed.org's approach and simplifies the user experience. Hardcoded extensions: .nfo, .srr, .srt, .sub, .idx, .ass, .ssa, .sup, .vtt, .txt Hardcoded path keywords: sample, !sample, proof, extras, bonus, trailer, featurette Changes: - Add ignore_patterns.go with hardcoded lists - Update shouldIgnoreFile to use hardcoded patterns - Remove ignorePatterns from settings model and API - Add migration to drop ignore_patterns column - Remove "Allowed extra files" UI from CrossSeedPage - Update documentation and OpenAPI spec - Fix related tests to reflect new behavior
WalkthroughThis PR removes configurable cross‑seed ignore patterns, replaces them with hardcoded default ignore lists, drops the DB column and struct field, and updates matching, service payloads, API, tests, docs, OpenAPI/types, and frontend UI/state to stop exposing ignorePatterns. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (2)📓 Common learnings📚 Learning: 2025-12-03T18:11:08.682ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🔇 Additional comments (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
internal/models/crossseed.go (1)
296-363: Migration file exists, but IgnorePatterns field removal is incomplete.While the migration file
048_drop_ignore_patterns_column.sqlexists and the SQL query correctly excludes theignore_patternscolumn, the refactoring is incomplete. TheIgnorePatternsfield still exists and is actively used in:
internal/services/crossseed/models.go: Lines 26–27, 138–139, 382 (struct definitions)internal/services/crossseed/service.go: Lines 462, 2290, 2375–2376, 2637, 2695, 6131–6132 (active usage)Remove the
IgnorePatternsfield from all model structs and update service methods to eliminate the remaining references.web/src/pages/CrossSeedPage.tsx (4)
79-79: Critical: Remove ignorePatterns field from GlobalCrossSeedSettings interface.The
ignorePatternsfield should have been removed as part of this PR's transition to hardcoded ignore patterns. The backend has already removed this field fromCrossSeedAutomationSettings(internal/models/crossseed.go), and the API no longer accepts or returns it.🔎 Proposed fix
runExternalProgramId?: number | null - ignorePatterns: string // Source-specific tagging
130-130: Critical: Remove ignorePatterns from DEFAULT_GLOBAL_SETTINGS.This default value references the removed field and will cause type errors once the interface is corrected.
🔎 Proposed fix
runExternalProgramId: null, - ignorePatterns: "", // Source-specific tagging defaults
171-173: Critical: Remove unused normalizeIgnorePatterns function.This function is no longer needed since ignore patterns are now hardcoded in the backend (internal/services/crossseed/ignore_patterns.go). The function references the removed
ignorePatternsfield.🔎 Proposed fix
-function normalizeIgnorePatterns(patterns: string): string[] { - return parseList(patterns.replace(/\r/g, "")) -} -
709-856: Critical: Remove all ignorePatterns references from settings initialization and patch building.Multiple locations still reference the removed
ignorePatternsfield:
- Lines 719-721: Settings initialization converts
settings.ignorePatternsarray to string- Line 796: Default fallback references
settings.ignorePatterns- Line 808: Joins ignorePatterns for display
- Line 835: Calls
normalizeIgnorePatterns(globalSource.ignorePatterns)in patch payloadAll of these should be removed since ignore patterns are now hardcoded in the backend.
🔎 Proposed fix
customCategory: settings.customCategory ?? "", runExternalProgramId: settings.runExternalProgramId ?? null, - ignorePatterns: Array.isArray(settings.ignorePatterns) - ? settings.ignorePatterns.join("\n") - : "", // Source-specific taggingAnd in
buildGlobalPatch:customCategory: settings.customCategory ?? "", runExternalProgramId: settings.runExternalProgramId ?? null, - ignorePatterns: ignorePatterns.length > 0 ? ignorePatterns.join(", ") : "", rssAutomationTags: settings.rssAutomationTags ?? ["cross-seed"],And remove from the returned patch object:
customCategory: globalSource.customCategory, runExternalProgramId: globalSource.runExternalProgramId, - ignorePatterns: normalizeIgnorePatterns(globalSource.ignorePatterns), // Source-specific tagging
🧹 Nitpick comments (2)
internal/services/crossseed/matching.go (1)
1049-1053: Consider deprecating the patterns parameter.The function documentation states that the
patternsparameter is kept for backwards compatibility but is ignored. This could be confusing for callers. Consider one of the following approaches:
- Remove the parameter entirely (breaking change but clearer)
- Add a deprecation notice to the function documentation
- Log a warning if a non-empty patterns slice is passed
🔎 Option 1: Remove the parameter
-// shouldIgnoreFile checks if a file should be ignored during matching. -// Uses hardcoded lists of extensions and path keywords to filter out scene -// metadata files, subtitles, samples, and other non-content files. -// The patterns parameter is kept for backwards compatibility but is ignored. -func shouldIgnoreFile(filename string, _ []string, normalizer *stringutils.Normalizer[string, string]) bool { +// shouldIgnoreFile checks if a file should be ignored during matching. +// Uses hardcoded lists of extensions and path keywords to filter out scene +// metadata files, subtitles, samples, and other non-content files. +func shouldIgnoreFile(filename string, normalizer *stringutils.Normalizer[string, string]) bool {Then update all callers to remove the patterns argument.
internal/services/crossseed/service.go (1)
2747-2782: Safety skip messages are clearer; just ensure UI label parityThe updated messages for
skipped_unsafe_piecesandskipped_recheckmore explicitly point users at the relevant toggles (“Piece boundary safety check” and “Skip recheck” under Cross‑Seed settings). Behavior is unchanged and the guidance is clearer; just double‑check the exact UI labels match these strings to avoid minor UX confusion.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
README.mddocs/CROSS_SEEDING.mdinternal/api/handlers/crossseed.gointernal/api/handlers/crossseed_patch_test.gointernal/database/migrations/048_drop_ignore_patterns_column.sqlinternal/models/crossseed.gointernal/models/crossseed_test.gointernal/services/crossseed/align_test.gointernal/services/crossseed/autobrr_apply_test.gointernal/services/crossseed/crossseed_test.gointernal/services/crossseed/find_individual_episodes_test.gointernal/services/crossseed/ignore_patterns.gointernal/services/crossseed/matching.gointernal/services/crossseed/service.gointernal/web/swagger/openapi.yamlweb/src/components/torrents/CrossSeedDialog.tsxweb/src/hooks/useCrossSeedSearch.tsxweb/src/pages/CrossSeedPage.tsx
💤 Files with no reviewable changes (3)
- internal/services/crossseed/autobrr_apply_test.go
- internal/services/crossseed/crossseed_test.go
- internal/web/swagger/openapi.yaml
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
📚 Learning: 2025-11-06T11:59:21.390Z
Learnt from: Audionut
Repo: autobrr/qui PR: 553
File: web/src/components/torrents/TorrentTableOptimized.tsx:1510-1515
Timestamp: 2025-11-06T11:59:21.390Z
Learning: In the qui project, the API layer in web/src/lib/api.ts normalizes backend snake_case responses to camelCase for frontend consumption. For CrossSeed search results, the backend's download_url field is transformed to downloadUrl in the searchCrossSeedTorrent method, so frontend code should always use the camelCase variant (result.downloadUrl).
Applied to files:
web/src/hooks/useCrossSeedSearch.tsxweb/src/components/torrents/CrossSeedDialog.tsx
📚 Learning: 2025-11-28T20:32:30.126Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
Applied to files:
web/src/hooks/useCrossSeedSearch.tsxinternal/services/crossseed/ignore_patterns.gointernal/services/crossseed/find_individual_episodes_test.gointernal/models/crossseed_test.gointernal/models/crossseed.gointernal/api/handlers/crossseed_patch_test.godocs/CROSS_SEEDING.mdinternal/services/crossseed/service.go
📚 Learning: 2025-11-28T22:21:20.730Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:2415-2457
Timestamp: 2025-11-28T22:21:20.730Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The determineSavePath function intentionally includes a contentLayout string parameter for future/content-layout branching and API consistency. Its presence is by design even if unused in the current body; do not flag as an issue in reviews.
Applied to files:
internal/services/crossseed/ignore_patterns.gointernal/services/crossseed/service.go
📚 Learning: 2025-12-28T18:44:10.496Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 876
File: internal/logstream/hub_test.go:188-192
Timestamp: 2025-12-28T18:44:10.496Z
Learning: In Go 1.25 (Aug 2025), use wg.Go(func()) to spawn a goroutine and automate the Add/Done lifecycle. Replace manual patterns like wg.Add(1); go func(){ defer wg.Done(); ... }() with wg.Go(func(){ ... }). Ensure the codebase builds with Go 1.25+ and apply this in relevant Go files (e.g., internal/logstream/hub_test.go). If targeting older Go versions, maintain the existing pattern.
Applied to files:
internal/services/crossseed/ignore_patterns.gointernal/services/crossseed/find_individual_episodes_test.gointernal/models/crossseed_test.gointernal/models/crossseed.gointernal/api/handlers/crossseed_patch_test.gointernal/api/handlers/crossseed.gointernal/services/crossseed/service.gointernal/services/crossseed/matching.gointernal/services/crossseed/align_test.go
📚 Learning: 2025-12-03T18:11:08.682Z
Learnt from: finevan
Repo: autobrr/qui PR: 677
File: web/src/components/torrents/AddTorrentDialog.tsx:496-504
Timestamp: 2025-12-03T18:11:08.682Z
Learning: In the AddTorrentDialog component (web/src/components/torrents/AddTorrentDialog.tsx), temporary path settings (useDownloadPath/downloadPath) should be applied per-torrent rather than updating global instance preferences. The UI/UX is designed to suggest that these options apply to the individual torrent being added.
Applied to files:
web/src/pages/CrossSeedPage.tsxweb/src/components/torrents/CrossSeedDialog.tsx
📚 Learning: 2025-12-19T07:24:42.043Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 825
File: internal/models/crossseed.go:68-68
Timestamp: 2025-12-19T07:24:42.043Z
Learning: In the autobrr/qui repository, SQLite migrations should use DEFAULT 0 for boolean column defaults instead of DEFAULT FALSE. This ensures consistency with SQLite typing and avoids potential confusion, even though both are functionally equivalent for SQLite. Apply this rule to all SQL migration files (e.g., migrations/*.sql and any SQL in subdirectories).
Applied to files:
internal/database/migrations/048_drop_ignore_patterns_column.sql
📚 Learning: 2025-11-06T12:11:04.963Z
Learnt from: Audionut
Repo: autobrr/qui PR: 553
File: internal/services/crossseed/service.go:1045-1082
Timestamp: 2025-11-06T12:11:04.963Z
Learning: The autobrr/qui project uses a custom go-qbittorrent client library (github.com/autobrr/go-qbittorrent) that supports both "paused" and "stopped" parameters when adding torrents via the options map. Both parameters should be set together when controlling torrent start state, as seen in internal/services/crossseed/service.go and throughout the codebase.
Applied to files:
docs/CROSS_SEEDING.mdinternal/services/crossseed/service.go
📚 Learning: 2025-11-27T15:33:39.007Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/jackett/service.go:309-330
Timestamp: 2025-11-27T15:33:39.007Z
Learning: In internal/services/jackett/service.go, synchronous execution paths (test executors, interactive searches with explicit indexer selection, and scheduler-unavailable fallback) intentionally pass jobID=0 to result callbacks. This is acceptable because outcome tracking via ReportIndexerOutcome is only used for async cross-seed operations that always use the scheduler and receive unique jobIDs. Interactive searches and tests do not use outcome tracking.
Applied to files:
internal/services/crossseed/service.go
🧬 Code graph analysis (3)
internal/services/crossseed/find_individual_episodes_test.go (2)
internal/models/crossseed.go (1)
CrossSeedAutomationSettings(20-76)web/src/types/index.ts (1)
CrossSeedAutomationSettings(1645-1689)
internal/api/handlers/crossseed.go (1)
web/src/types/index.ts (1)
Category(664-667)
internal/services/crossseed/matching.go (2)
pkg/stringutils/normalize.go (1)
Normalizer(19-22)internal/services/crossseed/ignore_patterns.go (2)
DefaultIgnoredExtensions(11-27)DefaultIgnoredPathKeywords(32-40)
🔇 Additional comments (19)
internal/models/crossseed_test.go (1)
78-104: LGTM!The test correctly reflects the removal of the
IgnorePatternsfield fromCrossSeedAutomationSettings. The round-trip test still validates all other fields comprehensively.web/src/hooks/useCrossSeedSearch.tsx (2)
81-85: LGTM!The condensed calculation is more concise while maintaining clarity. The ternary operator and chained operations keep the logic readable.
406-431: Excellent UX improvement!The aggregated feedback provides clear, actionable information about cross-seed application outcomes. The logic correctly handles all scenarios: all successes, mixed results, all failures, and no additions. The fallback for results without
instanceResultsensures backward compatibility.internal/services/crossseed/align_test.go (3)
712-723: LGTM!The test case correctly validates that non-hardcoded extensions (like
.zip) are NOT filtered and will cause a content mismatch. This is a good complement to the tests that verify hardcoded patterns ARE filtered.
765-776: LGTM!The test case correctly validates that hardcoded extensions (
.nfo,.srt) are automatically filtered without requiring explicitignorePatterns. The test name clearly communicates the new behavior.
806-817: LGTM!The test case correctly validates that hardcoded path keywords (like
sample) are automatically filtered. The change fromScreenstoSampleappropriately reflects the new hardcoded behavior.README.md (1)
462-462: LGTM!The simplified wording correctly reflects the removal of configurable ignore patterns. The phrase "samples, etc." is clear and concise without requiring users to understand pattern matching syntax.
internal/database/migrations/048_drop_ignore_patterns_column.sql (1)
1-7: LGTM!The migration cleanly removes the
ignore_patternscolumn, aligning with the PR's objective to hardcode these values. The comment clearly documents the rationale.Note: This migration uses
DROP COLUMN, which requires SQLite 3.35.0+ (March 2021). Given the project's modern stack, this should not be an issue for most deployments.internal/models/crossseed.go (1)
509-617: LGTM! UpsertSettings correctly updated.The
UpsertSettingsmethod has been properly updated to excludeignore_patternsfrom both the INSERT and UPDATE operations. The placeholder count (34) correctly matches the number of fields being inserted.internal/api/handlers/crossseed_patch_test.go (1)
13-97: LGTM! Test correctly updated.The test has been properly updated to remove
IgnorePatternsfrom both the initial state and the patch request. The remaining field assertions ensure that other settings continue to work correctly with the patch merge logic.internal/services/crossseed/find_individual_episodes_test.go (1)
121-121: Verify TypeScript type definition consistency.The
IgnorePatternsfield has been removed from the Go model, but the TypeScript type definition inweb/src/types/index.ts(lines 1644-1688) still includesignorePatterns: string[]. This mismatch will cause type errors when the API returns settings without this field.internal/services/crossseed/matching.go (1)
1053-1071: Implementation correctly uses hardcoded ignore lists.The new implementation efficiently checks file extensions and path keywords without glob pattern overhead. Both
DefaultIgnoredExtensionsandDefaultIgnoredPathKeywordsare properly defined inignore_patterns.go, and the use of normalized filenames ensures case-insensitive matching. All callers pass compatible arguments.web/src/components/torrents/CrossSeedDialog.tsx (1)
552-561: Status mappings verified. Both"rejected"and"skipped_unsafe_pieces"are returned by the backend service in appropriate scenarios. The frontend mappings are correct:"rejected"maps to "Size mismatch" (content file size mismatch detection), and"skipped_unsafe_pieces"maps to "Skipped - unsafe pieces" (piece boundary safety violation).internal/api/handlers/crossseed.go (1)
31-294: LGTM! IgnorePatterns successfully removed from API handlers.The removal of
IgnorePatternsfrom the request/patch structs, validation logic, and update handlers is complete and correct. The API layer now properly omits all references to the deprecated field, aligning with the backend model changes.docs/CROSS_SEEDING.md (1)
155-156: LGTM! Documentation accurately reflects hardcoded ignore behavior.The updated wording correctly reflects that extra files (NFO, SRT, samples, etc.) are now handled uniformly via hardcoded patterns rather than user-configurable settings. The clarification that piece-boundary checks apply to "all extra files" removes ambiguity about which files are subject to these rules.
Also applies to: 172-173
internal/services/crossseed/ignore_patterns.go (1)
1-40: Remove cross-seed.org standards verification claim; these are autobrr's own curated defaults.The patterns in this file are autobrr's own defaults, not from cross-seed.org (which ships with empty defaults and is fully user-configurable). The review premise about verifying alignment with "cross-seed.org standards" is incorrect—there are no such standards in cross-seed.org documentation.
The mutability concern is theoretical; the slices are never modified in the codebase (only read-only access in matching.go), so this is not a practical issue. If improved as a matter of Go best practices, consider the getter function approach, but it's not required for correctness.
Likely an incorrect or invalid review comment.
internal/services/crossseed/service.go (3)
1150-1162: Completion search now uses dedicated tags + inheritSourceTagsUsing
CompletionSearchTagsforTagsOverrideand wiringInheritSourceTagsthroughSearchRunOptionsis consistent with how seeded search handles tags and keeps state copying defensive (append([]string(nil), ...)). No issues spotted here.
1835-1853: RSS automation CrossSeedRequest wiring looks correct
RSSAutomationTags,InheritSourceTags, andSkipIfExistsare now explicitly set on theCrossSeedRequest, matching the intended behavior for automated RSS runs while keeping the struct literal defensively copied. This aligns with the new hardcoded ignore-pattern model and doesn’t introduce correctness issues.
2520-2533: AutobrrApply tag/paused behavior remains coherentForwarding
tags,inheritSourceTags, andreq.StartPauseddirectly intoCrossSeedRequestkeeps webhook-driven applies consistent with other call sites. GivenprocessCrossSeedCandidatealready defaultsStartPausedand merges tags viabuildCrossSeedTags, this wiring looks sound.
Remove leftover ignorePatterns field from interfaces, defaults, and state management now that ignore patterns are hardcoded in the backend.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
docs/CROSS_SEEDING.md (1)
155-156: Document the hardcoded ignore patterns explicitly.The PR replaces the user-configurable "Allowed extra files" setting with hardcoded ignore patterns, but the documentation doesn't explain this change. Users who previously customized these patterns need to know:
- The setting has been removed from the UI
- Ignore patterns are now hardcoded and align with cross-seed.org defaults
- What the specific hardcoded patterns are
Consider adding a subsection or note that explicitly lists the hardcoded patterns, for example:
#### Ignored extra files qui automatically ignores common extra files when matching torrents. The following patterns are hardcoded to align with cross-seed.org behavior: **Extensions:** `.nfo`, `.srr`, `.srt`, `.sub`, `.idx`, `.ass`, `.ssa`, `.sup`, `.vtt`, `.txt` **Path keywords:** `sample`, `!sample`, `proof`, `extras`, `bonus`, `trailer`, `featurette` These patterns are no longer user-configurable (previously available as "Allowed extra files" in the UI).internal/services/crossseed/service.go (2)
2751-2751: User‑facing messages align with setting names; consider slightly clearer wordingThe new messages correctly reference the UI toggles (“Piece boundary safety check” and “Skip recheck”), which should reduce confusion.
If you want to be maximally explicit, you might tweak the copy to describe the action rather than the control name, e.g.:
- “Uncheck ‘Piece boundary safety check’ in Cross‑Seed settings to allow.”
- “Uncheck ‘Skip recheck’ in Cross‑Seed settings to allow.”
Pure UX nit, behavior is fine as‑is.
Also applies to: 2782-2782
6135-6136: Leftover propagation ofIgnorePatternsfrom seeded search into CrossSeedRequest
executeCrossSeedSearchAttemptstill threadsstate.opts.IgnorePatternsintorequest.IgnorePatterns. Given this PR’s goal to move to hard‑coded ignore behavior and remove ignore pattern settings from the service/API layer, this path is likely now either unused or no‑op:
SearchRunOptions.IgnorePatternsremains in the struct.- There’s no evidence in this file of
IgnorePatternsbeing set from any remaining config/UI.- If
shouldIgnoreFilenow relies solely on hard‑coded defaults, passingIgnorePatternsis misleading and may cause confusion for future maintainers.Suggested follow‑up:
- Either remove
IgnorePatternsfromSearchRunOptionsand stop populatingCrossSeedRequest.IgnorePatternsentirely, or- Clearly document that per‑run ignore patterns are no longer supported and this field is intentionally ignored, then clean it up in a later pass.
No immediate correctness issue, but cleaning this up would better match the PR intent and reduce dead configuration surface.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
README.mddocs/CROSS_SEEDING.mdinternal/api/handlers/crossseed.gointernal/services/crossseed/autobrr_apply_test.gointernal/services/crossseed/service.gointernal/web/swagger/openapi.yamlweb/src/pages/CrossSeedPage.tsx
💤 Files with no reviewable changes (2)
- internal/web/swagger/openapi.yaml
- internal/services/crossseed/autobrr_apply_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- README.md
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
📚 Learning: 2025-12-28T18:44:10.496Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 876
File: internal/logstream/hub_test.go:188-192
Timestamp: 2025-12-28T18:44:10.496Z
Learning: In Go 1.25 (Aug 2025), use wg.Go(func()) to spawn a goroutine and automate the Add/Done lifecycle. Replace manual patterns like wg.Add(1); go func(){ defer wg.Done(); ... }() with wg.Go(func(){ ... }). Ensure the codebase builds with Go 1.25+ and apply this in relevant Go files (e.g., internal/logstream/hub_test.go). If targeting older Go versions, maintain the existing pattern.
Applied to files:
internal/api/handlers/crossseed.gointernal/services/crossseed/service.go
📚 Learning: 2025-12-03T18:11:08.682Z
Learnt from: finevan
Repo: autobrr/qui PR: 677
File: web/src/components/torrents/AddTorrentDialog.tsx:496-504
Timestamp: 2025-12-03T18:11:08.682Z
Learning: In the AddTorrentDialog component (web/src/components/torrents/AddTorrentDialog.tsx), temporary path settings (useDownloadPath/downloadPath) should be applied per-torrent rather than updating global instance preferences. The UI/UX is designed to suggest that these options apply to the individual torrent being added.
Applied to files:
web/src/pages/CrossSeedPage.tsx
📚 Learning: 2025-11-28T20:32:30.126Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
Applied to files:
internal/services/crossseed/service.go
📚 Learning: 2025-11-28T22:21:20.730Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:2415-2457
Timestamp: 2025-11-28T22:21:20.730Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The determineSavePath function intentionally includes a contentLayout string parameter for future/content-layout branching and API consistency. Its presence is by design even if unused in the current body; do not flag as an issue in reviews.
Applied to files:
internal/services/crossseed/service.go
📚 Learning: 2025-11-06T12:11:04.963Z
Learnt from: Audionut
Repo: autobrr/qui PR: 553
File: internal/services/crossseed/service.go:1045-1082
Timestamp: 2025-11-06T12:11:04.963Z
Learning: The autobrr/qui project uses a custom go-qbittorrent client library (github.com/autobrr/go-qbittorrent) that supports both "paused" and "stopped" parameters when adding torrents via the options map. Both parameters should be set together when controlling torrent start state, as seen in internal/services/crossseed/service.go and throughout the codebase.
Applied to files:
internal/services/crossseed/service.godocs/CROSS_SEEDING.md
📚 Learning: 2025-12-11T08:40:01.329Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 746
File: internal/services/reannounce/service.go:480-481
Timestamp: 2025-12-11T08:40:01.329Z
Learning: In autobrr/qui's internal/services/reannounce/service.go, the hasHealthyTracker, getProblematicTrackers, and getHealthyTrackers functions intentionally match qbrr's lenient tracker health logic (skip unregistered trackers and check if any other tracker is healthy) rather than go-qbittorrent's strict isTrackerStatusOK logic (which treats unregistered as an immediate failure). For multi-tracker torrents, if one tracker is working, reannouncing won't help. The duplication of the health check logic across these three functions is acceptable as it's a simple one-liner, and extracting it would add unnecessary complexity.
Applied to files:
docs/CROSS_SEEDING.md
🧬 Code graph analysis (1)
internal/api/handlers/crossseed.go (1)
web/src/types/index.ts (1)
Category(664-667)
🔇 Additional comments (1)
internal/services/crossseed/service.go (1)
1159-1161: Consistent use of source‑specific tag settings across flows looks goodThese changes wire
CompletionSearchTags,RSSAutomationTags, andWebhookTagsinto completion, RSS automation, and Autobrr apply respectively, still copying slices defensively and preservingInheritSourceTags/StartPausedsemantics. This is consistent with the rest of the service and with how seeded search usesSeededSearchTags. No functional issues spotted here.Also applies to: 1839-1842, 2524-2526
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
internal/services/crossseed/layout.go (1)
46-75: Largest‑file classification with hardcoded ignores and extended archive detection looks sound
classifyTorrentLayoutnow:
- Skips files via
shouldIgnoreFile(f.Name, normalizer)and selects the largest remaining path, then- Classifies that path using
isArchiveFilename, which correctly handles multi‑suffix archives (.tar.gz,.tar.xz,.tar.bz2,.tar) before falling back to thearchiveExtensionsmap for RAR parts, ZIP, 7z, etc.This keeps layout classification aligned with the new hardcoded ignore behavior and improves archive coverage. If this ever shows up hot in profiles, you could hoist
archiveSuffixesto a package‑level var to avoid re‑allocating the slice on each call, but it’s fine as‑is.Also applies to: 77-98
internal/services/crossseed/service.go (2)
2693-2850: Candidate selection & safety messaging look consistent with new matching / safety model
- Using
findBestCandidateMatch(ctx, candidate, sourceRelease, sourceFiles, candidateFilesByHash, tolerancePercent)keeps all file‑level and tolerance logic centralized and matches the new signature that no longer depends on user‑supplied ignore patterns.- Passing
s.stringNormalizerintohasContentFileSizeMismatch(sourceFiles, candidateFiles, s.stringNormalizer)is appropriate and keeps normalization consistent with the rest of the service.- The piece‑boundary safety block explicitly ignores ignore patterns when deciding if extra files are unsafe (
HasUnsafeIgnoredExtras), which is the right call: safety should not be bypassed by “ignored” extensions/paths.- The updated messages:
"Skipped: extra files share pieces with content. Disable 'Piece boundary safety check' in Cross-Seed settings to allow""Skipped: requires recheck. Disable 'Skip recheck' in Cross-Seed settings to allow"
are coherent with the corresponding boolean flags (SkipPieceBoundarySafetyCheck,SkipRecheck), assuming the UI labels match those phrases.No functional issues spotted in this region.
If you want, you could consider slightly rephrasing the messages to avoid the double‑negative around “Disable Skip recheck” (e.g., “Uncheck ‘Skip recheck’…”), but that’s purely UX polish.
3656-3755: findBestCandidateMatch correctly delegates to getMatchTypeWithReason; semantics look sound
- The new
findBestCandidateMatchsignature without ignore‑pattern parameters makes sense now that ignore handling is centralized elsewhere.- Calling
getMatchTypeWithReason(candidateRelease, sourceRelease, files, sourceFiles, tolerancePercent)matches the comment: we’re checking whether the existing torrent’s files (files) are contained within the new torrent’s files (sourceFiles), which is the intended direction for apply‑time validation.- Using
matchTypePriorityto accept only"exact","partial-in-pack", and"size"(others treated as priority 0) is a good guardrail to avoid accidentally acting on weaker layout‑only match types.- The tie‑breaker (prefer root‑folder layouts, then higher file count) is consistent with earlier dedup behavior and should pick more robust representatives.
Everything here is internally consistent with the surrounding code and PR intent.
If you later need finer cancellation behavior during heavy candidate scans, you could thread
ctxinto the inner loop (e.g., checkctx.Err()every N iterations), but it’s not necessary for this refactor.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
README.mdinternal/api/handlers/crossseed.gointernal/models/crossseed.gointernal/services/crossseed/align.gointernal/services/crossseed/align_test.gointernal/services/crossseed/crossseed_test.gointernal/services/crossseed/layout.gointernal/services/crossseed/layout_test.gointernal/services/crossseed/matching.gointernal/services/crossseed/matching_hdr_test.gointernal/services/crossseed/matching_layout_test.gointernal/services/crossseed/models.gointernal/services/crossseed/service.gointernal/web/swagger/openapi.yamlweb/src/pages/CrossSeedPage.tsx
💤 Files with no reviewable changes (1)
- internal/web/swagger/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
- internal/api/handlers/crossseed.go
- README.md
- internal/services/crossseed/crossseed_test.go
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
📚 Learning: 2025-11-28T20:32:30.126Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:209-212
Timestamp: 2025-11-28T20:32:30.126Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The cross-seed recheck-resume worker intentionally runs for the process lifetime and keys pending entries by hash only. This is acceptable under the current constraint that background seeded-search runs operate on a single instance at a time; graceful shutdown and instanceID|hash keying are deferred by design.
Applied to files:
internal/models/crossseed.gointernal/services/crossseed/models.gointernal/services/crossseed/matching_layout_test.gointernal/services/crossseed/service.go
📚 Learning: 2025-11-28T22:21:20.730Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go:2415-2457
Timestamp: 2025-11-28T22:21:20.730Z
Learning: Repo: autobrr/qui PR: 641
File: internal/services/crossseed/service.go
Learning: The determineSavePath function intentionally includes a contentLayout string parameter for future/content-layout branching and API consistency. Its presence is by design even if unused in the current body; do not flag as an issue in reviews.
Applied to files:
internal/models/crossseed.gointernal/services/crossseed/layout.gointernal/services/crossseed/align.gointernal/services/crossseed/models.gointernal/services/crossseed/layout_test.gointernal/services/crossseed/matching_layout_test.gointernal/services/crossseed/matching.gointernal/services/crossseed/service.go
📚 Learning: 2025-12-28T18:44:10.496Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 876
File: internal/logstream/hub_test.go:188-192
Timestamp: 2025-12-28T18:44:10.496Z
Learning: In Go 1.25 (Aug 2025), use wg.Go(func()) to spawn a goroutine and automate the Add/Done lifecycle. Replace manual patterns like wg.Add(1); go func(){ defer wg.Done(); ... }() with wg.Go(func(){ ... }). Ensure the codebase builds with Go 1.25+ and apply this in relevant Go files (e.g., internal/logstream/hub_test.go). If targeting older Go versions, maintain the existing pattern.
Applied to files:
internal/models/crossseed.gointernal/services/crossseed/layout.gointernal/services/crossseed/align.gointernal/services/crossseed/models.gointernal/services/crossseed/layout_test.gointernal/services/crossseed/matching_layout_test.gointernal/services/crossseed/align_test.gointernal/services/crossseed/matching.gointernal/services/crossseed/service.gointernal/services/crossseed/matching_hdr_test.go
📚 Learning: 2025-11-21T21:11:50.633Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 625
File: internal/qbittorrent/sync_manager.go:1112-1123
Timestamp: 2025-11-21T21:11:50.633Z
Learning: In internal/qbittorrent/sync_manager.go, the GetCachedFilesBatch interface documentation (around line 39-40) should specify "uppercase hex" instead of "lowercase hex" to match the actual normalization practice used throughout the codebase (e.g., normalizeHash in internal/services/crossseed/service.go uses strings.ToUpper, and hash filtering uses uppercase).
Applied to files:
internal/services/crossseed/layout.go
📚 Learning: 2025-11-06T12:11:04.963Z
Learnt from: Audionut
Repo: autobrr/qui PR: 553
File: internal/services/crossseed/service.go:1045-1082
Timestamp: 2025-11-06T12:11:04.963Z
Learning: The autobrr/qui project uses a custom go-qbittorrent client library (github.com/autobrr/go-qbittorrent) that supports both "paused" and "stopped" parameters when adding torrents via the options map. Both parameters should be set together when controlling torrent start state, as seen in internal/services/crossseed/service.go and throughout the codebase.
Applied to files:
internal/services/crossseed/models.go
📚 Learning: 2025-11-25T22:46:03.762Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 632
File: internal/backups/service.go:1401-1404
Timestamp: 2025-11-25T22:46:03.762Z
Learning: In qui's backup service (internal/backups/service.go), background torrent downloads initiated during manifest import intentionally use a fire-and-forget pattern with the shared service context (s.ctx). Per-run cancellation is not needed, as orphaned downloads completing after run deletion are considered harmless and acceptable. This design prioritizes simplicity over per-run lifecycle management for background downloads.
Applied to files:
internal/services/crossseed/matching_layout_test.go
📚 Learning: 2025-12-03T18:11:08.682Z
Learnt from: finevan
Repo: autobrr/qui PR: 677
File: web/src/components/torrents/AddTorrentDialog.tsx:496-504
Timestamp: 2025-12-03T18:11:08.682Z
Learning: In the AddTorrentDialog component (web/src/components/torrents/AddTorrentDialog.tsx), temporary path settings (useDownloadPath/downloadPath) should be applied per-torrent rather than updating global instance preferences. The UI/UX is designed to suggest that these options apply to the individual torrent being added.
Applied to files:
web/src/pages/CrossSeedPage.tsx
🧬 Code graph analysis (5)
internal/services/crossseed/layout.go (1)
pkg/stringutils/normalize.go (1)
Normalizer(19-22)
internal/services/crossseed/align.go (1)
pkg/stringutils/normalize.go (1)
Normalizer(19-22)
internal/services/crossseed/models.go (1)
web/src/types/index.ts (1)
Category(664-667)
internal/services/crossseed/layout_test.go (2)
internal/services/crossseed/layout.go (2)
TorrentLayout(18-18)LayoutUnknown(21-21)pkg/stringutils/normalize.go (1)
NewDefaultNormalizer(35-37)
internal/services/crossseed/matching.go (2)
pkg/stringutils/normalize.go (1)
Normalizer(19-22)internal/services/crossseed/ignore_patterns.go (2)
DefaultIgnoredExtensions(11-27)DefaultIgnoredPathKeywords(32-40)
🔇 Additional comments (16)
internal/services/crossseed/models.go (3)
20-76: Struct shape and defaults align with API after ignorePatterns removalThe updated
CrossSeedAutomationSettings(includingTargetInstanceIDs []intwith a default of[]int{}inDefaultCrossSeedAutomationSettings) looks consistent with the API intent and avoidsnullvs[]ambiguity on the wire. No strayignorePatternsfields remain here.Also applies to: 90-133
295-385: Settings load path correctly dropsignore_patternswhile keeping instance/indexer IDs JSON-backed
GetSettingsnow selectstarget_instance_ids/target_indexer_idsintoinstancesJSON/indexersJSONand decodes them viadecodeIntSlice, with clean handling ofNULL/empty as[]. The removedignore_patternscolumn is no longer referenced, which matches the migration, and the rest of the scan order stays consistent.
509-563: Upsert column list, placeholders, and Exec args stay in sync post‑refactorThe INSERT/ON CONFLICT block now omits
ignore_patternsand includestarget_instance_ids/target_indexer_idswith matchinginstanceJSON/indexerJSONarguments. Column count,?placeholders, andExecContextarguments line up correctly, so the migration to dropignore_patternswon’t break persistence.Also applies to: 566-611
internal/services/crossseed/align_test.go (1)
651-920: Content‑mismatch tests correctly capture hardcoded ignore semanticsThe updated
TestHasContentFileSizeMismatchtable now:
- Treats
.nfo/.srtandsamplepath variants as ignored (no mismatch), including the “all ignored files” case.- Confirms non‑ignored extras like
.zipstill trigger mismatches and that mismatched sizes are surfaced viaexpectedFiles.- Uses the new
hasContentFileSizeMismatch(sourceFiles, candidateFiles, normalizer)signature without any per‑request ignorePatterns.This gives good coverage of the new hardcoded ignore lists and path‑keyword handling.
web/src/pages/CrossSeedPage.tsx (2)
80-159: Global settings/frontend shape correctly updated to dropignorePatterns
GlobalCrossSeedSettings,DEFAULT_GLOBAL_SETTINGS, theglobalSettingsinitializationuseEffect, andbuildGlobalPatchall line up after removingignorePatterns. The remaining fields (category mode booleans, customCategory, tagging, skip* flags, webhook source filters, external program) are passed through consistently, so the frontend patch payload still fully represents the server’s global cross‑seed settings without leaking the old ignorePatterns knob.Also applies to: 782-817, 868-931
2604-2610: Save global button wiring remains correct after refactorThe “Save global cross-seed settings” button still delegates to
handleSaveGlobaland is disabled whilepatchSettingsMutationis pending, preventing double‑submits. No residual dependency on ignorePatterns is present in this flow.internal/services/crossseed/layout_test.go (1)
15-52: Layout classification tests updated appropriately for hardcoded ignores
TestClassifyTorrentLayoutnow exercises:
- mkv + sidecar
.nfo→LayoutFiles,- classic multi‑part RAR +
.sfv→LayoutArchives,.tar.gz→LayoutArchivesvia the new multi‑suffix handling,- only ignored sidecar files (
.txt,.nfo) →LayoutUnknown.The test’s use of
classifyTorrentLayout(tt.files, stringutils.NewDefaultNormalizer())matches the new signature, and expectations align with the intended archive vs file layout heuristic.Also applies to: 54-57
internal/services/crossseed/matching_layout_test.go (2)
18-39: Layout‑aware match selection tests correctly exercise new signaturesThe updated calls:
svc.getMatchType(&sourceRelease, &candidateRelease, sourceFiles, archiveFiles)and friends, andsvc.findBestCandidateMatch(ctx, candidate, &sourceRelease, sourceFiles, filesByHash, 5.0)all reflect the new APIs without ignorePatterns. The tests still assert:
- mkv vs rar candidates don’t match, but archive↔archive and file↔file do,
findBestCandidateMatchprefers the mkv candidate over rar when the source is mkv,- tie‑breaking prefers the top‑level‑folder layout when match type is identical, and
- a season‑pack source correctly rejects a single‑episode candidate with
rejectReasonSeasonPackFromEpisode.These are good regression checks for the new hardcoded‑ignore + layout‑compatibility flow.
Also applies to: 41-114, 116-149
236-255: Title/release‑key fallback tests remain valid without ignorePatterns parameterThe
getMatchTypeFromTitletests now pass onlycandidateFilesand still cover:
- Fallback to a permissive
"partial-in-pack"when both releases are episodic and parsing yields no usable keys (random_data_file.bin),- Requiring matching release keys for non‑episodic content (mismatched years yield no match; matching years give
"partial-in-pack"),- Game scene RAR releases matching on title/group alone (
"release-match"), and- Filename‑based fallback to
"size"matches for single‑file torrents with equivalent base names.These updated call sites match the refactored signature and keep the intended matching semantics well covered.
Also applies to: 258-285, 287-313, 315-350, 352-371
internal/services/crossseed/align.go (1)
580-618: LGTM! Signature correctly updated to use hardcoded ignore patterns.The removal of the
ignorePatternsparameter and the updatedshouldIgnoreFilecalls align with the PR objective of centralizing ignore logic to hardcoded defaults.internal/services/crossseed/matching_hdr_test.go (1)
182-182: LGTM! Test correctly updated for new signature.The test call properly reflects the removal of the
ignorePatternsparameter fromgetMatchType.internal/services/crossseed/matching.go (4)
501-608: LGTM! Function signature correctly updated.The removal of
ignorePatternsfromgetMatchTypeFromTitleand the updatedshouldIgnoreFilecall at Line 505 correctly delegate filtering to the hardcoded ignore logic.
619-746: LGTM! Function signature correctly updated.The removal of
ignorePatternsfromgetMatchTypeWithReasonis correct. TheshouldIgnoreFilecalls at Lines 650 and 670 properly use the new signature with just the normalizer.
814-934: LGTM! Function signature correctly updated.The removal of
ignorePatternsfromgetMatchTypealigns with the PR objective. The filtering logic at Lines 843 and 864 correctly uses the refactoredshouldIgnoreFile.
1049-1070: LGTM! Refactor correctly implements hardcoded ignore logic.The rewritten
shouldIgnoreFilefunction now usesDefaultIgnoredExtensionsandDefaultIgnoredPathKeywordsfrom the hardcoded lists, replacing the previous pattern-based approach. The implementation is straightforward:
- Extension matching (Lines 1056-1060): Uses
strings.HasSuffixto check file extensions.- Keyword matching (Lines 1063-1067): Uses
strings.Containsto check path keywords.The keyword matching with
Containscould theoretically match partial words (e.g., "sampleton" containing "sample"), but this is acceptable given:
- Scene releases rarely use such naming
- The PR intentionally simplifies the logic
- The hardcoded lists come from cross-seed.org best practices
internal/services/crossseed/service.go (1)
2350-2367: getMatchTypeFromTitle call now cleanly aligns with updated matching APIThe updated call signature (
getMatchTypeFromTitle(req.TorrentName, torrent.Name, targetRelease, candidateRelease, candidateFiles)) matches the revised matching API and correctly passes the parsed releases plus the candidate’s files for file‑level verification. Behavior is consistent with the new hardcoded ignore patterns; nothing to fix here.
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.11.0` → `v1.12.0` | --- ### Release Notes <details> <summary>autobrr/qui (ghcr.io/autobrr/qui)</summary> ### [`v1.12.0`](https://github.com/autobrr/qui/releases/tag/v1.12.0) [Compare Source](autobrr/qui@v1.11.0...v1.12.0) #### Changelog ##### New Features - [`202e950`](autobrr/qui@202e950): feat(automations): Add `free_space` condition ([#​1061](autobrr/qui#1061)) ([@​Barcode-eng](https://github.com/Barcode-eng)) - [`3b106d6`](autobrr/qui@3b106d6): feat(automations): make conditions optional for non-delete rules and add drag reorder ([#​939](autobrr/qui#939)) ([@​s0up4200](https://github.com/s0up4200)) - [`0684d75`](autobrr/qui@0684d75): feat(config): Add QUI\_\_OIDC\_CLIENT\_SECRET\_FILE env var ([#​841](autobrr/qui#841)) ([@​PedDavid](https://github.com/PedDavid)) - [`dac0173`](autobrr/qui@dac0173): feat(config): allow disabling tracker icon fetching ([#​823](autobrr/qui#823)) ([@​s0up4200](https://github.com/s0up4200)) - [`dc10bad`](autobrr/qui@dc10bad): feat(crossseed): add cancel run and opt-in errored torrent recovery ([#​918](autobrr/qui#918)) ([@​s0up4200](https://github.com/s0up4200)) - [`cd1fcc9`](autobrr/qui@cd1fcc9): feat(crossseed): add custom category option for cross-seeds ([#​907](autobrr/qui#907)) ([@​s0up4200](https://github.com/s0up4200)) - [`d189fe9`](autobrr/qui@d189fe9): feat(crossseed): add indexerName to webhook apply + fix category mode defaults ([#​916](autobrr/qui#916)) ([@​s0up4200](https://github.com/s0up4200)) - [`03a147e`](autobrr/qui@03a147e): feat(crossseed): add option to skip recheck-required matches ([#​825](autobrr/qui#825)) ([@​s0up4200](https://github.com/s0up4200)) - [`edae00a`](autobrr/qui@edae00a): feat(crossseed): add optional hardlink mode for cross-seeding ([#​849](autobrr/qui#849)) ([@​s0up4200](https://github.com/s0up4200)) - [`0938436`](autobrr/qui@0938436): feat(crossseed): add source aliasing for WEB/WEB-DL/WEBRip precheck matching ([#​874](autobrr/qui#874)) ([@​s0up4200](https://github.com/s0up4200)) - [`65f6129`](autobrr/qui@65f6129): feat(crossseed): show failure reasons, prune runs, and add cache cleanup ([#​923](autobrr/qui#923)) ([@​s0up4200](https://github.com/s0up4200)) - [`e10fba8`](autobrr/qui@e10fba8): feat(details): torrent details panel improvements ([#​884](autobrr/qui#884)) ([@​s0up4200](https://github.com/s0up4200)) - [`6921140`](autobrr/qui@6921140): feat(docs): add Docusaurus documentation site ([@​s0up4200](https://github.com/s0up4200)) - [`6a5a66c`](autobrr/qui@6a5a66c): feat(docs): add Icon and webUI variables to the Unraid install guide ([#​942](autobrr/qui#942)) ([@​BaukeZwart](https://github.com/BaukeZwart)) - [`281fce7`](autobrr/qui@281fce7): feat(docs): add local search plugin ([#​1076](autobrr/qui#1076)) ([@​s0up4200](https://github.com/s0up4200)) - [`566de08`](autobrr/qui@566de08): feat(docs): add qui logo, update readme, remove v4 flag ([@​s0up4200](https://github.com/s0up4200)) - [`b83ac5a`](autobrr/qui@b83ac5a): feat(docs): apply minimal.css theme to Docusaurus ([@​s0up4200](https://github.com/s0up4200)) - [`fe6a6df`](autobrr/qui@fe6a6df): feat(docs): improve documentation pages and add support page ([@​s0up4200](https://github.com/s0up4200)) - [`62a7ad5`](autobrr/qui@62a7ad5): feat(docs): use qui favicon ([@​s0up4200](https://github.com/s0up4200)) - [`5d124c0`](autobrr/qui@5d124c0): feat(orphan): add auto cleanup mode ([#​897](autobrr/qui#897)) ([@​s0up4200](https://github.com/s0up4200)) - [`3172ad9`](autobrr/qui@3172ad9): feat(settings): add log settings + live log stream ([#​876](autobrr/qui#876)) ([@​s0up4200](https://github.com/s0up4200)) - [`3c1b34b`](autobrr/qui@3c1b34b): feat(torrents): add "torrent introuvable" to unregistered status ([#​836](autobrr/qui#836)) ([@​kephasdev](https://github.com/kephasdev)) - [`afe4d39`](autobrr/qui@afe4d39): feat(torrents): add tracker URL editing for single torrents ([#​848](autobrr/qui#848)) ([@​s0up4200](https://github.com/s0up4200)) - [`76dedd7`](autobrr/qui@76dedd7): feat(torrents): update GeneralTabHorizontal to display limits and improve layout ([#​1078](autobrr/qui#1078)) ([@​martylukyy](https://github.com/martylukyy)) - [`6831c24`](autobrr/qui@6831c24): feat(ui): unify payment options into single dialog ([@​s0up4200](https://github.com/s0up4200)) - [`4dcdf7f`](autobrr/qui@4dcdf7f): feat(web): add local file access indicator to instance cards ([#​911](autobrr/qui#911)) ([@​s0up4200](https://github.com/s0up4200)) - [`a560e5e`](autobrr/qui@a560e5e): feat(web): compact torrent details panel ([#​833](autobrr/qui#833)) ([@​martylukyy](https://github.com/martylukyy)) - [`557e7bd`](autobrr/qui@557e7bd): feat: add issue/discussion template ([#​945](autobrr/qui#945)) ([@​s0up4200](https://github.com/s0up4200)) - [`8b93719`](autobrr/qui@8b93719): feat: add workflow automation system with category actions, orphan scanner, and hardlink detection ([#​818](autobrr/qui#818)) ([@​s0up4200](https://github.com/s0up4200)) ##### Bug Fixes - [`b85ad6b`](autobrr/qui@b85ad6b): fix(automations): allow delete rules to match incomplete torrents ([#​926](autobrr/qui#926)) ([@​s0up4200](https://github.com/s0up4200)) - [`ae06200`](autobrr/qui@ae06200): fix(automations): make tags field condition operators tag-aware ([#​908](autobrr/qui#908)) ([@​s0up4200](https://github.com/s0up4200)) - [`ace0101`](autobrr/qui@ace0101): fix(crossseed): detect folder mismatch for bare file to folder cross-seeds ([#​846](autobrr/qui#846)) ([@​s0up4200](https://github.com/s0up4200)) - [`1cc1243`](autobrr/qui@1cc1243): fix(crossseed): enforce resolution and language matching with sensible defaults ([#​855](autobrr/qui#855)) ([@​s0up4200](https://github.com/s0up4200)) - [`cefb9cd`](autobrr/qui@cefb9cd): fix(crossseed): execute external program reliably after injection ([#​1083](autobrr/qui#1083)) ([@​s0up4200](https://github.com/s0up4200)) - [`867e2da`](autobrr/qui@867e2da): fix(crossseed): improve matching with size validation and relaxed audio checks ([#​845](autobrr/qui#845)) ([@​s0up4200](https://github.com/s0up4200)) - [`4b5079b`](autobrr/qui@4b5079b): fix(crossseed): persist custom category settings in PATCH handler ([#​913](autobrr/qui#913)) ([@​s0up4200](https://github.com/s0up4200)) - [`cfbbc1f`](autobrr/qui@cfbbc1f): fix(crossseed): prevent season packs matching episodes ([#​854](autobrr/qui#854)) ([@​s0up4200](https://github.com/s0up4200)) - [`c7c1706`](autobrr/qui@c7c1706): fix(crossseed): reconcile interrupted runs on startup ([#​1084](autobrr/qui#1084)) ([@​s0up4200](https://github.com/s0up4200)) - [`7d633bd`](autobrr/qui@7d633bd): fix(crossseed): use ContentPath for manually-managed single-file torrents ([#​832](autobrr/qui#832)) ([@​s0up4200](https://github.com/s0up4200)) - [`d5db761`](autobrr/qui@d5db761): fix(database): include arr\_instances in string pool cleanup + remove auto-recovery ([#​898](autobrr/qui#898)) ([@​s0up4200](https://github.com/s0up4200)) - [`c73ec6f`](autobrr/qui@c73ec6f): fix(database): prevent race between stmt cache access and db close ([#​840](autobrr/qui#840)) ([@​s0up4200](https://github.com/s0up4200)) - [`a40b872`](autobrr/qui@a40b872): fix(db): drop legacy hardlink columns from cross\_seed\_settings ([#​912](autobrr/qui#912)) ([@​s0up4200](https://github.com/s0up4200)) - [`e400af3`](autobrr/qui@e400af3): fix(db): recover wedged SQLite writer + stop cross-seed tight loop ([#​890](autobrr/qui#890)) ([@​s0up4200](https://github.com/s0up4200)) - [`90e15b4`](autobrr/qui@90e15b4): fix(deps): update rls to recognize IP as iPlayer ([#​922](autobrr/qui#922)) ([@​s0up4200](https://github.com/s0up4200)) - [`8e81b9f`](autobrr/qui@8e81b9f): fix(proxy): honor TLSSkipVerify for proxied requests ([#​1051](autobrr/qui#1051)) ([@​s0up4200](https://github.com/s0up4200)) - [`eb2bee0`](autobrr/qui@eb2bee0): fix(security): redact sensitive URL parameters in logs ([#​853](autobrr/qui#853)) ([@​s0up4200](https://github.com/s0up4200)) - [`40982bc`](autobrr/qui@40982bc): fix(themes): prevent reset on license errors, improve switch performance ([#​844](autobrr/qui#844)) ([@​s0up4200](https://github.com/s0up4200)) - [`a8a32f7`](autobrr/qui@a8a32f7): fix(ui): incomplete torrents aren't "Completed: 1969-12-31" ([#​851](autobrr/qui#851)) ([@​finevan](https://github.com/finevan)) - [`5908bba`](autobrr/qui@5908bba): fix(ui): preserve category collapse state when toggling incognito mode ([#​834](autobrr/qui#834)) ([@​jabloink](https://github.com/jabloink)) - [`25c847e`](autobrr/qui@25c847e): fix(ui): torrents with no creation metadata don't display 1969 ([#​873](autobrr/qui#873)) ([@​finevan](https://github.com/finevan)) - [`6403b6a`](autobrr/qui@6403b6a): fix(web): column filter status now matches all states in category ([#​880](autobrr/qui#880)) ([@​s0up4200](https://github.com/s0up4200)) - [`eafc4e7`](autobrr/qui@eafc4e7): fix(web): make delete cross-seed check rely on content\_path matches ([#​1080](autobrr/qui#1080)) ([@​s0up4200](https://github.com/s0up4200)) - [`d57c749`](autobrr/qui@d57c749): fix(web): only show selection checkbox on normal view ([#​830](autobrr/qui#830)) ([@​jabloink](https://github.com/jabloink)) - [`60338f6`](autobrr/qui@60338f6): fix(web): optimize TorrentDetailsPanel for mobile view and make tabs scrollable horizontally ([#​1066](autobrr/qui#1066)) ([@​martylukyy](https://github.com/martylukyy)) - [`aedab87`](autobrr/qui@aedab87): fix(web): speed limit input reformatting during typing ([#​881](autobrr/qui#881)) ([@​s0up4200](https://github.com/s0up4200)) - [`df7f3e0`](autobrr/qui@df7f3e0): fix(web): truncate file progress percentage instead of rounding ([#​919](autobrr/qui#919)) ([@​s0up4200](https://github.com/s0up4200)) - [`2fadd01`](autobrr/qui@2fadd01): fix(web): update eslint config for flat config compatibility ([#​879](autobrr/qui#879)) ([@​s0up4200](https://github.com/s0up4200)) - [`721cedd`](autobrr/qui@721cedd): fix(web): use fixed heights for mobile torrent cards ([#​812](autobrr/qui#812)) ([@​jabloink](https://github.com/jabloink)) - [`a7db605`](autobrr/qui@a7db605): fix: remove pnpm-workspace.yaml breaking CI ([@​s0up4200](https://github.com/s0up4200)) - [`c0ddc0a`](autobrr/qui@c0ddc0a): fix: use prefix matching for allowed bash commands ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`fff52ce`](autobrr/qui@fff52ce): chore(ci): disable reviewer ([@​s0up4200](https://github.com/s0up4200)) - [`7ef2a38`](autobrr/qui@7ef2a38): chore(ci): fix automated triage and deduplication workflows ([#​1057](autobrr/qui#1057)) ([@​s0up4200](https://github.com/s0up4200)) - [`d84910b`](autobrr/qui@d84910b): chore(docs): move Tailwind to documentation workspace only ([@​s0up4200](https://github.com/s0up4200)) - [`37ebe05`](autobrr/qui@37ebe05): chore(docs): move netlify.toml to documentation directory ([@​s0up4200](https://github.com/s0up4200)) - [`e25de38`](autobrr/qui@e25de38): chore(docs): remove disclaimer ([@​s0up4200](https://github.com/s0up4200)) - [`c59b809`](autobrr/qui@c59b809): chore(docs): update support sections ([#​1063](autobrr/qui#1063)) ([@​s0up4200](https://github.com/s0up4200)) - [`b723523`](autobrr/qui@b723523): chore(tests): remove dead tests and optimize slow test cases ([#​842](autobrr/qui#842)) ([@​s0up4200](https://github.com/s0up4200)) - [`662a1c6`](autobrr/qui@662a1c6): chore(workflows): update runners from 4vcpu to 2vcpu for all jobs ([#​859](autobrr/qui#859)) ([@​s0up4200](https://github.com/s0up4200)) - [`46f2a1c`](autobrr/qui@46f2a1c): chore: clean up repo root by moving Docker, scripts, and community docs ([#​1054](autobrr/qui#1054)) ([@​s0up4200](https://github.com/s0up4200)) - [`2f27c0d`](autobrr/qui@2f27c0d): chore: remove old issue templates ([@​s0up4200](https://github.com/s0up4200)) - [`04f361a`](autobrr/qui@04f361a): ci(triage): add labeling for feature-requests-ideas discussions ([@​s0up4200](https://github.com/s0up4200)) - [`f249c69`](autobrr/qui@f249c69): ci(triage): remove needs-triage label after applying labels ([@​s0up4200](https://github.com/s0up4200)) - [`bdda1de`](autobrr/qui@bdda1de): ci(workflows): add self-dispatch workaround for discussion events ([@​s0up4200](https://github.com/s0up4200)) - [`a9732a2`](autobrr/qui@a9732a2): ci(workflows): increase max-turns to 25 for Claude workflows ([@​s0up4200](https://github.com/s0up4200)) - [`d7d830d`](autobrr/qui@d7d830d): docs(README): add Buy Me a Coffee link ([#​863](autobrr/qui#863)) ([@​s0up4200](https://github.com/s0up4200)) - [`266d92e`](autobrr/qui@266d92e): docs(readme): Clarify ignore pattern ([#​878](autobrr/qui#878)) ([@​quorn23](https://github.com/quorn23)) - [`9586084`](autobrr/qui@9586084): docs(readme): add banner linking to stable docs ([#​925](autobrr/qui#925)) ([@​s0up4200](https://github.com/s0up4200)) - [`e36a621`](autobrr/qui@e36a621): docs(readme): use markdown link for Polar URL ([@​s0up4200](https://github.com/s0up4200)) - [`9394676`](autobrr/qui@9394676): docs: add frontmatter titles and descriptions, remove marketing language ([@​s0up4200](https://github.com/s0up4200)) - [`ba9d45e`](autobrr/qui@ba9d45e): docs: add local filesystem access snippet and swizzle Details component ([@​s0up4200](https://github.com/s0up4200)) - [`4329edd`](autobrr/qui@4329edd): docs: disclaimer about unreleased features ([#​943](autobrr/qui#943)) ([@​s0up4200](https://github.com/s0up4200)) - [`735d065`](autobrr/qui@735d065): docs: improve external programs, orphan scan, reverse proxy, tracker icons documentation ([@​s0up4200](https://github.com/s0up4200)) - [`78faef2`](autobrr/qui@78faef2): docs: remove premature tip and fix stat command ([@​s0up4200](https://github.com/s0up4200)) - [`eaad3bf`](autobrr/qui@eaad3bf): docs: update social card image in Docusaurus configuration ([@​s0up4200](https://github.com/s0up4200)) - [`02a68e5`](autobrr/qui@02a68e5): refactor(crossseed): hardcode ignore patterns for file matching ([#​915](autobrr/qui#915)) ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.11.0...v1.12.0> #### Docker images - `docker pull ghcr.io/autobrr/qui:v1.12.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:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4yIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/3060 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Summary
Hardcoded extensions:
.nfo,.srr,.srt,.sub,.idx,.ass,.ssa,.sup,.vtt,.txtHardcoded path keywords:
sample,!sample,proof,extras,bonus,trailer,featuretteChanges
ignore_patterns.gowith hardcoded extension and keyword listsshouldIgnoreFileto use hardcoded patterns instead of user-configurable onesignorePatternsfield from settings model, API handlers, and service layerignore_patternscolumnTest plan
Summary by CodeRabbit
Documentation
Changes
Migration
✏️ Tip: You can customize this high-level summary in your review settings.