Skip to content

Add "Rescan directory" import prompt choice - #6945

Open
barrettclark wants to merge 5 commits into
beetbox:masterfrom
barrettclark:import-rescan-directory
Open

Add "Rescan directory" import prompt choice#6945
barrettclark wants to merge 5 commits into
beetbox:masterfrom
barrettclark:import-rescan-directory

Conversation

@barrettclark

@barrettclark barrettclark commented Aug 20, 2026

Copy link
Copy Markdown

Description

Fixes #166

When beets can't confidently match an album, the interactive import prompt now offers a "Rescan directory" option alongside Skip/Use as-is/etc. It re-reads the task's directories from disk and re-runs the match, so files can be cleaned up (duplicates, junk) mid-import without restarting the whole beet import run.

  • New Action.RESCAN enum member (actions.py) and set_choice wiring (tasks.py).
  • New "r" PromptChoice, shown for album tasks with a toppath, added to both the "no candidates" and "pick a candidate" prompt variants since they share the same choice list (session.py).
  • New rescan_tasks() generator and a RESCAN branch in the user_query pipeline stage that re-extends the pipeline with freshly-discovered tasks, mirroring the existing TRACKS/ALBUMS branches (stages.py).
  • Tests covering added/removed/all files between the initial scan and the rescan (test_importer.py).
  • Documents the new choice in the auto-tagger guide (docs/guides/tagger.rst).
  • Changelog entry.

Also syncs uv.lock's recorded package version (2.13.0 -> 2.13.1) to match pyproject.toml; it was drifting and getting silently regenerated by every uv/poe invocation, which was tripping up pre-commit's file-modification detection.

To Do

  • Documentation.
  • Changelog.
  • Tests.

Example

Don McLean has multiple albums named "The Best of Don McLean". In this case 2 of them were smashed together in the same directory.

/volume2/ConsolidatedMusic/Don McLean/The Best of Don McLean (29 items)
No matching release found for 29 tracks.
For help, see: https://beets.readthedocs.org/en/latest/faq.html#nomatch
➜ [S]kip, Use as-is, as Tracks, Group albums, Rescan directory,
Enter search, enter Id, aBort? r

