mirror of
https://github.com/beetbox/beets.git
synced 2026-08-21 16:46:18 -04:00
Fix unique_path counter for names ending in two or more digits (#6875)
## Problem
`unique_path` parses a trailing counter so it can continue from it, but
the pattern is `\.(\d)+$` — a single-digit group repeated, rather than a
multi-digit group. `group(1)` is therefore only the final digit, and the
counter restarts from it:
```python
>>> # with only track.10.mp3 present
>>> unique_path("track.10.mp3")
'track.1.mp3' # expected track.11.mp3
```
```
base re.search(rb"\.(\d)+$", base).group(1)
song.9 -> b'9' -> 9 ✓
song.10 -> b'0' -> 0 ✗
song.12 -> b'2' -> 2 ✗
song.123 -> b'3' -> 3 ✗
```
The returned path still does not exist, so nothing is overwritten — but
the new name sorts *before* the file it was derived from, and when the
low numbers are already taken the loop rescans them from scratch instead
of continuing past the existing counter. `unique_path` is used for art
and item destinations in `beets/library/models.py`.
Existing coverage only exercised a single-digit counter (`x.1.mp3`),
which is why this held.
## Fix
`\.(\d)+$` → `\.(\d+)$`.
Changelog entry added under *Bug fixes*. I did not open an issue first,
so there is no `🐛` reference — happy to file one and add the
reference if you prefer that.
This commit is contained in:
@@ -644,7 +644,7 @@ def unique_path(path: AnyStr) -> AnyStr:
|
||||
|
||||
byte_path = os.fsencode(path)
|
||||
base, ext = os.path.splitext(byte_path)
|
||||
match = re.search(rb"\.(\d)+$", base)
|
||||
match = re.search(rb"\.(\d+)$", base)
|
||||
if match:
|
||||
num = int(match.group(1))
|
||||
base = base[: match.start()]
|
||||
|
||||
@@ -19,6 +19,10 @@ Bug fixes
|
||||
- A date range query written back to front (for example ``added:2024..2020``) no
|
||||
longer crashes with an uncaught ``ValueError``. The endpoints are now swapped,
|
||||
so such a range means the same as ``added:2020..2024``.
|
||||
- Deduplicating a file whose name already ends in a counter of two or more
|
||||
digits no longer restarts the numbering: ``track.10.mp3`` now yields
|
||||
``track.11.mp3`` instead of ``track.1.mp3``. The counter was matched with
|
||||
``\.(\d)+$``, which captures only the final digit.
|
||||
|
||||
..
|
||||
For plugin developers
|
||||
|
||||
@@ -428,6 +428,11 @@ class UniquePathTest(BeetsTestCase):
|
||||
path = util.unique_path(self.base / "x.1.mp3")
|
||||
assert path == str(self.base / "x.3.mp3")
|
||||
|
||||
def test_conflicting_file_with_multi_digit_number_increases_number(self):
|
||||
(self.base / "w.10.mp3").touch()
|
||||
path = util.unique_path(self.base / "w.10.mp3")
|
||||
assert path == str(self.base / "w.11.mp3")
|
||||
|
||||
|
||||
class MkDirAllTest(BeetsTestCase):
|
||||
def test_mkdirall(self):
|
||||
|
||||
Reference in New Issue
Block a user