Skip to content

feat(webui): bulk album delete + Lidarr re-download completion tracking#20

Merged
chodeus merged 1 commit intomainfrom
feat/bulk-album-delete
Apr 19, 2026
Merged

feat(webui): bulk album delete + Lidarr re-download completion tracking#20
chodeus merged 1 commit intomainfrom
feat/bulk-album-delete

Conversation

@chodeus
Copy link
Copy Markdown
Owner

@chodeus chodeus commented Apr 19, 2026

Summary

  • Adds a whole-folder delete path to the WebUI with an async job tracker so users can purge up to 50 corrupt albums in one pass without the browser hanging. Staggered album-by-album (30s between each) to avoid flooding Lidarr and indexers, matching the existing auto-delete pipeline.
  • Adds a pending-redownload tracker — after any Lidarr-backed delete, BeatsCheck records the affected album IDs and polls Lidarr history at the start of the next scan cycle. Successful grabs are logged as LIDARR REDOWNLOAD: <artist> — <album> — grabbed (1 report) to beats_check.log. Stale entries time out after 24h.
  • Removes the redundant "Re-download" button (it was functionally identical to "Delete" — Lidarr auto-searches monitored albums after any trackfile deletion regardless of which button was clicked). Replaced with a cleaner button set: per-row Delete (corrupt files only) + Delete Album (whole folder) + Ignore.
  • Adds "Select N" helper (number input + button, capped at 50) for quick multi-album selection; respects the search filter.
  • Progress modal polls /api/delete-job-status every 2s, shows current album + phase (running / waiting / done / cancelled), supports mid-flight cancel.

Backend

  • main.pydelete_album_folders() with mount-root, MUSIC_DIR, and symlink guards; factored into _delete_one_folder, _stagger_sleep, _validate_delete_folders, _finalize_delete_state helpers to stay under the project's complexity cap.
  • main.py_record_pending_redownloads / _poll_pending_redownloads hooked into the scan-loop start; integrated with delete_corrupt_files and run_auto_delete so every Lidarr delete path records grabs.
  • webui.pyPOST /api/delete-albums (202 + job_id, fire-and-forget) / GET /api/delete-job-status / POST /api/delete-job-cancel; in-memory job store with 1h auto-prune; .scanning lock held during bulk delete so scans can't collide.

New state files

  • pending_redownloads.json — tracks {albumId: {deletedAt, albumName, monitored}}. Auto-created; pruned as grabs are detected.

Test plan

  • Build the container: docker build -t beatscheck .
  • Start a fresh container with Lidarr configured; run a scan so corrupt.txt has entries across multiple albums.
  • WebUI → Corrupt FilesGroup by Album. Confirm the old "Re-download" button is gone.
  • Per-row Delete still deletes only corrupt files (not whole folder) and logs LIDARR DELETE: ....
  • Per-row Delete Album confirms with total file count, routes through Lidarr API, and triggers Lidarr's auto-search for monitored albums.
  • Select 5 then click Delete Albums (Whole) — progress modal shows 1/5 → 5/5 with ~30s between albums; current folder label updates; Cancel mid-flight leaves the remaining albums untouched.
  • Bulk delete rejected with 409 if a scan is active (.scanning lock exists).
  • Bulk delete rejected with 400 if >50 folders posted to /api/delete-albums.
  • After a Lidarr delete, confirm pending_redownloads.json gets an entry. On the next scan cycle, confirm beats_check.log gets a LIDARR REDOWNLOAD: ... — grabbed (1 report) line once Lidarr grabs a replacement.
  • Confirm path-traversal / symlink / mount-root guards all reject with errors in the response (no actual delete) — hit /api/delete-albums with a folder outside MUSIC_DIR, a symlinked folder, and MUSIC_DIR itself.
  • flake8 . --max-complexity=25 --max-line-length=127 clean.

🤖 Generated with Claude Code

Adds a whole-folder delete path to the WebUI with async job tracking so
users can purge up to 50 corrupt albums in one pass without the browser
hanging. Deletes are staggered album-by-album (30s between each) to
avoid flooding Lidarr and indexers, matching the existing auto-delete
pipeline.

Also adds a pending-redownload tracker: after any Lidarr-backed delete,
BeatsCheck records the affected album IDs and checks Lidarr history at
the start of the next scan cycle. Successful grabs are logged to
beats_check.log so users can confirm re-downloads landed without
tailing Lidarr's own logs. Stale entries time out after 24h.

WebUI changes:
- Per-album row: new "Delete Album" button (whole folder); dropped
  the redundant "Re-download" button (it was identical to Delete
  under the hood since Lidarr auto-searches monitored albums after
  any trackfile deletion).
- Bulk "Delete Albums (Whole)" button with a 50-album cap.
- "Select N" helper (number input + button) to fill the first N
  visible album checkboxes, respecting the search filter.
- Progress modal polls /api/delete-job-status every 2s, shows
  current album + phase (running / waiting / done / cancelled),
  and supports mid-flight cancel.

Backend changes:
- main.py: delete_album_folders() with mount-root, MUSIC_DIR,
  and symlink guards; factored _delete_one_folder, _stagger_sleep,
  _validate_delete_folders, _finalize_delete_state helpers to keep
  cyclomatic complexity under the project cap.
- main.py: _record_pending_redownloads / _poll_pending_redownloads
  hooked into the scan-loop start; integrated with delete_corrupt_files
  and run_auto_delete so every Lidarr delete path records grabs.
- webui.py: POST /api/delete-albums (202 + job_id, fire-and-forget),
  GET /api/delete-job-status, POST /api/delete-job-cancel; in-memory
  job store with 1h auto-prune; .scanning lock held for the duration
  of a bulk delete so scans can't collide.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chodeus chodeus force-pushed the feat/bulk-album-delete branch from 2b0a215 to 7bbfa29 Compare April 19, 2026 04:40
@chodeus chodeus merged commit 2e0c79a into main Apr 19, 2026
5 checks passed
@chodeus chodeus deleted the feat/bulk-album-delete branch April 19, 2026 04:42
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