feat(orphan): add auto cleanup mode - #897
Conversation
Add ability to automatically delete torrents when seeding goals are reached (ratio or seeding time limit). Supports two delete modes: - delete: removes torrent entry, keeps files - deleteWithFiles: removes torrent entry and data files Deletion only triggers when torrent is completed and has reached either the configured ratio limit or seeding time limit.
Add new delete mode "deleteWithFilesPreserveCrossSeeds" that safely deletes torrents and files only when no cross-seed is detected sharing the same data. When a cross-seed is found (matching ContentPath), only the torrent entry is removed while files are preserved. - Add detectCrossSeeds() function using ContentPath comparison - Add normalizePath() helper for consistent path matching - Update deletion logic to route cross-seed mode to appropriate action - Add 4th option to TrackerRulesPanel dropdown - Update badge display to show "XS safe" for cross-seed mode
Add ability to automatically delete torrents that are no longer registered with the tracker. Uses existing 60-second cached UnregisteredSet from SyncManager. - Add delete_unregistered column to tracker_rules table - Add DeleteUnregistered field to model, handler, and types - Add unregistered deletion logic to service (reuses delete modes) - Add checkbox and badge to TrackerRulesPanel
- Include torrent name, hash, and specific reason in all deletion logs - Log ratio/seeding time values that triggered deletion - Add filesKept boolean for clear indication of file fate - Consolidate cross-seed detection into single log message - Use user-friendly summary: "removed N torrents (files kept)" vs "with files"
Previously torrents were marked in lastDeleted map before BulkAction, blocking retry for 5 minutes even if deletion failed. - Move deletion timestamp to after successful BulkAction - Add hashes to failure logs for debugging
…ents - Add queuedForDeletion set to skip torrents already queued by ratio/seeding limits when processing unregistered torrents - Update DeleteMode comments to include deleteWithFilesPreserveCrossSeeds
- Move "Delete unregistered" checkbox before delete mode selector - Disable checkbox when no delete mode is selected - Add tooltip explaining why checkbox is disabled - Clear deleteUnregistered when switching to "Don't delete" - Clarify that "Don't delete" just pauses torrents
Use AlertDialog for delete confirmation, consistent with rest of codebase.
…tion
- Introduced a new option to apply rules to all trackers, allowing users to select a single pattern ("*") for broader matching.
- Updated validation to require at least one tracker or the 'Apply to all' option to be enabled.
- Adjusted the TrackerRulesPanel to include a toggle for the new feature, improving user experience in tracker management.
- Add 'Speed limits' and 'Seeding limits & auto-delete' sections - Add OR separator between ratio and seeding time fields - Make dialog scrollable with fixed header/footer - Move Enabled toggle to footer alongside Cancel/Save buttons - Constrain dialog height to 90dvh for smaller screens
… categories and tags - Refactor TrackerRule and TrackerRulePayload to replace single category and tag fields with arrays for categories and tags. - Implement cleanStringSlice function to handle trimming and filtering of category and tag inputs. - Update database interactions to accommodate new array structure for categories and tags. - Modify TrackerRulesPanel to support multi-select for categories and tags, enhancing user experience in rule management.
Add tagMatchMode field to tracker rules allowing users to choose whether torrents must match ANY (default) or ALL of the specified tags. Categories continue to use ANY matching since torrents can only have one category. - Add TagMatchModeAny/TagMatchModeAll constants to reduce string literals - Add tag_match_mode column via migration 037 - Update selectRule() to handle both matching modes - Add input validation for tagMatchMode in Create/Update handlers - Add frontend toggle (shown when 2+ tags selected) - Auto-reset tagMatchMode to "any" when tags drop below 2 - Update RuleSummary to display "(any)" or "(all)" for multiple tags
Add database-backed activity logging for tracker rules that persists through restarts. Records deletions (ratio/seeding/unregistered) and errors (delete/limit failures) with tracker domain, rule info, and detailed metadata. - Add tracker_rule_activity table with migration - Add TrackerRuleActivityStore with Create, ListByInstance, DeleteOlderThan, Prune - Record activity on successful/failed deletions and limit changes - Add API endpoints for listing and clearing activity - Add TrackerRulesActivityOverview component on Services page - Auto-prune entries older than 7 days (configurable) - Include tracker domain in activity records for filtering
…vior Reduce MaxBatchHashes from 150 to 50 to match qBittorrent's max_concurrent_http_announces default. This ensures each batch completes tracker announces in a single wave (~2s) rather than multiple waves, reducing timeout risk with reverse proxies while maintaining the same total processing time. Add documentation explaining that libtorrent automatically sends stopped announces with final stats when torrents are deleted, so no manual pause/reannounce step is needed. Works identically for v1 and v2 torrents.
- Add deleteMode validation in Create/Update handlers - Log JSON decode errors for debugging - Return 503 when service unavailable in ApplyNow - Log activity store errors instead of discarding - Add cleanupStaleEntries to prevent memory growth in lastDeleted map - Fix "Delete all" to actually delete all activity (days=0)
Add comprehensive tests for the trackerrules service covering: - matchesTracker: pattern matching (wildcard, exact, suffix, glob, multi-pattern) - shouldDeleteTorrent: deletion logic (progress, mode, limits, ratio, seeding time) - detectCrossSeeds: cross-seed detection with path normalization - selectRule: rule selection (enabled, tracker, category, tag modes) - limitHashBatch: batch splitting edge cases - normalizePath: path normalization - torrentHasTag: tag matching 92 test cases covering the high-risk decision logic areas.
… labels - Add tracker customization support (icons and display names) - Change outcome badge from 'success' to 'Removed' for clarity - Rename failed action labels to avoid redundancy: - 'Delete failed' → 'Delete' + 'Failed' - 'Limit failed' → 'Set limits' + 'Failed' - Update description to 'History of torrents removed by tracker rules'
Only show event.reason for failures where it contains the actual error message. For successes, the action badge and details line already provide all necessary information without the redundant text.
Store deleteMode in activity details JSON so the frontend can display contextual labels based on the configured delete mode and outcome: - "Torrent only" - delete mode configured to keep files - "Files kept due to cross-seeds" - cross-seeds detected, files preserved - "With files" - files deleted as configured - "Files kept"/"Files deleted" - legacy fallback for existing records
- Extract TrackerRuleDialog into reusable component - Remove redundant summary section and Configure button from overview - Add direct edit/delete actions on each rule in overview - Use TruncatedText for rule names and release names (tooltip on truncate only) - Add cursor-default to non-interactive badges - Fix "All trackers" detection when pattern is "*" - Add delete mode badge to rule preview
- Extend MultiSelect to support optional icon prop in options - TrackerRuleDialog now shows tracker icons with display names - Merged domains display as single option (e.g., "BTN" vs individual domains) - Map existing rule domains to merged options when editing - Update Services page to 2-column grid layout for rules/activity - Expand all instance accordions by default on first load - Update description text to "Automatic limits and deletion" - Move rule name to details row in activity overview
…s and actions - Restructure dialog: triggers first (seeding limits, unregistered), action last - Rename section from 'Seeding limits & auto-delete' to 'Auto-removal' - Clearer action labels: 'Pause torrent' vs 'Remove torrent (keep files)' etc - Destructive red styling for removal actions in dropdown and when selected - Always-enabled unregistered checkbox with auto-set removal action - Add validation: block submission if unregistered enabled without removal action - Update min values: ratio min=0.01, time min=1 (prevent invalid 0/negative) - Update speed limits description to 'Applied continuously to matching torrents' - Remove unused imports (DialogDescription, Tooltip)
Add a powerful condition system for tracker rules that allows users to create complex deletion criteria using a visual query builder. Backend: - Add condition evaluator with AND/OR logic, nested groups, and negation - Support string operators (equals, contains, starts/ends with, regex) - Support numeric operators (comparisons, between ranges) - Add preview endpoint to show affected torrents before enabling rules - Add `deleted_condition` activity action for expression-based deletions - Fix deletion reason tracking (prefer seeding time when both limits met) Frontend: - Add QueryBuilder component with drag-and-drop condition reordering - Support 20+ torrent fields (name, size, ratio, seeding time, state, etc.) - Add rule preview dialog showing torrents that would be deleted - Add unregistered torrent preview support - Add "Load more" pagination to activity overview - Remove auto-expand behavior from accordions Migration adds conditions JSON column to tracker_rules table.
- Add shadcn Calendar component with react-day-picker for date selection - Add theme-aware CSS overrides for calendar to use app color variables - Timestamp fields now use styled calendar popover instead of native input - BETWEEN operator for timestamps shows two date pickers (From - To) - Duration fields now include hours option (minutes, hours, days) - Fix duration unit selection bug where unit couldn't be changed before entering value - Add helper text to auto-removal section explaining the options
- Add previewLimit/previewOffset params to preview API endpoint - Implement 'Load more' button in preview dialog for large result sets - Apply ratio color coding to preview table - Use TruncatedText for category column
…d selector - Add deleteUnregisteredMinAge field to support minimum age filtering for unregistered torrent deletion (simple mode with dropdown: 1/3/7/14/30 days) - Add IS_UNREGISTERED boolean field to query builder for advanced mode, allowing isUnregistered to be combined with any other conditions - Add EvalContext to pass unregistered status to condition evaluator - Create new Tracker field group with: Tracker, Trackers, Private, Unregistered, Comment - Replace field dropdown with searchable combobox using Command component for better UX when selecting from many fields - Add delete mode constants to replace magic strings - Fix nil map access in PreviewDeleteRule when health counts are empty
Add tag action as 4th mutually exclusive action type alongside speedLimits, pause, and delete. Implements tqm-style smart tagging: - Three modes: full (add+remove), add-only, remove-only - Condition-based evaluation for when to apply/remove tags - Version-aware API: uses SetTags (qBit 5.1+) or falls back to AddTags/RemoveTags for older versions - Batch activity logging with per-tag counts for adds/removes - Startup race condition fix: skips tag evaluation when health data isn't available yet for IS_UNREGISTERED conditions Also removes dead code: lastDeleted map and 5-minute deletion cooldown (deleted torrents can't be re-processed anyway).
- Rename internal/services/trackerrules/ to internal/services/automations/ - Rename models: TrackerRule -> Automation, TrackerRuleStore -> AutomationStore - Rename handlers: TrackerRuleHandler -> AutomationHandler - Update API route: /tracker-rules -> /automations - Add migration 036 to drop old tables and create new automations tables - Update all frontend components and types to use Automation naming - Update README to reference Automations feature
- Removed timestamp fields from CONDITION_FIELDS and updated FIELD_GROUPS accordingly. - Adjusted FieldType to exclude 'timestamp' and removed related operators from OPERATORS_BY_TYPE.
…filter conditions
Make timeout configurable via Config.ApplyTimeout for large instances.
Replace defer resp.Body.Close() with httphelpers.DrainAndClose(resp) in arr client to drain response bodies before closing, enabling proper HTTP keep-alive connection reuse.
Add opt-in auto-cleanup that allows scheduled orphan scans to automatically delete found orphans without manual confirmation. Behavior: - Manual scans always show preview (unchanged) - Scheduled scans + auto-cleanup OFF: show preview (current behavior) - Scheduled scans + auto-cleanup ON: auto-delete after scan Safety features: - Default OFF (opt-in only) - File count threshold (default 100): if scan finds more files than threshold, skip auto-cleanup and require manual review - Existing safeguards (grace period, ignore paths) still apply Settings: - autoCleanupEnabled: bool (default false) - autoCleanupMaxFiles: int (default 100) Also removes the requirement to enable scanning before configuring settings - users can now set up everything first, then enable.
WalkthroughAdds auto-cleanup controls for orphan scans: two new settings (AutoCleanupEnabled, AutoCleanupMaxFiles) persisted via a DB migration, wired through models, API handlers, service logic (maybeAutoCleanup that may trigger deletions after scheduled scans), and exposed in the frontend settings UI. Changes
Sequence DiagramsequenceDiagram
participant Scheduler
participant Service
participant DB
participant Deleter
Note over Scheduler,Service: Scheduled orphan scan run
Scheduler->>Service: Trigger scan (scheduled)
Service->>Service: Execute scan, collect orphans (filesFound)
Service->>DB: Update run record (preview_ready, results)
Service->>Service: maybeAutoCleanup(ctx, instanceID, runID, settings, filesFound)
Service->>DB: Fetch run details (verify TriggeredBy)
DB-->>Service: Run info (e.g., TriggeredBy="scheduled")
Service->>Service: Check settings.AutoCleanupEnabled and threshold (AutoCleanupMaxFiles)
alt Conditions met (scheduled && enabled && filesFound ≤ threshold)
Service->>Deleter: Initiate ConfirmDeletion(runID, files)
Deleter->>DB: Record deletion actions / mark files
Note over Service,Deleter: Automatic cleanup started
else Conditions not met
Service->>Service: Log reason and skip auto-cleanup
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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)
🚧 Files skipped from review as they are similar to previous changes (1)
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 |
…headers Move Switch components outside AccordionTrigger to avoid invalid HTML nesting (button inside button). Use CSS Grid layout to maintain proper spacing with controls aligned to the right.
…splay - Replace Sheet with Dialog for reannounce settings to match orphan scan pattern - Show settings form even when monitoring is disabled (allow config before enabling) - Add tracker icons and customizations to tracker domain selector - Add hideCheckIcon prop to MultiSelect for better alignment when using icons
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
web/src/components/instances/preferences/OrphanScanOverview.tsx (1)
211-223: Consider displaying auto-cleanup settings in the summary.The settings summary displays grace period, interval, and max files, but doesn't show the new auto-cleanup settings (
autoCleanupEnabled,autoCleanupMaxFiles) introduced in this PR. Since auto-cleanup can automatically delete files without confirmation, users might benefit from seeing its status at a glance in this overview.If this information is deemed important for quick reference, consider adding it to the summary line or as a separate indicator.
web/src/components/instances/preferences/TrackerReannounceForm.tsx (1)
98-167: LGTM! Comprehensive tracker customization logic.The tracker processing correctly handles domain merging, display name deduplication, and icon rendering. The memoization dependencies are appropriate, and the logic properly skips secondary domains to avoid duplicate entries.
Optional: Consider extracting tracker processing logic
The tracker customization mapping (lines 99-121) and processing (lines 123-167) are well-structured but complex. For maintainability, consider extracting into a separate hook or utility function:
// Example structure function useProcessedTrackerOptions( trackersData: Record<string, unknown> | undefined, trackerCustomizations: TrackerCustomization[] | undefined, trackerIcons: TrackerIcon[] | undefined ): Option[]This would improve testability and reduce cognitive load in the form component.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
web/src/components/instances/preferences/OrphanScanOverview.tsxweb/src/components/instances/preferences/OrphanScanSettingsForm.tsxweb/src/components/instances/preferences/ReannounceOverview.tsxweb/src/components/instances/preferences/ReannounceSettingsDialog.tsxweb/src/components/instances/preferences/TrackerReannounceForm.tsxweb/src/components/instances/preferences/WorkflowDialog.tsxweb/src/components/ui/multi-select.tsxweb/src/pages/Automations.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-25T11:39:54.748Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 637
File: web/src/pages/Dashboard.tsx:805-831
Timestamp: 2025-11-25T11:39:54.748Z
Learning: In web/src/pages/Dashboard.tsx, the TrackerIconImage component intentionally receives displayDomain (incognito-mapped name) instead of the real domain in incognito mode. This causes icon lookups to fail and show only fallback letters, which is desired behavior for privacy - hiding both tracker names and icons when incognito mode is enabled.
Applied to files:
web/src/components/instances/preferences/WorkflowDialog.tsxweb/src/components/instances/preferences/TrackerReannounceForm.tsx
🧬 Code graph analysis (4)
web/src/components/ui/multi-select.tsx (1)
web/src/lib/utils.ts (1)
cn(10-12)
web/src/components/instances/preferences/ReannounceSettingsDialog.tsx (4)
web/src/hooks/useInstances.ts (1)
useInstances(10-188)web/src/components/ui/dialog.tsx (6)
Dialog(136-136)DialogContent(138-138)DialogHeader(141-141)DialogTitle(144-144)DialogDescription(139-139)DialogFooter(140-140)web/src/components/instances/preferences/TrackerReannounceForm.tsx (1)
TrackerReannounceForm(63-743)web/src/components/ui/button.tsx (1)
Button(64-64)
web/src/components/instances/preferences/OrphanScanSettingsForm.tsx (6)
web/src/components/ui/separator.tsx (1)
Separator(33-33)web/src/components/ui/label.tsx (1)
Label(29-29)web/src/components/ui/tooltip.tsx (3)
Tooltip(184-184)TooltipTrigger(184-184)TooltipContent(184-184)web/src/components/ui/switch.tsx (1)
Switch(34-34)web/src/components/ui/input.tsx (1)
Input(26-26)web/src/components/ui/button.tsx (1)
Button(64-64)
web/src/pages/Automations.tsx (2)
web/src/hooks/useInstances.ts (1)
useInstances(10-188)web/src/components/instances/preferences/ReannounceSettingsDialog.tsx (1)
ReannounceSettingsDialog(27-60)
🔇 Additional comments (10)
web/src/components/instances/preferences/OrphanScanOverview.tsx (1)
151-206: LGTM! Well-structured header refactoring.The two-column grid layout cleanly separates the accordion trigger from the toggle controls, preventing accidental expansion when users interact with the switch. The
stopPropagationhandler and custom chevron with group-scoped animation are implemented correctly.web/src/components/instances/preferences/WorkflowDialog.tsx (1)
682-682: LGTM! UI polish for tracker selection.The
hideCheckIconprop cleans up the visual presentation when tracker icons are displayed, preventing redundant check marks alongside icons.web/src/components/ui/multi-select.tsx (1)
26-27: LGTM! Well-implemented optional feature.The
hideCheckIconprop is properly typed, documented, and defaults tofalsefor backward compatibility. The conditional rendering preserves existing behavior when the prop is not provided.Also applies to: 39-39, 158-165
web/src/components/instances/preferences/ReannounceSettingsDialog.tsx (1)
1-60: LGTM! Clean dialog implementation.The dialog properly coordinates form submission via
FORM_ID, manages loading state withisUpdating, and handles the success flow by closing on completion. The structure aligns with the new dialog-based configuration pattern described in the PR.web/src/components/instances/preferences/ReannounceOverview.tsx (1)
209-254: LGTM! Improved accordion header layout.The grid-based restructure provides better visual separation between the title/status badges and the enable toggle/chevron. The
stopPropagationon the switch prevents accidental accordion collapse, and the custom chevron rotation is properly tied to the accordion state.web/src/components/instances/preferences/TrackerReannounceForm.tsx (1)
554-554: LGTM! Consistent with tracker selection UX.The
hideCheckIconprop usage aligns with the WorkflowDialog tracker selection, providing a cleaner visual when icons are present.web/src/pages/Automations.tsx (1)
9-9: LGTM! Clean migration to dialog-based configuration.The refactor successfully decouples reannounce configuration into a dedicated dialog, simplifying the parent component. The removal of
isUpdatingusage is appropriate since it's now managed withinReannounceSettingsDialog. The variable rename toconfigureReannounceInstanceimproves clarity.Also applies to: 15-15, 37-37, 61-61, 77-82
web/src/components/instances/preferences/OrphanScanSettingsForm.tsx (3)
33-34: LGTM! Well-structured auto-cleanup feature with appropriate safeguards.The auto-cleanup implementation follows the PR's safety-first approach:
- Defaults to disabled (opt-in)
- File threshold with sensible default (100)
- Conditional UI shows threshold only when enabled
- Clear tooltips explain behavior and risks
- Validation ensures minimum of 1 file
The feature aligns with the PR objectives and provides the necessary safety mechanisms for automated deletion.
Also applies to: 57-58, 71-72, 232-295
94-96: Verify behavioral change: toggle now requires explicit save.The
handleToggleEnabledfunction now only updates local state without persisting, requiring users to click "Save Changes" to commit the toggle. This differs from the typical pattern where enable/disable toggles save immediately.Is this intentional? If so, it's consistent with the form-based approach. If not, consider restoring immediate persistence for the enabled toggle while keeping other settings requiring explicit save.
For comparison,
ReannounceOverview.tsx(line 94-125) and the reannounce form'shandleToggleEnabled(TrackerReannounceForm.tsx, line 254-261) both persist immediately.
140-230: LGTM! Removal of conditional rendering improves UX.Always showing the Schedule section aligns with the PR objective: "removes the requirement to enable scanning before configuring scan-related settings, allowing users to configure settings prior to enabling scanning." This is a positive UX improvement.
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>
Add opt-in auto-cleanup that allows scheduled orphan scans to automatically delete found orphans without manual confirmation.
Behavior:
Safety features:
Settings:
Also removes the requirement to enable scanning before configuring settings - users can now set up everything first, then enable.
Goes in after #889
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.