Skip to content

feat(downloads): reap stale ImportBlocked downloads that can't resolve - #992

Open
dny238 wants to merge 1 commit into
Listenarrs:canaryfrom
dny238:feat/reap-stale-blocked-downloads
Open

dny238 wants to merge 1 commit into
Listenarrs:canaryfrom
dny238:feat/reap-stale-blocked-downloads

Conversation

@dny238

@dny238 dny238 commented Sep 17, 2026

Copy link
Copy Markdown

Summary

Downloads parked in ImportBlocked stay on the Activity page indefinitely, even after the situation behind them is gone — e.g. the audiobook was deleted, or the book was received another way and already has files. Nothing ever clears them, so they accumulate as stale "activity" that's no longer relevant or actionable.

This adds a small background reaper that removes an ImportBlocked download when it can no longer resolve to anything actionable.

Reap conditions

An ImportBlocked download is removed when:

  • it has no associated audiobook, or
  • its audiobook no longer exists (was deleted), or
  • its audiobook already has at least one file (the book was received another way, so the blocked import is redundant).

A blocked download whose audiobook still exists with no files is deliberately left alone — that one is a genuine unresolved failure the user may still want to retry (via the existing retry-import action).

Design

Mirrors the existing MovedDownloadCleanup pair — a processor (StaleBlockedDownloadCleanupProcessor) plus a hosted BackgroundService, registered through the same AddHostedProcessor<…> helper. It runs on a relaxed 5-minute cadence (1-minute initial delay) since this is low-urgency housekeeping, not the download-polling hot path. Removal uses the same IDownloadRepository.RemoveAsync path as the manual delete endpoint. Each reap is logged with its reason.

GetActiveAsync intentionally excludes ImportBlocked, so the reaper reads the full set and filters.

Tests

StaleBlockedDownloadCleanupProcessorTests covers all three reap conditions, the leave-alone case (audiobook exists, no files), and that non-ImportBlocked downloads are never touched. Architecture gates (file-focus, test conventions) green.

Note on scope

The "already has files" condition is the one judgement call — it treats a book that's been received as satisfying any older blocked import for it. If you'd prefer that gated (e.g. only when the block is also older than N hours, to avoid ever dropping a just-failed upgrade), easy to add — flagging it for review.

🤖 Generated with Claude Code

Downloads parked in ImportBlocked stay on the Activity page forever, even once
the situation behind them is gone — e.g. the audiobook was deleted, or the book
was received another way and already has files. Nothing clears them, so they
pile up as confusing "activity" that's no longer relevant.

Add a small background reaper (mirrors the existing MovedDownloadCleanup pair:
processor + hosted service on a relaxed 5-minute cadence) that removes an
ImportBlocked download when it can no longer resolve to anything actionable:
- it has no associated audiobook, or
- its audiobook no longer exists (deleted), or
- its audiobook already has at least one file (received another way).

A blocked download whose audiobook still exists with no files is left alone —
that's a genuine unresolved failure the user may still want to retry.

Tests cover all three reap conditions, the leave-alone case, and that
non-blocked downloads are never touched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dny238
dny238 requested a review from a team September 17, 2026 14:38
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