Running this command in verbose (-vv) mode:
C:\apps\ytdl
λ beet -vv mv -a projections
user configuration: C:\Users\RollingStar\AppData\Roaming\beets\config.yaml
data directory: C:\Users\RollingStar\AppData\Roaming\beets
plugin paths:
Sending event: pluginload
inline: adding item field i_cust_catalog
inline: adding item field i_main
inline: adding album field alb_main
inline: adding album field alb_main_suffix
library database: e:\Music\beetslibrary.bib
library directory: e:\Music
Sending event: library_opened
Moving 0 albums (1 already in place).
Sending event: cli_exit
# same command - but changed config to change the CAPITALIZATION in the path
C:\apps\ytdl
λ beet -vv mv -a projections
user configuration: C:\Users\RollingStar\AppData\Roaming\beets\config.yaml
data directory: C:\Users\RollingStar\AppData\Roaming\beets
plugin paths:
Sending event: pluginload
inline: adding item field i_cust_catalog
inline: adding item field i_main
inline: adding album field alb_main
inline: adding album field alb_main_suffix
library database: e:\Music\beetslibrary.bib
library directory: e:\Music
Sending event: library_opened
Moving 1 album.
moving: e:\Music\Wolfgun - 2013 - PROJECTIONS
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
moving album art e:\Music\Wolfgun - 2013 - PROJECTIONS\front.1.jpg to e:\Music\Wolfgun - 2013 - Projections\front.jpg
Sending event: database_change
Sending event: cli_exit
Led to this problem:
Beets correctly identifies when a path changes case (c:\path -> c:\PATH). But the move command doesn't move the files, because (?) Windows paths are case-insensitive.
Relevant code:
https://github.com/beetbox/beets/blob/b659ad6b0c7e7be35f6d39df09b740b4ed69f5f5/beets/library.py#L796
The os.rename(path, dest) call may be the culprit.
Potential workaround (code): move the files to a temp folder, delete the wrongly-capitalized path once it's empty, and move them to a new folder with the right caps?
Workaround (user): Change the path each time you want to change caps. Add a useless character and remove it if you need to.
That's too bad! I'm not exactly sure what the solution is, but seems like there's something to be done here.
Potential workaround (code): move the files to a temp folder, delete the wrongly-capitalized path once it's empty, and move them to a new folder with the right caps?
I guess if that turns out to be the right method, it should rather rename twice within the original folder (i.e. old name -> some temporary, randomized filename -> old name with new capitalization). Otherwise, you'll easily end up with cross-filesystem moves. Moving to a new folder also has a greater potential of data loss if something goes wrong halfway.
Don't forget that the folder can change too. C:\music\album\file -> c:\music\ALBUM\file.
Fair enough: So the first move should probably go to the new folder (which might be the same as the old). If that already entails a cross-filesystem move, that would have been necessary anyway.
To make matters somewhat more complicated, it's not entirely clear what should happen when the directory case differs and there are other files (not necessarily tracked by beets) in those directories. So if you have:
/foo/bar/baz/one.mp3
/foo/bar/baz/two.mp3
And we need to move /foo/bar/baz/two.mp3 to /FOO/bar/BAZ/TWO.MP3, should we really rename the _entire_ directory path, also affecting one.mp3 (and anything else under /foo)? Doing so would risk creating an infinite loop if the two files have conflicting beliefs about what the case of the path should be, e.g., if one.mp3 later needs to move to the lower-case path…
I'd be fine with only doing the folder rename if it was entirely affected by a beets move. Feels more predictable that way. Isn't the design of beets that the user has a library at /foo/ and isn't it implied that it will be mostly/entirely beets stuff within that folder?
Yeah, it certainly would mostly be, but we don't guarantee that. And indeed, if we did change directory names, we'd probably want to split the path so it only happens under the beets directory and not above. Anyway, all of these aspects are surprisingly subtle!
Most helpful comment
I guess if that turns out to be the right method, it should rather rename twice within the original folder (i.e. old name -> some temporary, randomized filename -> old name with new capitalization). Otherwise, you'll easily end up with cross-filesystem moves. Moving to a new folder also has a greater potential of data loss if something goes wrong halfway.