Skip to content

v0.13.5

Choose a tag to compare

@github-actions github-actions released this 23 Oct 03:02
· 112 commits to main since this release
ebfaa46

Performance

  • The folder-cleanup DB-reference scan no longer resolves realpath(MUSIC_ROOT) once per Beets item. composite_workflows.norm_library_path runs once per item during a full-library scan that repeats on every rename; on a ~5,000-folder live run that was a realpath syscall storm that could make the run crawl or look hung. The root's realpath is now resolved once per distinct MUSIC_ROOT string (functools.lru_cache) and reused. The normalization result is byte-for-byte identical; only the trusted root is realpath-resolved, still never a caller path.

Fixed

  • A tracked album folder whose Beets DB path is a DANGLING symlink is now HELD (needs_db_resync), not relocated into a loud partial move. _album_db_paths_stale used os.path.lexists, which reports a dangling symlink (link present, target gone) as present, so the relocation ran and Beets skipped every track (a rollback-able partial_move). It now uses os.path.exists, so a DB path with no real file behind it is treated as stale and held. Regression test: tests/test_folder_mbid_names_apply.py::TrackedRelocationTests::test_dangling_symlink_db_path_held_needs_db_resync.

  • Renaming a Beets-TRACKED album folder to Album (Year) {release-group-mbid} no longer fails with PATH_IS_TRACKED when the DB and disk paths are desynced. The folder-MBID apply (_apply_folder_mbid_names) decided tracked-vs-disk-only from its own item-count map (_placeholder_folder_db), which keyed item paths with a raw os.path.dirname — no realpath rewrite and no relative-path resolution. On a symlinked/realpath-diverging MUSIC_ROOT (and after an earlier placeholder-strip left the DB naming the pre-rename path) that map reported db_item_count=0 for a folder the engine still considered tracked, so the apply picked the disk-only folder_rename and the engine's descendant-aware, realpath-aware DB-reference refusal (_library_refs_under) rejected it (folder_cleanup_rename_failed / PATH_IS_TRACKED). Both detectors now normalize through one shared composite_workflows.norm_library_path, so a folder the engine treats as tracked is classified tracked by the apply and routed to the DB-aware relocation (relocate_album / Beets Album.move) — folder and DB item/album paths move together. When Beets names paths that are no longer on disk (the strip-desync), the album is HELD (needs_db_resync) rather than relocated off stale paths, so no file is lost and DB and disk never diverge. The existing holds (tracked_rgid_mismatch, tracked_multiple_albums, target_exists), disk-only renames, idempotent already_correct, dry-run, the confirmed gate, batch_limit, resume and rollback-by-job are unchanged. Regression tests: tests/test_folder_mbid_names_apply.py::TrackedRelocationTests (realpath-desynced tracked relocation; stale-DB needs_db_resync hold).