/volume2/ConsolidatedMusic/Don McLean/The Best of Don McLean (20 items)

  Match (93.1%):
  Don McLean - The Best of Don McLean
  ≠ country, tracks, data source
  MusicBrainz, CD, 1991, GB, EMI International, CDP 7983602, None
  https://musicbrainz.org/release/4e620700-089d-4dbe-bd75-f3e0ab65fae9
  * Artist: Don McLean
  * Album: The Best of Don McLean
     * (#1) American Pie (8:32)
     * (#2) Castles in the Air (1981 version) (3:41)
     * (#3) Dreidel (3:46)
     * (#4) Winterwood (3:11)
     * (#5) Everyday (2:26)
     * (#6) Sister Fatima (2:33)
     * (#7) Empty Chairs (3:26)
     * (#8) The Birthday Song (2:37)
     * (#9) Wonderful Baby (2:04)
     * (#10) La La I Love You (3:46)
     * (#11) Vincent (4:01)
     * (#12) Crossroads (3:39)
     * (#13) And I Love You So (4:16)
     * (#14) Fools Paradise (4:05)
     * (#15) If We Try (3:34)
     * (#16) Mountains of Mourne (4:29)
     * (#17) The Grave (3:12)
     * (#18) Respectable (2:28)
     * (#19) Going for the Gold (2:43)
     * (#20) Crying (3:40)

When beets can't confidently match an album, the interactive import
prompt now offers a "Rescan directory" option alongside Skip/Use
as-is/etc. It re-reads the task's directories from disk and re-runs
the match, so files can be cleaned up (duplicates, junk) mid-import
without restarting the whole `beet import` run.

- New Action.RESCAN enum member (actions.py) and set_choice wiring
  (tasks.py).
- New "r" PromptChoice, shown for album tasks with a toppath, added to
  both the "no candidates" and "pick a candidate" prompt variants
  since they share the same choice list (session.py).
- New rescan_tasks() generator and a RESCAN branch in the user_query
  pipeline stage that re-extends the pipeline with freshly-discovered
  tasks, mirroring the existing TRACKS/ALBUMS branches (stages.py).
- Tests covering added/removed/all files between the initial scan and
  the rescan (test_importer.py).
- Documents the new choice in the auto-tagger guide
  (docs/guides/tagger.rst).
- Changelog entry.

Also syncs uv.lock's recorded package version (2.13.0 -> 2.13.1) to
match pyproject.toml; it was drifting and getting silently
regenerated by every uv/poe invocation, which was tripping up
pre-commit's file-modification detection.
@barrettclark
barrettclark requested a review from a team as a code owner August 20, 2026 01:02
- rescan_tasks(): assert task.toppath is not None before passing to
  ImportTaskFactory (it's only reachable when the "r" choice was
  offered, which already requires a truthy toppath), and use distinct
  variable names for the singleton/album branches so mypy doesn't
  unify them into an incompatible union type.
- test_plugins.py: the new built-in "r" (Rescan directory) choice
  collided with a test plugin's example "r" choice, which the
  short-letter conflict resolution silently drops in favor of
  built-ins. Moved the test plugin's choice to "z" and added "Rescan
  directory" to the expected option tuples across the four
  TestPromptChoices cases that exercise the album prompt.
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.17%. Comparing base (07f39c5) to head (790a11b).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6945      +/-   ##
==========================================
- Coverage   76.29%   76.17%   -0.13%     
==========================================
  Files         164      164              
  Lines       21542    21559      +17     
  Branches     3334     3340       +6     
==========================================
- Hits        16436    16422      -14     
- Misses       4316     4353      +37     
+ Partials      790      784       -6     
Files with missing lines Coverage Δ
beets/importer/actions.py 96.96% <100.00%> (+0.09%) ⬆️
beets/importer/stages.py 93.92% <100.00%> (+0.50%) ⬆️
beets/importer/tasks.py 91.18% <ø> (+0.15%) ⬆️
beets/ui/commands/import_/session.py 67.59% <100.00%> (+0.30%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- rescan_tasks(): remove the singleton-mode branch. It was dead code
  -- the "Rescan directory" choice is only ever offered on album
  tasks, and album tasks only exist when the session isn't in
  singleton mode, so the branch could never actually run.
- test_importer.py: the existing rescan tests mutated the album
  directory *before* calling importer.run(), so the initial scan
  (not just the rescan) already saw the mutated state -- the tests
  passed, but didn't actually exercise re-reading a directory that
  changed mid-import. Added
  ImportRescanTest._run_with_cleanup_before_first_prompt(), which
  performs the filesystem edit exactly when the first prompt is
  answered (via a choose_match side_effect), and switched all
  existing rescan tests to use it.
- Added two more tests: rescanning a directory left with only an
  unreadable file (covers factory.album() returning None), and
  rescanning after the user splits one messy directory into two
  proper album subdirectories.
@henry-oberholtzer

Copy link
Copy Markdown
Member

Hi! Thanks for the PR! Is there an active issue / feature request this is related to?

@barrettclark

barrettclark commented Aug 20, 2026

Copy link
Copy Markdown
Author

Hi! Thanks for the PR! Is there an active issue / feature request this is related to?

Hey @henry-oberholtzer! I didn't find one at first, but I looked again and found what looks like an old issue: #166 (google-code-export/beets#384). I updated the PR description to reflect this.

This may be a niche situation, but it's been plaguing me lately. It's been super helpful for me, and hopefully others will find it helpful as well. I also added an example in the PR description to show it in use.

@amogus07

Copy link
Copy Markdown
Contributor

Possibly #6527 could be related

@barrettclark

Copy link
Copy Markdown
Author

@henry-oberholtzer is there anything else needed for this PR or is it good to be merged?

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.

Rescan current album directory during import

3 participants