diff --git a/beets/importer.py b/beets/importer.py index d2943b511..4c4316a8e 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -772,7 +772,7 @@ class ImportTask(BaseImportTask): if (not dup_item.album_id or dup_item.album_id in replaced_album_ids): continue - replaced_album = dup_item.get_album() + replaced_album = dup_item._cached_album if replaced_album: replaced_album_ids.add(dup_item.album_id) self.replaced_albums[replaced_album.path] = replaced_album diff --git a/beets/library.py b/beets/library.py index ba822c513..87bbcd134 100644 --- a/beets/library.py +++ b/beets/library.py @@ -395,9 +395,9 @@ class FormattedItemMapping(dbcore.db.FormattedMapping): album_keys.append(key) return album_keys - @lazy_property + @property def album(self): - return self.item.get_album() + return self.item._cached_album def _get(self, key): """Get the value for a key, either from the album or the item. @@ -542,6 +542,29 @@ class Item(LibModel): _format_config_key = 'format_item' + __album = None + """Cached album object. Read-only.""" + + @property + def _cached_album(self): + """The Album object that this item belongs to, if any, or + None if the item is a singleton or is not associated with a + library. + The instance is cached and refreshed on access. + + DO NOT MODIFY! + If you want a copy to modify, use :meth:`get_album`. + """ + if not self.__album and self._db: + self.__album = self._db.get_album(self) + elif self.__album: + self.__album.load() + return self.__album + + @_cached_album.setter + def _cached_album(self, album): + self.__album = album + @classmethod def _getters(cls): getters = plugins.item_field_getters() @@ -568,6 +591,8 @@ class Item(LibModel): value = bytestring_path(value) elif isinstance(value, BLOB_TYPE): value = bytes(value) + elif key == 'album_id': + self._cached_album = None changed = super(Item, self)._setitem(key, value) @@ -581,9 +606,8 @@ class Item(LibModel): try: return super(Item, self).__getitem__(key) except KeyError: - album = self.get_album() - if album: - return album[key] + if self._cached_album: + return self._cached_album[key] raise def keys(self, computed=False, with_album=True): @@ -591,10 +615,8 @@ class Item(LibModel): controls whether the album's fields are included. """ keys = super(Item, self).keys(computed=computed) - if with_album: - album = self.get_album() - if album: - keys += album.keys(computed=computed) + if with_album and self._cached_album: + keys += self._cached_album.keys(computed=computed) return keys def get(self, key, default=None, with_album=True): @@ -604,9 +626,8 @@ class Item(LibModel): try: return self._get(key, default, raise_=with_album) except KeyError: - album = self.get_album() - if album: - return album.get(key, default) + if self._cached_album: + return self._cached_album.get(key, default) return default def update(self, values): diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 6ed139da0..c873ec7c1 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -332,7 +332,7 @@ class ConvertPlugin(BeetsPlugin): item.store() # Store new path and audio data. if self.config['embed']: - album = item.get_album() + album = item._cached_album if album and album.artpath: self._log.debug(u'embedding album art from {}', util.displayable_path(album.artpath))