Skip to content

feat(library): add cancel, status, and recovery for Aurral album downloads - #918

Merged
lklynet merged 7 commits into
mainfrom
t3code/find-next-task-spec
Sep 25, 2026
Merged

lklynet merged 7 commits into
mainfrom
t3code/find-next-task-spec

Conversation

@lklynet

@lklynet lklynet commented Sep 25, 2026

Copy link
Copy Markdown
Owner

What changed

  • Aurral-managed albums can be cancelled with POST /api/library/albums/aurral/:canonicalId/cancel. Queued tracks stop right away, and active transfers are stopped through each download source's existing cleanup. Finished tracks stay in place.
  • A cancelled album job stays cancelled. After a restart, interrupted downloads go back to the queue, but cancelled ones do not. Late pipeline errors can no longer turn a cancelled job into a failure.
  • GET /api/library/albums/aurral/:canonicalId/status reports one status for the album: complete, downloading, queued, blocked, cancelled, failed, partial, or missing. The response has per-track counts, and a recovery object when the user needs to do something (download_source_missing, review_required, source_failed). GET /api/library/downloads/status accepts aurral:<canonicalId> keys. Album add and request responses include the same status as albumStatus.
  • Requesting an album again retries its cancelled and failed tracks using the existing jobs. If no download source is configured, the request saves the album and reports the missing source. It no longer queues jobs that can only fail.
  • In Activity, cancelled album tracks show as cancelled, not as timed-out failures.
  • Aurral album requests are no longer checked against Lidarr's search history. Before this change, Aurral looked up the canonical album ID in Lidarr and could mark an Aurral album as "No results".
  • The API docs cover the new routes and statuses.

Why

