diff --git a/beets/library.py b/beets/library.py index 0b2513df5..f676bc49e 100644 --- a/beets/library.py +++ b/beets/library.py @@ -66,7 +66,7 @@ ITEM_FIELDS = [ ('length', 'real', False, True), ('bitrate', 'int', False, True), ('format', 'text', False, True), - ('file_mtime', 'int', False, False), + ('mtime', 'int', False, False), ] ITEM_KEYS_WRITABLE = [f[0] for f in ITEM_FIELDS if f[3] and f[2]] ITEM_KEYS_META = [f[0] for f in ITEM_FIELDS if f[3]] @@ -200,6 +200,10 @@ class Item(object): for key in ITEM_KEYS_META: setattr(self, key, getattr(f, key)) self.path = read_path + + # Database's mtime should now reflect the on-disk value. + if read_path == self.path: + self.mtime = self.current_mtime() def write(self): """Writes the item's metadata to the associated file. @@ -208,9 +212,9 @@ class Item(object): for key in ITEM_KEYS_WRITABLE: setattr(f, key, getattr(self, key)) f.save() - - # Set file modified time, we now know when beets last changed this file - setattr(self, 'file_mtime', os.path.getmtime(syspath(self.path))) + + # The file has a new mtime. + self.mtime = self.current_mtime() # Files themselves. @@ -226,9 +230,15 @@ class Item(object): # Either copying or moving succeeded, so update the stored path. self.path = dest - - # Update file modified time in the library - setattr(self, 'file_mtime', os.path.getmtime(syspath(self.path))) + + # Update mtime to reflect the new file. + self.mtime = self.current_mtime() + + def current_mtime(self): + """Returns the current mtime of the file, rounded to the nearest + integer. + """ + return int(os.path.getmtime(syspath(self.path))) # Library queries. diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 9648af526..fdbd5a377 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -725,9 +725,11 @@ def update_items(lib, query, album, move, color, pretend): continue # Did the item change since last checked? - if os.path.getmtime(syspath(item.path)) == getattr(item, 'file_mtime'): + if item.current_mtime() <= item.mtime: + log.debug(u'skipping %s because mtime is up to date (%i)' % + (item.path, item.mtime)) continue - + # Read new data. old_data = dict(item.record) item.read() @@ -761,12 +763,13 @@ def update_items(lib, query, album, move, color, pretend): lib.store(item) affected_albums.add(item.album_id) - else: - if not pretend: - # file_mtime is different, but no changes to the metadata. - # store the new mtime so we don't check this again in the future. - setattr(item, 'file_mtime', os.path.getmtime(syspath(item.path))) - + elif not pretend: + # The file's mtime was different, but there were no changes + # to the metadata. Store the new mtime, which is set in the + # call to read(), so we don't check this again in the + # future. + lib.store(item) + # Skip album changes while pretending. if pretend: return diff --git a/docs/changelog.rst b/docs/changelog.rst index c8d4ab578..f019afe4f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -26,6 +26,8 @@ Changelog * The importer now ignores certain "clutter" files like ``.AppleDouble`` directories and ``._*`` files. The list of ignored patterns is configurable via the ``ignore`` setting; see :doc:`/reference/config`. +* The database now keeps track of files' modification times so that, during + an ``update``, unmodified files can be skipped. (Thanks to Jos van der Til.) * Fix a crash after using the "as Tracks" option during import. * Fix a Unicode error when tagging items with missing titles.