mirror of
https://github.com/beetbox/beets.git
synced 2025-12-28 03:22:39 +01:00
remove Library.{move,store} methods
These methods are now provided by LibModel, which makes dealing with items and albums symmetric.
This commit is contained in:
parent
15cf046285
commit
c7fe017752
16 changed files with 53 additions and 94 deletions
|
|
@ -811,7 +811,7 @@ def plugin_stage(session, func):
|
|||
|
||||
# Stage may modify DB, so re-load cached item data.
|
||||
for item in task.imported_items():
|
||||
session.lib.load(item)
|
||||
item.load()
|
||||
|
||||
def manipulate_files(session):
|
||||
"""A coroutine (pipeline stage) that performs necessary file
|
||||
|
|
@ -858,7 +858,7 @@ def manipulate_files(session):
|
|||
# Save new paths.
|
||||
with session.lib.transaction():
|
||||
for item in items:
|
||||
session.lib.store(item)
|
||||
item.store()
|
||||
|
||||
# Plugin event.
|
||||
plugins.send('import_task_files', session=session, task=task)
|
||||
|
|
|
|||
|
|
@ -1405,6 +1405,8 @@ class Library(object):
|
|||
item.added = time.time()
|
||||
if copy:
|
||||
self.move(item, copy=True)
|
||||
if not item._lib:
|
||||
item._lib = self
|
||||
|
||||
# Build essential parts of query.
|
||||
columns = ','.join([key for key in ITEM_KEYS if key != 'id'])
|
||||
|
|
@ -1438,49 +1440,6 @@ class Library(object):
|
|||
self._memotable = {}
|
||||
return new_id
|
||||
|
||||
def load(self, item):
|
||||
"""Refresh the item's metadata from the library database.
|
||||
"""
|
||||
if item.id is None:
|
||||
raise ValueError('cannot load item with no id')
|
||||
stored_item = self.get_item(item.id)
|
||||
item.update(dict(stored_item))
|
||||
item.clear_dirty()
|
||||
|
||||
def store(self, item):
|
||||
"""Save the item's metadata into the library database.
|
||||
"""
|
||||
# Build assignments for query.
|
||||
assignments = ''
|
||||
subvars = []
|
||||
for key in ITEM_KEYS:
|
||||
if key != 'id' and key in item._dirty:
|
||||
assignments += key + '=?,'
|
||||
value = getattr(item, key)
|
||||
# Wrap path strings in buffers so they get stored
|
||||
# "in the raw".
|
||||
if key == 'path' and isinstance(value, str):
|
||||
value = buffer(value)
|
||||
subvars.append(value)
|
||||
assignments = assignments[:-1] # Knock off last ,
|
||||
|
||||
with self.transaction() as tx:
|
||||
# Main table update.
|
||||
if assignments:
|
||||
query = 'UPDATE items SET ' + assignments + ' WHERE id=?'
|
||||
subvars.append(item.id)
|
||||
tx.mutate(query, subvars)
|
||||
|
||||
# Flexible attributes.
|
||||
flexins = 'INSERT INTO item_attributes ' \
|
||||
' (entity_id, key, value)' \
|
||||
' VALUES (?, ?, ?)'
|
||||
for key, value in item._values_flex.items():
|
||||
tx.mutate(flexins, (item.id, key, value))
|
||||
|
||||
item.clear_dirty()
|
||||
self._memotable = {}
|
||||
|
||||
def remove(self, item, delete=False, with_album=True):
|
||||
"""Removes this item. If delete, then the associated file is
|
||||
removed from disk. If with_album, then the item's album (if any)
|
||||
|
|
@ -1531,7 +1490,7 @@ class Library(object):
|
|||
old_path = item.path
|
||||
item.move(dest, copy)
|
||||
if item.id is not None:
|
||||
self.store(item)
|
||||
item.store()
|
||||
|
||||
# If this item is in an album, move its art.
|
||||
if with_album:
|
||||
|
|
@ -1648,7 +1607,7 @@ class Library(object):
|
|||
if item.id is None:
|
||||
self.add(item)
|
||||
else:
|
||||
self.store(item)
|
||||
item.store()
|
||||
|
||||
# Construct the new Album object.
|
||||
album_values['id'] = album_id
|
||||
|
|
|
|||
|
|
@ -941,14 +941,14 @@ def update_items(lib, query, album, move, pretend):
|
|||
if move and lib.directory in ancestry(item.path):
|
||||
lib.move(item)
|
||||
|
||||
lib.store(item)
|
||||
item.store()
|
||||
affected_albums.add(item.album_id)
|
||||
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)
|
||||
item.store()
|
||||
|
||||
# Skip album changes while pretending.
|
||||
if pretend:
|
||||
|
|
@ -1163,10 +1163,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
|
|||
else:
|
||||
lib.move(obj)
|
||||
|
||||
if album:
|
||||
obj.store()
|
||||
else:
|
||||
lib.store(obj)
|
||||
obj.store()
|
||||
|
||||
# Apply tags if requested.
|
||||
if write:
|
||||
|
|
@ -1223,7 +1220,7 @@ def move_items(lib, dest, query, copy, album):
|
|||
obj.move(copy, basedir=dest)
|
||||
else:
|
||||
lib.move(obj, copy, basedir=dest)
|
||||
lib.store(obj)
|
||||
obj.store()
|
||||
|
||||
move_cmd = ui.Subcommand('move',
|
||||
help='move or copy items', aliases=('mv',))
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
|
|||
help='generate fingerprints for items without them')
|
||||
def fingerprint_cmd_func(lib, opts, args):
|
||||
for item in lib.items(ui.decargs(args)):
|
||||
fingerprint_item(item, lib=lib,
|
||||
fingerprint_item(item,
|
||||
write=config['import']['write'].get(bool))
|
||||
fingerprint_cmd.func = fingerprint_cmd_func
|
||||
|
||||
|
|
@ -237,12 +237,12 @@ def submit_items(userkey, items, chunksize=64):
|
|||
submit_chunk()
|
||||
|
||||
|
||||
def fingerprint_item(item, lib=None, write=False):
|
||||
def fingerprint_item(item, write=False):
|
||||
"""Get the fingerprint for an Item. If the item already has a
|
||||
fingerprint, it is not regenerated. If fingerprint generation fails,
|
||||
return None. If `lib` is provided, then new fingerprints are saved
|
||||
to the database. If `write` is set, then the new fingerprints are
|
||||
also written to files' metadata.
|
||||
return None. If the items are associated with a library, they are
|
||||
saved to the database. If `write` is set, then the new fingerprints
|
||||
are also written to files' metadata.
|
||||
"""
|
||||
# Get a fingerprint and length for this track.
|
||||
if not item.length:
|
||||
|
|
@ -271,8 +271,8 @@ def fingerprint_item(item, lib=None, write=False):
|
|||
util.displayable_path(item.path)
|
||||
))
|
||||
item.write()
|
||||
if lib:
|
||||
lib.store(item)
|
||||
if item._lib:
|
||||
item.store()
|
||||
return item.acoustid_fingerprint
|
||||
except acoustid.FingerprintGenerationError as exc:
|
||||
log.info(
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ def convert_item(lib, dest_dir, keep_new, path_formats):
|
|||
# writing) to get new bitrate, duration, etc.
|
||||
if keep_new:
|
||||
item.read()
|
||||
lib.store(item) # Store new path and audio data.
|
||||
item.store() # Store new path and audio data.
|
||||
|
||||
if config['convert']['embed']:
|
||||
album = lib.get_album(item)
|
||||
|
|
@ -142,7 +142,7 @@ def convert_on_import(lib, item):
|
|||
item.path = dest
|
||||
item.write()
|
||||
item.read() # Load new audio information data.
|
||||
lib.store(item)
|
||||
item.store()
|
||||
|
||||
|
||||
def convert_func(lib, opts, args):
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ def fetch_item_tempo(lib, loglevel, item, write):
|
|||
item.bpm = tempo
|
||||
if write:
|
||||
item.write()
|
||||
lib.store(item)
|
||||
item.store()
|
||||
|
||||
def get_tempo(artist, title):
|
||||
"""Get the tempo for a song."""
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
# track on the album.
|
||||
if 'track' in self.sources:
|
||||
item.genre, src = self._get_genre(item)
|
||||
lib.store(item)
|
||||
item.store()
|
||||
log.info(u'genre for track {0} - {1} ({2}): {3}'.format(
|
||||
item.artist, item.title, src, item.genre
|
||||
))
|
||||
|
|
@ -353,17 +353,18 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
album.genre, src = self._get_genre(album)
|
||||
log.debug(u'added last.fm album genre ({0}): {1}'.format(
|
||||
src, album.genre))
|
||||
album.store()
|
||||
|
||||
if 'track' in self.sources:
|
||||
for item in album.items():
|
||||
item.genre, src = self._get_genre(item)
|
||||
log.debug(u'added last.fm item genre ({0}): {1}'.format(
|
||||
src, item.genre))
|
||||
session.lib.store(item)
|
||||
item.store()
|
||||
|
||||
else:
|
||||
item = task.item
|
||||
item.genre, src = self._get_genre(item)
|
||||
log.debug(u'added last.fm item genre ({0}): {1}'.format(
|
||||
src, item.genre))
|
||||
session.lib.store(item)
|
||||
item.store()
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ class LyricsPlugin(BeetsPlugin):
|
|||
|
||||
if write:
|
||||
item.write()
|
||||
lib.store(item)
|
||||
item.store()
|
||||
|
||||
def get_lyrics(self, artist, title):
|
||||
"""Fetch lyrics, trying each source in turn. Return a string or
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ def _print_and_apply_changes(lib, item, move, pretend, write):
|
|||
log.error(u'could not sync {0}: {1}'.format(
|
||||
util.displayable_path(item.path), exc))
|
||||
return False
|
||||
lib.store(item)
|
||||
item.store()
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
for item, info in zip(items, rgain_infos):
|
||||
item.rg_track_gain = info['gain']
|
||||
item.rg_track_peak = info['peak']
|
||||
lib.store(item)
|
||||
item.store()
|
||||
|
||||
log.debug(u'replaygain: applied track gain {0}, peak {1}'.format(
|
||||
item.rg_track_gain,
|
||||
|
|
@ -241,3 +241,4 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
album.rg_album_gain,
|
||||
album.rg_album_peak
|
||||
))
|
||||
album.store()
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ def remove_lib():
|
|||
if os.path.exists(TEMP_LIB):
|
||||
os.unlink(TEMP_LIB)
|
||||
def boracay(l):
|
||||
return beets.library.Item(
|
||||
return beets.library.Item(l,
|
||||
**l._connection().execute('select * from items where id=3').fetchone()
|
||||
)
|
||||
np = util.normpath
|
||||
|
|
@ -56,12 +56,12 @@ class LoadTest(unittest.TestCase):
|
|||
def test_load_restores_data_from_db(self):
|
||||
original_title = self.i.title
|
||||
self.i.title = 'something'
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertEqual(original_title, self.i.title)
|
||||
|
||||
def test_load_clears_dirty_flags(self):
|
||||
self.i.artist = 'something'
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('artist' not in self.i._dirty)
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
|
|
@ -74,7 +74,7 @@ class StoreTest(unittest.TestCase):
|
|||
|
||||
def test_store_changes_database_value(self):
|
||||
self.i.year = 1987
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
new_year = self.lib._connection().execute(
|
||||
'select year from items where '
|
||||
'title="Boracay"').fetchone()['year']
|
||||
|
|
@ -83,7 +83,7 @@ class StoreTest(unittest.TestCase):
|
|||
def test_store_only_writes_dirty_fields(self):
|
||||
original_genre = self.i.genre
|
||||
self.i._values_fixed['genre'] = 'beatboxing' # change w/o dirtying
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
new_genre = self.lib._connection().execute(
|
||||
'select genre from items where '
|
||||
'title="Boracay"').fetchone()['genre']
|
||||
|
|
@ -91,7 +91,7 @@ class StoreTest(unittest.TestCase):
|
|||
|
||||
def test_store_clears_dirty_flags(self):
|
||||
self.i.composer = 'tvp'
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
self.assertTrue('composer' not in self.i._dirty)
|
||||
|
||||
class AddTest(unittest.TestCase):
|
||||
|
|
@ -884,7 +884,7 @@ class PathStringTest(_common.TestCase):
|
|||
def test_special_chars_preserved_in_database(self):
|
||||
path = 'b\xe1r'
|
||||
self.i.path = path
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
i = list(self.lib.items())[0]
|
||||
self.assertEqual(i.path, path)
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class AlbumFileTest(_common.TestCase):
|
|||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move()
|
||||
self.ai.store()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
|
||||
self.assert_('newAlbumName' in self.i.path)
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ class AlbumFileTest(_common.TestCase):
|
|||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move()
|
||||
self.ai.store()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
|
||||
self.assertFalse(os.path.exists(oldpath))
|
||||
self.assertTrue(os.path.exists(self.i.path))
|
||||
|
|
@ -190,14 +190,14 @@ class AlbumFileTest(_common.TestCase):
|
|||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move(True)
|
||||
self.ai.store()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
|
||||
self.assertTrue(os.path.exists(oldpath))
|
||||
self.assertTrue(os.path.exists(self.i.path))
|
||||
|
||||
def test_albuminfo_move_to_custom_dir(self):
|
||||
self.ai.move(basedir=self.otherdir)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.ai.store()
|
||||
self.assertTrue('testotherdir' in self.i.path)
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ class ArtFileTest(_common.TestCase):
|
|||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbum'
|
||||
self.ai.move()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
|
||||
self.assertNotEqual(self.i.path, oldpath)
|
||||
self.assertFalse(os.path.exists(self.art))
|
||||
|
|
@ -245,7 +245,7 @@ class ArtFileTest(_common.TestCase):
|
|||
# Move the album to another directory.
|
||||
self.ai.move(basedir=self.otherdir)
|
||||
self.ai.store()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
|
||||
# Art should be in new directory.
|
||||
self.assertNotExists(self.art)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class IHatePluginTest(unittest.TestCase):
|
|||
task = ImportTask()
|
||||
task.cur_artist = u'Test Artist'
|
||||
task.cur_album = u'Test Album'
|
||||
task.items = [Item({'genre': 'Test Genre'})]
|
||||
task.items = [Item(genre='Test Genre')]
|
||||
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
|
||||
album_p, white_p))
|
||||
genre_p = 'some_genre test\sgenre'.split()
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ class ImportApplyTest(_common.TestCase):
|
|||
shutil.copy(os.path.join(_common.RSRC, 'full.mp3'), self.srcpath)
|
||||
self.i = library.Item.from_path(self.srcpath)
|
||||
self.i.comp = False
|
||||
self.lib.add(self.i)
|
||||
|
||||
trackinfo = TrackInfo('one', 'trackid', 'some artist',
|
||||
'artistid', 1)
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin):
|
|||
|
||||
def test_unicode_query(self):
|
||||
self.single_item.title = u'caf\xe9'
|
||||
self.lib.store(self.single_item)
|
||||
self.single_item.store()
|
||||
|
||||
q = u'title:caf\xe9'
|
||||
results = self.lib.items(q)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class ListTest(_common.TestCase):
|
|||
|
||||
def test_list_unicode_query(self):
|
||||
self.item.title = u'na\xefve'
|
||||
self.lib.store(self.item)
|
||||
self.item.store()
|
||||
self.lib._connection().commit()
|
||||
|
||||
self._run_list([u'na\xefve'])
|
||||
|
|
@ -241,42 +241,42 @@ class MoveTest(_common.TestCase):
|
|||
|
||||
def test_move_item(self):
|
||||
self._move()
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testlibdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertNotExists(self.itempath)
|
||||
|
||||
def test_copy_item(self):
|
||||
self._move(copy=True)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testlibdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertExists(self.itempath)
|
||||
|
||||
def test_move_album(self):
|
||||
self._move(album=True)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testlibdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertNotExists(self.itempath)
|
||||
|
||||
def test_copy_album(self):
|
||||
self._move(copy=True, album=True)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testlibdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertExists(self.itempath)
|
||||
|
||||
def test_move_item_custom_dir(self):
|
||||
self._move(dest=self.otherdir)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testotherdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertNotExists(self.itempath)
|
||||
|
||||
def test_move_album_custom_dir(self):
|
||||
self._move(dest=self.otherdir, album=True)
|
||||
self.lib.load(self.i)
|
||||
self.i.load()
|
||||
self.assertTrue('testotherdir' in self.i.path)
|
||||
self.assertExists(self.i.path)
|
||||
self.assertNotExists(self.itempath)
|
||||
|
|
@ -306,7 +306,7 @@ class UpdateTest(_common.TestCase):
|
|||
self.io.addinput('y')
|
||||
if reset_mtime:
|
||||
self.i.mtime = 0
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
commands.update_items(self.lib, query, album, move, False)
|
||||
|
||||
def test_delete_removes_item(self):
|
||||
|
|
@ -376,7 +376,7 @@ class UpdateTest(_common.TestCase):
|
|||
|
||||
# Make in-memory mtime match on-disk mtime.
|
||||
self.i.mtime = os.path.getmtime(self.i.path)
|
||||
self.lib.store(self.i)
|
||||
self.i.store()
|
||||
|
||||
self._update(reset_mtime=False)
|
||||
item = self.lib.items().get()
|
||||
|
|
|
|||
Loading…
Reference in a new issue