Aurral albums (from #855) queue one download job per track, but they had no way to cancel, no album-level status, and restarts and late errors could bring back or fail work the user had stopped. Aurral albums need a full download lifecycle before Aurral can manage albums without Lidarr. Lidarr behavior is unchanged. None of these paths call Lidarr.

This is part of the optional-Lidarr roadmap. Artist monitoring builds on it in the stacked follow-up PR.

Scope checklist

  • This pull request has one clear purpose
  • I kept unrelated fixes, refactors, formatting changes, dependency updates, and features out of this pull request
  • If this adds a feature, I linked the approved feature request or included the Discord context in the Why section

Linked issue

No linked issue. The work is tracked on the maintainer roadmap for optional Lidarr support.

UI changes

No new screens or controls. The library album view and Activity now show a cancelled track state (an X icon labelled "Cancelled") instead of treating it as searching or failed. I didn't capture screenshots because no UI control can cancel an album yet. The destination and cancel controls come in a later PR.

Testing

  • npm run lint: passed
  • npm test: 1,347 passed, 0 failed
  • npm run docs:build: passed
  • New .tests/library/aurral-album-lifecycle.test.js covers:
    • restart recovery, and late pipeline transitions after a cancel
    • cancel through the route with yt-dlp staging cleanup
    • a provider cleanup failure
    • unknown, malformed, and Lidarr-managed albums
    • every aggregate status
    • retry in place, and no download source
  • Extended .tests/library/aurral-owned-writes.test.js and .tests/history/aurral-history.test.js.
  • Each new test failed before its change.
  • Live check against the shared test services, with Lidarr connected and managedBy: "aurral":
    • Requested a 16-track album, then cancelled it mid-download: all 16 jobs cancelled and no failures were logged.
    • Restarted: the album stayed cancelled.
    • Requested it again: the same 16 jobs were requeued, with no duplicates.
    • Activity showed 16 cancelled tracks and no failed entries.
    • No test files were left in the shared download folders.

Release impact

  • Major: incompatible change
  • Minor: backward-compatible feature
  • Patch: backward-compatible fix
  • None: documentation, CI, tests, or internal-only change

…arts

Album jobs flagged for cancellation ignore late pipeline transitions and
restart moves interrupted cancellations to cancelled instead of pending.
Adds POST /api/library/albums/aurral/:canonicalId/cancel. Pending tracks
are cancelled immediately, active transfers are stopped through the
existing provider cleanup, and finished tracks stay in place.
Adds GET /api/library/albums/aurral/:canonicalId/status and aurral:<id>
keys on the batch download status route. Status combines canonical track
availability with per-track jobs and includes an actionable recovery for
blocked and failed albums. Album add and request responses include the
same albumStatus.
…source

Re-requesting an album requeues its cancelled and failed tracks in place.
Without a configured download source the request keeps the album state,
queues nothing, and reports the missing source instead of creating jobs
that can only fail.
Activity history records cancelled album track downloads as cancelled
instead of timing them out as failures, and the library and activity views
render the new state. Documents the Aurral album status and cancel routes.
Aurral album requests were recorded as Lidarr searches and the sync looked
up their canonical album ID in Lidarr, which marked cancelled or queued
Aurral albums as not found.
@github-actions github-actions Bot added enhancement Requested improvement or new capability. size:XL 500-999 changed lines. labels Sep 25, 2026
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Aurral preview image ready

This image was rebuilt from the latest push to this pull request. It will be replaced when you push another change.

docker pull ghcr.io/lklynet/aurral:pr-918

To test it with your existing Docker Compose setup:

  1. Back up your Aurral config.
  2. Temporarily change the Aurral service image to ghcr.io/lklynet/aurral:pr-918.
  3. Run docker compose pull aurral && docker compose up -d aurral.
  4. Exercise the behavior changed by this pull request.
  5. Restore the image reference that was configured before testing.

View the preview workflow run · Report a problem

Comment thread backend/services/libraryManager.js
Comment thread backend/services/weeklyFlow/weeklyFlowDownloadTracker.js
@github-actions github-actions Bot added size:XL 500-999 changed lines. and removed size:XL 500-999 changed lines. labels Sep 25, 2026
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: QUIET

Plan: Advanced

Run ID: e12fd2ae-9e36-4ee6-9ec8-3dc1c62709c1

📥 Commits

Reviewing files that changed from the base of the PR and between b056fc7 and b056fc7.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds cancellation states and recovery handling for download jobs, Aurral album status summaries and cancellation operations, and retry behavior for cancelled or failed tracks. New album endpoints and prefixed download-status lookups expose Aurral status. History records manager ownership and cancellation, while the frontend displays cancelled requests.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to b056f

Cancellation activity can show a misleading state, and an album with same-title tracks may miss a download. These bounded cases warrant fixes or explicit acceptance before merge.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to b056f

Cancellation has appropriate permission checks, but a retry can race with cleanup and a restart can leave cancelled work appearing queued. These are bounded lifecycle risks rather than evidence of unauthorized access.

Retained concerns

  • Medium · reliability · inferred: A concurrent album re-request can restore a pending job while its cancellation is still awaiting provider cleanup. When cleanup returns, the original cancellation can mark that retried job cancelled again, so the retry and terminal status do not have a stable owner. The provider effects of this interleaving are not established.
  • Low · reliability · inferred: A process interruption after cancellation markers are persisted but before a pending job is marked cancelled leaves that job pending after restart. The marker prevents it from running, yet album status can report it as queued and re-request treats it as active rather than retryable.
Security review details

Security Blast Radius

  • observed — Cancellation is scoped by library playlist type, Aurral management, and normalized album ID before job IDs are passed to shared cancellation and provider cleanup.

Security Findings and Attack Paths

  • inferred — No unauthorized access path is established. The status route is subject to configured global authentication and shares the existing album-read exposure model; deployments configured without required authentication can expose album reads, including this new status response, anonymously.

Trust Boundaries and Controls

  • observed — The mutating route applies requireAuth and requirePermission before reaching the library manager; the permission check rejects requests without a user or addAlbum authority.

Resilience and Maintainability Implications

  • inferred — Durable cancellation markers prevent marked pending jobs from being dispatched, but tracker status and recovery can disagree with those markers after interruption. Concurrent retry presents a separate ordering gap because cleanup and restoration act on the same job ID.

Hardening Proposals

  • proposed — Serialize cancellation finalization and retry for each album or job, and reconcile pending jobs with durable cancellation markers during restart recovery.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 11 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: cancellation, status, and recovery for Aurral album downloads.
Description check ✅ Passed The description covers the change, rationale, scope, testing, UI impact, linked-issue context, and release impact. It is complete and focused.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 11 files. (1 skipped: 1 unsupported.)


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
backend/services/aurralHistoryService.js-609-613 (1)

609-613: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Represent cancel_requested jobs as cancellation-pending.

When provider cleanup fails, the tracker remains cancel_requested. The history sync skips this state, so the row remains processing without showing that cancellation is pending. Do not mark the row cancelled only because it is stale. Provider work may still be active. Record a cancellation-pending state and use cancelled only after cleanup succeeds and the tracker reaches cancelled.

backend/services/aurralAlbumJobs.js-13-26 (1)

13-26: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use title fallback only when both MBIDs are absent.

jobMatchesTrack falls back to the normalized title when only one side has an MBID. An album can contain distinct canonical tracks with the same title because tracks use separate canonical IDs, while imported tracks can have a null MBID. During _finishAurralAlbum, this can match one track to another track’s active or completed job and skip its download.

Suggested fix
 export function jobMatchesTrack(job, track) {
-  if (job.trackMbid && track.mbid) {
-    return normalizeKey(job.trackMbid) === normalizeKey(track.mbid);
+  const jobMbid = normalizeKey(job.trackMbid);
+  const trackMbid = normalizeKey(track.mbid);
+  if (jobMbid || trackMbid) {
+    return Boolean(jobMbid && trackMbid) && jobMbid === trackMbid;
   }
   return normalizeKey(job.trackName) === normalizeKey(track.title);
 }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: QUIET

Plan: Advanced

Run ID: 41fc2af2-53f4-4afd-9c82-ada282a6c0d7

📥 Commits

Reviewing files that changed from the base of the PR and between 6059bc1 and b056fc7.

📒 Files selected for processing (12)
  • .tests/history/aurral-history.test.js
  • .tests/library/aurral-album-lifecycle.test.js
  • .tests/library/aurral-owned-writes.test.js
  • backend/routes/library/handlers/albums.js
  • backend/routes/library/handlers/downloads.js
  • backend/services/aurralAlbumJobs.js
  • backend/services/aurralHistoryService.js
  • backend/services/libraryManager.js
  • backend/services/weeklyFlow/weeklyFlowDownloadTracker.js
  • docs/src/content/docs/api/endpoints.mdx
  • frontend/src/pages/LibraryPage.jsx
  • frontend/src/pages/activity/ActivityRequestRow.jsx

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.

@lklynet
lklynet added this pull request to stack #920 September 25, 2026 20:46
@lklynet
lklynet merged commit 2ca8920 into main Sep 25, 2026
9 of 11 checks passed
@lklynet
lklynet deleted the t3code/find-next-task-spec branch September 25, 2026 21:41
@github-actions github-actions Bot mentioned this pull request Sep 25, 2026
5 tasks
@github-actions github-actions Bot added the nightly Available in the nightly image but not yet in a stable release. label Sep 25, 2026
@github-actions

Copy link
Copy Markdown

Available on nightly

A linked pull request was merged into main and is now included in the latest nightly build. Linked issues stay open until this change ships in a stable release.

docker pull ghcr.io/lklynet/aurral:nightly

lklynet added a commit that referenced this pull request Sep 25, 2026
> Stacked on #918. This PR targets that branch, so the diff shows only
the monitoring changes. After #918 merges, rebase onto `main` and
retarget before merging.

## What changed

- Aurral-managed artists can be monitored without Lidarr. `PUT
/api/library/artists/:mbid` and artist add accept these `monitorOption`
values:
  - `all` and `missing` queue every album that is not complete yet.
  - `latest` and `first` queue the newest or oldest album.
  - `future` queues only albums released after monitoring starts.
  - `none` stops new work and keeps existing media.

`existing` applies only to Lidarr and returns `400
unsupported_monitor_mode` for Aurral artists.
- Only official albums and EPs with no secondary type are picked up.
Albums managed by Lidarr are skipped and listed in `monitoring.skipped`.
The chosen albums go through the existing Aurral album request and
download pipeline, in a system task.
- If artist metadata can't be loaded, the request returns `503
metadata_unavailable` and the previous monitor mode is kept.
- `PUT /api/library/albums/aurral/:canonicalId` with `{ "monitored":
false }` cancels that album's downloads and keeps finished tracks. The
choice is kept when the artist's monitoring changes. `{ "monitored":
true }` queues the album again.
- A daily `aurral-monitoring-reconcile` system task queues newly listed
releases for monitored Aurral artists. It doesn't requeue albums already
in the library, and one artist's metadata failure doesn't stop the run.
- The library-ownership cache now picks up changes made by other
processes. Before this change, albums created by the system task looked
unmanaged in the web process until a restart.
- The user and API docs cover Aurral monitoring.

## Why

Monitoring and new-release acquisition required Lidarr. This lets users
who don't run Lidarr monitor artists and get their albums through
Aurral's own download sources. Artists and albums managed by Lidarr keep
their current behavior, and Aurral monitoring never calls Lidarr.

This is part of the optional-Lidarr roadmap.

## Scope checklist

- [x] This pull request has one clear purpose
- [x] I kept unrelated fixes, refactors, formatting changes, dependency
updates, and features out of this pull request
- [x] If this adds a feature, I linked the approved feature request or
included the Discord context in the Why section

## Linked issue

No linked issue. The work is tracked on the maintainer roadmap for
optional Lidarr support.

## Testing

- `npm run lint`: passed
- `npm test`: 1,354 passed, 0 failed
- `npm run docs:build`: passed
- New `.tests/library/aurral-monitoring.test.js` goes through the artist
and album routes and the system task, using a local metadata stub. It
asserts that Lidarr is never called, and covers:
  - release eligibility and each monitor mode
  - a metadata outage
  - skipping Lidarr-managed albums
  - album overrides
- repeated reconciliation, and one artist failing during reconciliation
- `.tests/library/library-management.test.js` adds a check that writes
through a second SQLite connection. The round-trip assertion now ignores
the new `updatedAt` field and checks it separately.
- Each new test failed before its change.
- Live check against the shared test services:
- Added an artist with `latest`: the background system task created the
album and queued its 7 tracks.
  - Reconciliation ran three times without creating duplicates.
- The live check caught the ownership cache bug above. After the fix,
the running web process cancelled an album created by the background
task without a restart.
  - Setting `none` stopped new work.
  - No test files were left in the shared download folders.

## Release impact

- [ ] Major: incompatible change
- [x] Minor: backward-compatible feature
- [ ] Patch: backward-compatible fix
- [ ] None: documentation, CI, tests, or internal-only change
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Requested improvement or new capability. nightly Available in the nightly image but not yet in a stable release. size:XL 500-999 changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant