diff --git a/beets/library.py b/beets/library.py index 91034df90..1b08cb571 100644 --- a/beets/library.py +++ b/beets/library.py @@ -281,11 +281,12 @@ class FlexModel(object): def __setitem__(self, key, value): """Assign the value for a field. """ - if key in self._fields: - self._values_fixed[key] = value - else: - self._values_flex[key] = value - self._dirty.add(key) + source = self._values_fixed if key in self._fields \ + else self._values_flex + old_value = source.get(key) + source[key] = value + if old_value != value: + self._dirty.add(key) def update(self, values): """Assign all values in the given dict. @@ -344,6 +345,11 @@ class LibModel(FlexModel): """The flex field SQLite table name. """ + _bytes_keys = ('path', 'artpath') + """Keys whose values should be stored as raw bytes blobs rather than + strings. + """ + def __init__(self, lib=None, **values): self._lib = lib super(LibModel, self).__init__(**values) @@ -355,7 +361,7 @@ class LibModel(FlexModel): """ if not self._lib: raise ValueError('{0} has no library'.format(type(self).__name__)) - if not self._id: + if not self.id: raise ValueError('{0} has no id'.format(type(self).__name__)) def store(self): @@ -372,7 +378,7 @@ class LibModel(FlexModel): value = self[key] # Wrap path strings in buffers so they get stored # "in the raw". - if key == 'path' and isinstance(value, str): + if key in self._bytes_keys and isinstance(value, str): value = buffer(value) subvars.append(value) assignments = assignments[:-1] # Knock off last , @@ -1727,7 +1733,7 @@ class Album(LibModel): self._lib.move(item, copy, basedir=basedir, with_album=False) # Move art. - self.move_art(self._lib, copy) + self.move_art(copy) def item_dir(self): """Returns the directory containing the album's first item, diff --git a/test/test_db.py b/test/test_db.py index d01798c42..744933049 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -533,21 +533,21 @@ class DisambiguationTest(unittest.TestCase, PathFormattingMixin): def test_unique_with_default_arguments_uses_albumtype(self): album2 = self.lib.get_album(self.i1) album2.albumtype = 'bar' - self.lib._connection().commit() + album2.store() self._setf(u'foo%aunique{}/$title') self._assert_dest('/base/foo [bar]/the title', self.i1) def test_unique_expands_to_nothing_for_distinct_albums(self): album2 = self.lib.get_album(self.i2) album2.album = 'different album' - self.lib._connection().commit() + album2.store() self._assert_dest('/base/foo/the title', self.i1) def test_use_fallback_numbers_when_identical(self): album2 = self.lib.get_album(self.i2) album2.year = 2001 - self.lib._connection().commit() + album2.store() self._assert_dest('/base/foo 1/the title', self.i1) self._assert_dest('/base/foo 2/the title', self.i2) @@ -561,6 +561,8 @@ class DisambiguationTest(unittest.TestCase, PathFormattingMixin): album2.year = 2001 album1 = self.lib.get_album(self.i1) album1.albumtype = 'foo/bar' + album2.store() + album1.store() self._setf(u'foo%aunique{albumartist album,albumtype}/$title') self._assert_dest('/base/foo [foo_bar]/the title', self.i1) @@ -757,6 +759,7 @@ class AlbumInfoTest(unittest.TestCase): def test_albuminfo_stores_art(self): ai = self.lib.get_album(self.i) ai.artpath = '/my/great/art' + ai.store() new_ai = self.lib.get_album(self.i) self.assertEqual(new_ai.artpath, '/my/great/art') @@ -906,9 +909,10 @@ class PathStringTest(_common.TestCase): self.assert_(isinstance(dest, str)) def test_artpath_stores_special_chars(self): - path = 'b\xe1r' + path = b'b\xe1r' alb = self.lib.add_album([self.i]) alb.artpath = path + alb.store() alb = self.lib.get_album(self.i) self.assertEqual(path, alb.artpath)