Skip to content

Add item grabbed notification#564

Merged
Flaminel merged 5 commits into
mainfrom
add_item_grabbed_notification
Apr 15, 2026
Merged

Add item grabbed notification#564
Flaminel merged 5 commits into
mainfrom
add_item_grabbed_notification

Conversation

@Flaminel

Copy link
Copy Markdown
Contributor

Relates to #557

@Flaminel

Flaminel commented Apr 15, 2026

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

@Flaminel

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Apr 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a SearchItemGrabbed notification event that fires when a completed Seeker search results in a download being grabbed. It also refactors SeekerCommandMonitor from batch-per-event tracker processing to one-tracker-per-search, removes the now-redundant item_type column from seeker_command_trackers, and adds the new event flag across all 7 notification providers (UI + backend + migration).

  • The new SearchItemAsync wrapper in ArrClient calls ids.First() on the result of SearchItemsAsync, which will throw InvalidOperationException if the list is empty. The old code had an explicit guard (if (commandIds.Count == 0) return;) that handled this gracefully; the new path propagates an exception to the callers' catch blocks instead.

Confidence Score: 4/5

Safe to merge after addressing the ids.First() throw on empty list in SearchItemAsync

The overall feature is well-structured and consistently applied across all layers, but ArrClient.SearchItemAsync removes the previous empty-list guard and calls ids.First() unconditionally. While the outer try/catch prevents a crash, it changes graceful no-op behavior to an exception, and the intent of the old guard suggests empty-list returns were considered a real possibility.

code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs (empty-list handling) and code/backend/Cleanuparr.Infrastructure/Features/Jobs/SeekerCommandMonitor.cs (early return skips orphaned terminal trackers)

Important Files Changed

Filename Overview
code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs Adds SearchItemAsync wrapper that calls SearchItemsAsync and returns ids.First() — throws if result is empty, removing the previous graceful guard
code/backend/Cleanuparr.Infrastructure/Features/Jobs/SeekerCommandMonitor.cs Refactored from batch (grouped by event) to per-tracker processing; upgraded to async scope; early return on empty pending list may skip orphaned terminal trackers in crash-recovery scenarios
code/backend/Cleanuparr.Infrastructure/Events/EventPublisher.cs Added instanceType/instanceUrl parameters to PublishSearchCompleted and wires NotifySearchItemGrabbed when grabbed items are present — correct and well-guarded
code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs Adds NotifySearchItemGrabbed method and BuildSearchItemGrabbedContext — consistent with existing patterns, all parameters passed correctly
code/backend/Cleanuparr.Persistence/Migrations/Data/20260415085810_AddSearchItemGrabbedNotification.cs Drops item_type column from seeker_command_trackers (now inferred from navigation property) and adds on_search_item_grabbed to notification_configs — reversible and correct
code/backend/Cleanuparr.Api/Features/Notifications/Controllers/NotificationProvidersController.cs Consistently adds OnSearchItemGrabbed mapping across all provider create, update, and preview paths for all 7 provider types
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs Switches from SearchItemsAsync+SaveCommandTrackersAsync to single-item SearchItemAsync+SaveCommandTrackerAsync; logic is correct but depends on ArrClient.SearchItemAsync not throwing on empty return
code/backend/Cleanuparr.Persistence/Models/State/SeekerCommandTracker.cs Removes ItemType property (now derived from ArrInstance.ArrConfig.Type navigation property) — correct given all usages load via Include

Sequence Diagram

sequenceDiagram
    participant S as Seeker
    participant AC as ArrClient
    participant EP as EventPublisher
    participant DB as DataContext
    participant SCM as SeekerCommandMonitor
    participant NP as NotificationPublisher

    S->>AC: SearchItemAsync(instance, item)
    AC->>AC: SearchItemsAsync([item])
    AC-->>S: commandId (long)
    S->>EP: PublishSearchTriggered(title, type, reason)
    EP-->>S: eventId (Guid)
    S->>DB: SeekerCommandTrackers.Add(tracker)

    Note over SCM: Background poll (every 15s)
    SCM->>AC: GetCommandStatusAsync(instance, commandId)
    AC-->>SCM: ArrCommandStatus
    SCM->>DB: tracker.Status = Completed
    SCM->>AC: GetQueueItemsAsync(instance, page 1)
    AC-->>SCM: QueueListResponse (grabbed items)
    SCM->>EP: PublishSearchCompleted(eventId, Completed, instanceType, url, grabbedItems)
    EP->>DB: Update SearchEventData.GrabbedItems
    EP->>NP: NotifySearchItemGrabbed(title, items, type, url)
    NP-->>NP: SendNotificationAsync(SearchItemGrabbed, context)
    SCM->>DB: SeekerCommandTrackers.Remove(tracker)
Loading

Comments Outside Diff (1)

  1. code/backend/Cleanuparr.Infrastructure/Features/Jobs/SeekerCommandMonitor.cs, line 79-82 (link)

    P2 Early exit skips stuck terminal trackers

    If the service crashes after the first SaveChangesAsync (which updates tracker statuses to Completed/Failed/TimedOut) but before the second one (which removes them), those terminal trackers persist in the database. On the next poll cycle, if no non-terminal trackers exist, this early return fires and the orphaned terminal trackers are never cleaned up — they won't send notifications and won't be removed.

    The old implementation queried all trackers unconditionally, so it naturally handled this edge case. Consider querying for terminal trackers separately from the pending-trackers check, or at minimum processing terminal trackers regardless of whether any pending ones exist.

Reviews (1): Last reviewed commit: "added db migration" | Re-trigger Greptile

Comment thread code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs
@Cleanuparr Cleanuparr deleted a comment from SourceryAI Apr 15, 2026
@Flaminel Flaminel merged commit 447db69 into main Apr 15, 2026
12 of 13 checks passed
@Flaminel Flaminel deleted the add_item_grabbed_notification branch April 15, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant