nightly-4d4fc023-ls342
Pre-releaseCI Report:
https://ci-tests.linuxserver.io/linuxserver/beets/nightly-4d4fc023-ls342/index.html
LinuxServer Changes:
Full Changelog: nightly-cef180ea-ls341...nightly-4d4fc023-ls342
Remote Changes:
Add "Rescan directory" import prompt choice (#6945)
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.RESCANenum member (actions.py) andset_choicewiring
(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 aRESCANbranch in the
user_querypipeline stage that re-extends the pipeline with
freshly-discovered tasks, mirroring the existingTRACKS/ALBUMS
branches (stages.py). - Documents the new choice in the auto-tagger guide
(docs/guides/tagger.rst). - Changelog entry.
Design notes
Kept here rather than as long inline comments (per review feedback), so
the code stays readable.
Discovery is delegated, not reimplemented. Every group
rescan_tasks yields comes from ImportTaskFactory.paths() — the same
albums_in_dir / flat-mode / single-file logic the initial scan uses —
so the two can't disagree about what counts as an album. rescan_tasks
only decides where to re-walk and filters the results back down to
this task.
Scope heuristic. Re-walking the whole toppath is correct but
wasteful (rescanning one album in a large library shouldn't re-scan the
rest). When every directory in task.paths lives under task.paths[0]
(a plain album, or a nested multi-disc album whose disc dirs are
children of the album root), that subtree is the whole scope — walk it
directly, no filtering needed. Otherwise (flattened sibling discs with
no wrapping directory) walk one level up from task.paths[0] — the
shared parent — and keep only the groups still related to the original
directories, so an unrelated sibling album picked up by that same walk
is left alone.
Emitted tasks keep the original toppath. Discovery runs through a
factory scoped to the walk root, but the emitted tasks are built by a
second factory scoped to task.toppath. Using the rescanned directory
as toppath caused two regressions (found by @snejus, now covered by
tests): with move=True, pruning stops at toppath, so the emptied
source directory was left behind; with resume=True, progress is keyed
by toppath, so stale progress state was left behind.
The choice is withheld, and re-guarded, for tasks with no directory
scope. _get_choices only offers "Rescan directory" when
task.toppath is set and the task isn't a "Group albums" result (items
grouped by tag, not directory). user_query re-checks not task.is_album or task.toppath is None or task.is_grouped before
entering rescan_tasks, because a plugin can force choice_flag = Action.RESCAN via import_task_choice, bypassing the offered choices;
without the guard a singleton or query-produced task would be wrapped
back into a mismatched album-style task (or hit the assert in
rescan_tasks).
The r shortcut is now reserved by the default album-task prompt,
displacing any plugin-provided choice bound to the same letter. The
singleton-task prompt is unaffected, since Rescan is never offered
there.
Known limitations
The rescan scope is reconstructed from task.paths, which can't fully
recover the original discovery grouping in a few layouts. These are
accepted for now (per this
discussion);
a rescan in these cases either re-prompts for something already handled
or splits an album, but never corrupts the library:
- Flattened sibling discs at different depths (one disc nested a
level deeper than its sibling):task.paths[0]isn't an ancestor of
every disc, so a shallower sibling can be dropped from the rescanned
album. - A loose album sharing a sibling-disc set's parent
(Artist/track.mp3besideArtist/Set Disc 1/+Artist/Set Disc 2/):
rescanning the disc set walksArtistand the ancestor-match filter can
re-offer the already-imported loose album. - A pre-existing plain subdirectory (e.g. a bonus-tracks folder)
inside a rescanned album's directory: the rescan rediscovers it while
its own original task is still pending, so it can be imported twice.
An earlier revision of this PR fixed all three (lowest-common-ancestor
scoping + a rescan-"generation" mechanism to drop superseded tasks) but
was judged too complex/verbose for the benefit; that history is on the
PR if it's wanted later.
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)