Skip to content

Commit

Permalink
Don’t unnecessarily update files on file systems with lower mtime res…
Browse files Browse the repository at this point in the history
…olutions

Fixes #74
  • Loading branch information
geigerzaehler committed Dec 14, 2023
1 parent 2c0f891 commit 647e6d3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
6 changes: 6 additions & 0 deletions beetsplug/alternatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ def item_change_actions(self, item, path, dest):
actions.append(Action.MOVE)

item_mtime_alt = os.path.getmtime(syspath(path))
if item_mtime_alt % 1 == 0:
# On Some filesystems (e.g. FAT32) the modification time resolution is
# a second. This means external files can look one second older than
# they actually are, which leads to unnecessary writes.
item_mtime_alt += 1

if item_mtime_alt < os.path.getmtime(syspath(item.path)):
actions.append(Action.WRITE)
album = item.get_album()
Expand Down
22 changes: 21 additions & 1 deletion test/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def test_update_older(self):
mediafile = MediaFile(syspath(self.get_path(item)))
self.assertEqual(mediafile.composer, "JSB")

def test_no_udpdate_newer(self):
def test_no_update(self):
item = self.add_external_track("myexternal")
item["composer"] = "JSB"
item.store()
Expand All @@ -284,6 +284,26 @@ def test_no_udpdate_newer(self):
mediafile = MediaFile(syspath(self.get_path(item)))
self.assertNotEqual(mediafile.composer, "JSB")

def test_no_update_mtime_second_resolution(self):
"""Assert that we don’t update the external file if the file system it is
on has a modification time resolution of only a second."""
item = self.add_external_track("myexternal")

mtime = 10000.5
os.utime(item.path, (os.path.getatime(item.path), mtime))
# Set modification time of external path to the integral part of the
# actual modification time. This makes the file appear older than it
# actually is.
os.utime(self.get_path(item), (os.path.getatime(item.path), int(mtime)))

item["composer"] = "JSB"
item.store()

self.runcli("alt", "update", "myexternal")
item.load()
mediafile = MediaFile(syspath(self.get_path(item)))
self.assertNotEqual(mediafile.composer, "JSB")

def test_move_after_path_format_update(self):
item = self.add_external_track("myexternal")
old_path = self.get_path(item)
Expand Down
6 changes: 4 additions & 2 deletions test/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,10 @@ def add_external_album(self, ext_name, **kwargs):
album.load()
return album

def get_path(self, item, path_key="alt.myexternal") -> Optional[bytes]:
return alternatives.External._get_path(item, path_key)
def get_path(self, item, path_key="alt.myexternal") -> bytes:
path = alternatives.External._get_path(item, path_key)
assert path is not None
return path


class MockedWorker(alternatives.Worker):
Expand Down

0 comments on commit 647e6d3

Please sign in to comment.